zoukankan      html  css  js  c++  java
  • [转载]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() {
            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,里面的代码都应该比较熟悉。里面注意这点代码:

    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 = 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上面有一句注释掉的

    1
    final Message message = new Message();

     如果用这个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 = 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方法:

    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 = new ArrayList<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 = 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的注册。

    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 = new HeaderViewListAdapter(mHeaderViewInfos, mFooterViewInfos, adapter);
           } else {
               mAdapter = adapter;
           }

    赋值成功后:

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

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

     

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

     

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

    路漫漫其修远兮 吾将上下而求索
  • 相关阅读:
    CSP2020 游记
    关于对拍
    联考day7
    CSP 模拟26
    动态规划
    JAVA单元测试的用法和要点(入门篇)
    乐观锁和悲观锁的区别
    并发基础篇(六):线程Thread类的start()方法和run()方法【转载】
    this和super差异
    Java中的static关键字解析
  • 原文地址:https://www.cnblogs.com/hudabing/p/3075156.html
Copyright © 2011-2022 走看看