HardBirch

ListView中使用自定义Adapter及时更新数据

时间:12-10-30 栏目:安卓源码解析与小应用 作者:张飞不张,文采横飞 评论:0 点击: 2,104 次

        又到10点半,时间真是过的真快。在项目中,遇到不能ListView及时更新的问题。写了一个demo,其中也遇到一些问题,一并写出来。前几个月总是有点懒,但是这个月总算是凑够4篇了。

代码比较简单,遇到点简单的问题,弄到了现在。

好吧,上代码

public class PersonAdapter extends BaseAdapter {
	private ArrayList<PersonBean> mList;
	private Context mContext;

	public PersonAdapter(ArrayList<PersonBean> list, Context context) {
		mList = list;
		mContext = context;
	}

	public void refresh(ArrayList<PersonBean> list) {
		mList = list;
		notifyDataSetChanged();
	}

	@Override
	public int getCount() {
		return mList.size();
	}

	@Override
	public Object getItem(int position) {
		return mList.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Holder holder = null;
		if (convertView == null) {
			LayoutInflater inflater = LayoutInflater.from(mContext);
			convertView = inflater.inflate(R.layout.list_item, null);
			holder = new Holder();
			holder.mNameText = (TextView)convertView.findViewById(R.id.name_text);
			holder.mIDText = (TextView)convertView.findViewById(R.id.id_text);
			convertView.setTag(holder);
		} else {
			holder = (Holder) convertView.getTag();
		}
		holder.mNameText.setText(mList.get(getCount() - position - 1).getName());
		holder.mIDText.setText(mList.get(getCount() - position - 1).getID());
		return convertView;
	}

	class Holder {
		private TextView mNameText, mIDText;
	}
}

PersonAdapter继承自BaseAdapter,里面的代码都应该比较熟悉。里面注意这点代码:

public void refresh(ArrayList<PersonBean> list) {
		mList = list;
		notifyDataSetChanged();
	}

在初始化PersonAdapter的时候,需要外部导入一个mList。

public PersonAdapter(ArrayList<PersonBean> list, Context context) {
		mList = list;
		mContext = context;
	}

在使用这种类型时,在Activity使用mAdapter.notifyDataSetChanged()时候,有时候会发现数据不能够及时的更新。这个时候,就比较需要调用refresh()这个方法了。

下面看一下主Activity:

public class ListViewRefreshActivity extends Activity {

	private ListView mListView;
	private ArrayList<PersonBean> mList;
	private PersonAdapter mAdapter;
	private Handler mHandler;
	private String mName, mID;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		mListView = (ListView)findViewById(R.id.listView);
		mList = new ArrayList<PersonBean>();
		mAdapter = new PersonAdapter(mList, ListViewRefreshActivity.this);
		mListView.setAdapter(mAdapter);

		mHandler = new Handler() {

			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				mList.add((PersonBean) msg.obj);
				Log.v("@@@@@@", "this is get message");
				mAdapter.refresh(mList);
//				mAdapter.notifyDataSetChanged();
			}
		};

//		final Message message = new Message();
		new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					for (int i = 0; i < 10; i++) {
						mName = "hao :" + i;
						mID = "" + i;
						PersonBean bean = new PersonBean();
						bean.setID(mID);
						bean.setName(mName);
						Message message = new Message();
						message.obj = bean;
						Thread.sleep(3000);
						mHandler.sendMessage(message);
//						mHandler.sendMessageDelayed(message, 10000);
					}}catch (Exception e) {
						e.printStackTrace();
					}
			}
		}).start();
	}
}

先说一个小bug吧,看一下在new Thread上面有一句注释掉的

final Message message = new Message();

如果用这个message,注释run方法体内的message,运行程序,在我机子上,发送第四个消息时,就会报android.util.AndroidRuntimeException:This message is already in use这个错,message已经被使用。所以,每一次发送,都要重新创建一个新的message。也可以使用一下语句:

message = mHandler.obtainMessage();

里面主要看一下handler中重写handlerMessage这个方法:

@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				mList.add((PersonBean) msg.obj);
				Log.v("@@@@@@", "this is get message");
				mAdapter.refresh(mList);
//				mAdapter.notifyDataSetChanged();
			}

当然,在这个小例子中,使用mAdapter.refresh这个方法更麻烦点,直接调用notifyDataSetChange就可以达到效果,如果你的代码里面不能达到效果,就可以使用mAdapter.refresh试一下。

notifyDataSetChanged这个方法的设计是典型观察者模式。看一下源代码:

