[转载]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类,就是一个实体类,很简单,不在详述。

路漫漫其修远兮 吾将上下而求索
原文地址:https://www.cnblogs.com/hudabing/p/3075156.html