[转载]ListView中使用自定义Adapter及时更新数据
摘要 在项目中,遇到不能ListView及时更新的问题。写了一个demo,其中也遇到一些问题,一并写出来。 好吧,上代码 : public class PersonAdapter extends BaseAdapter { private ArrayListPersonBean mList; private Context mContext; public PersonAdapter(Arr
在项目中,遇到不能ListView及时更新的问题。写了一个demo,其中也遇到一些问题,一并写出来。好吧,上代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | 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() {        returnmList.size();    }      @Override    public Object getItem(int position) {        returnmList.get(position);    }      @Override    public long getItemId(int position) {        returnposition;    }      @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 = newHolder();            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());        returnconvertView;    }      class Holder {        private TextView mNameText, mIDText;    }} | 
PersonAdapter继承自BaseAdapter,里面的代码都应该比较熟悉。里面注意这点代码:
| 1 2 3 4 | public void refresh(ArrayList<PersonBean> list) {        mList = list;        notifyDataSetChanged();    } | 
在初始化PersonAdapter的时候,需要外部导入一个mList。
| 1 2 3 4 | public PersonAdapter(ArrayList<PersonBean> list, Context context) {        mList = list;        mContext = context;    } | 
在使用这种类型时,在Activity使用mAdapter.notifyDataSetChanged()时候,有时候会发现数据不能够及时的更新。这个时候,就比较需要调用refresh()这个方法了。
下面看一下主Activity:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | 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 = newArrayList<PersonBean>();        mAdapter = newPersonAdapter(mList, ListViewRefreshActivity.this);        mListView.setAdapter(mAdapter);          mHandler = newHandler() {              @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();        newThread(newRunnable() {              @Override            public void run() {                try{                    for(int i = 0; i < 10; i++) {                        mName = "hao :"+ i;                        mID = ""+ i;                        PersonBean bean = newPersonBean();                        bean.setID(mID);                        bean.setName(mName);                        Message message = newMessage();                        message.obj = bean;                        Thread.sleep(3000);                        mHandler.sendMessage(message);//                      mHandler.sendMessageDelayed(message, 10000);                    }}catch(Exception e) {                        e.printStackTrace();                    }            }        }).start();    }} | 
先说一个小bug吧,看一下在new Thread上面有一句注释掉的
| 1 | final Message message = newMessage(); | 
如果用这个message,注释run方法体内的message,运行程序,在我机子上,发送第四个消息时,就会报android.util.AndroidRuntimeException:This message is already in use这个错,message已经被使用。所以,每一次发送,都要重新创建一个新的message。也可以使用一下语句:
| 1 | message = mHandler.obtainMessage(); | 
里面主要看一下handler中重写handlerMessage这个方法:
| 1 2 3 4 5 6 7 8 | @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这个方法的设计是典型观察者模式。看一下源代码:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | public abstract class BaseAdapter implements ListAdapter, SpinnerAdapter {    private final DataSetObservable mDataSetObservable = newDataSetObservable();      public boolean hasStableIds() {        returnfalse;    }          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方法:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | /**     * 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的定义如下:
| 1 | protected final ArrayList<T> mObservers = newArrayList<T>(); | 
通过遍历一个ArrayList来通知各个观察者。
前面说到了,我们可以调用registerDataSetObserver注册为观察者,但是是在哪注册的呢?因为如果没有注册,adapter就不应该发生变化。所以,我们看下ListView的SetAdapter这个方法:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | @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 = newHeaderViewListAdapter(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 = newAdapterDataSetObserver();            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的注册。
| 1 2 3 | if(mAdapter != null&& mDataSetObserver != null) {            mAdapter.unregisterDataSetObserver(mDataSetObserver);        } | 
然后,把传入的adapter这个参数,赋值给mAdapter:
| 1 2 3 4 5 | if(mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) {           mAdapter = newHeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);       } else{           mAdapter = adapter;       } | 
赋值成功后:
| 1 2 3 | if(mAdapter != null) {            mDataSetObserver = newAdapterDataSetObserver();            mAdapter.registerDataSetObserver(mDataSetObserver); | 
重新为mDataSetObserver赋值,然后把mAdapter注册为mDataSetObserver的观察者。
至此,思路应该清晰了:在listview的setAdapter中把adapter注册为mDataSetObserver的观察者。当数据变化时,就可以调用notifyDataSetChanged方法来提示观察者数据已经变化。
最后就是说一下,里面PersonBean类,就是一个实体类,很简单,不在详述。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号