public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {
    private final DataSetObservable mDataSetObservable = new DataSetObservable();

    public boolean hasStableIds() {
        return false;
    }

    public void registerDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.registerObserver(observer);
    }

    public void unregisterDataSetObserver(DataSetObserver observer) {
        mDataSetObservable.unregisterObserver(observer);
    }

    /**
     * Notifies the attached observers that the underlying data has been changed
     * and any View reflecting the data set should refresh itself.
     */
    public void notifyDataSetChanged() {
        mDataSetObservable.notifyChanged();
    }

    /**
     * Notifies the attached observers that the underlying data is no longer valid
     * or available. Once invoked this adapter is no longer valid and should
     * not report further data set changes.
     */
    public void notifyDataSetInvalidated() {
        mDataSetObservable.notifyInvalidated();
    }

有一个数据被观察者:mDataSetObservable。当被观察者数据发生改变时,通知观察者。我们使用registerDataSetObserver这个方法注册观察者。都是调用notifyDataSetChanged方法。就是告诉观察者,数据有所改变。在这个方法中,又调用了DataSetObserveable的notifyChanged方法:

/**
     * Invokes onChanged on each observer. Called when the data set being observed has
     * changed, and which when read contains the new state of the data.
     */
    public void notifyChanged() {
        synchronized(mObservers) {
            // since onChanged() is implemented by the app, it could do anything, including
            // removing itself from {@link mObservers} - and that could cause problems if
            // an iterator is used on the ArrayList {@link mObservers}.
            // to avoid such problems, just march thru the list in the reverse order.
            for (int i = mObservers.size() - 1; i >= 0; i--) {
                mObservers.get(i).onChanged();
            }
        }
    }

看一下他的方法说明:当数据被观察到已经改变,调用每一个观察者的onChanged方法去读取数据的最新状态。

mObservers的定义如下:

protected final ArrayList<T> mObservers = new ArrayList<T>();

通过遍历一个ArrayList来通知各个观察者。

前面说到了,我们可以调用registerDataSetObserver注册为观察者,但是是在哪注册的呢?因为如果没有注册,adapter就不应该发生变化。所以,我们看下ListView的SetAdapter这个方法:

@Override
    public void setAdapter(ListAdapter adapter) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

        resetList();
        mRecycler.clear();

        if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

        mOldSelectedPosition = INVALID_POSITION;
        mOldSelectedRowId = INVALID_ROW_ID;

        // AbsListView#setAdapter will update choice mode states.
        super.setAdapter(adapter);

        if (mAdapter != null) {
            mAreAllItemsSelectable = mAdapter.areAllItemsEnabled();
            mOldItemCount = mItemCount;
            mItemCount = mAdapter.getCount();
            checkFocus();

            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

            mRecycler.setViewTypeCount(mAdapter.getViewTypeCount());

            int position;
            if (mStackFromBottom) {
                position = lookForSelectablePosition(mItemCount - 1, false);
            } else {
                position = lookForSelectablePosition(0, true);
            }
            setSelectedPositionInt(position);
            setNextSelectedPositionInt(position);

            if (mItemCount == 0) {
                // Nothing selected
                checkSelectionChanged();
            }
        } else {
            mAreAllItemsSelectable = true;
            checkFocus();
            // Nothing selected
            checkSelectionChanged();
        }

        requestLayout();
    }

如果mAdapter和mDataSetObserver都不为空的话,取消mAdapter对mDataSetObserver的注册。

if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.unregisterDataSetObserver(mDataSetObserver);
        }

然后,把传入的adapter这个参数,赋值给mAdapter:

 if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {
            mAdapter = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
        } else {
            mAdapter = adapter;
        }

赋值成功后:

if (mAdapter != null) {
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.registerDataSetObserver(mDataSetObserver);

重新为mDataSetObserver赋值,然后把mAdapter注册为mDataSetObserver的观察者。

至此,思路应该清晰了:在listview的setAdapter中把adapter注册为mDataSetObserver的观察者。当数据变化时,就可以调用notifyDataSetChanged方法来提示观察者数据已经变化。

关于观察者的详细情况:浅学设计模式之观察者<Observer>模式及在android中的应用

最后就是说一下,里面PersonBean类,就是一个实体类,很简单,不在详述。

最后,源代码:http://download.csdn.net/detail/aomandeshangxiao/4704585


声明: 本文由( 张飞不张,文采横飞 )原创编译,转载请保留链接: ListView中使用自定义Adapter及时更新数据

ListView中使用自定义Adapter及时更新数据:等您坐沙发呢!

发表评论


QQ群互动

Linux系统与内核学习群:194051772

WP建站技术学习交流群:194062106

魔豆之路QR

魔豆的Linux内核之路

魔豆的Linux内核之路

优秀工程师当看优秀书籍

优秀程序员,要看优秀书!

赞助商广告

友荐云推荐