ListView中使用自定義Adapter及時更新資料
又到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(2)——自定義AdapterViewAPT
- 自定義viewgroup(6)--使用adapter適配資料ViewAPT
- ListView動態更新資料View
- ListView(1)——各種Adapter的使用ViewAPT
- Adapter的getViewTypeCount和getItemViewType及-ListView/GridViewAPTView
- 多佈局的自定義AdapterAPT
- 使用 JWT 時,新增自定義資料並在登陸時校驗JWT
- vue 自定義指令實現資料拉取更新Vue
- ListView 通用 Adapter 封裝ViewAPT封裝
- Laravel 中自定義使用者登入的資料表Laravel
- listView中多個listItem佈局時,convertView快取及使用View快取
- python中什麼時候使用自定義類Python
- SQL Server 中自定義資料型別SQLServer資料型別
- Istio Mixer Adapter開發 (三)自定義Mixer Grpc Adapter部署APTRPC
- 自定義viewgroup(7)--最終版,adapter適配資料且重新整理ViewAPT
- 何時使用自定義HTTP 方法HTTP
- Mysql資料庫自定義函式的定義、使用方法及操作注意事項MySql資料庫函式
- Android listview與adapter用法AndroidViewAPT
- 在資料庫中自定義外部函式資料庫函式
- Android中利用ViewHolder優化自定義Adapter的典型寫法AndroidView優化APT
- Unity ECS System在什麼時候更新?如何自定義這個更新的時機?Unity
- vue3 - 使用reactive定義響應式資料進行資料修改賦值時,資料更新但檢視不更新VueReact賦值
- angular中的表單資料自定義驗證Angular
- 自定義資料型別資料型別
- 自定義版本更新彈窗
- Java 自定義註解及使用場景Java
- JavaFx Tooltip懸浮提示使用及自定義Java
- android 滑動刪除的listview(自定義view)AndroidView
- 使用jQuery在javascript中自定義事件jQueryJavaScript事件
- BW中自定義資料來源的Delta機制
- 在VC++中建立自定義資料庫類 (轉)C++資料庫
- Android開發 - 使用自定義介面在新視窗中傳回資料Android
- 資料庫中AS的使用意義資料庫
- DM自定義資料型別資料型別
- PyTorch 自定義資料集PyTorch
- Angular過濾器 自定義及使用方法Angular過濾器
- 如何實現 axios 的自定義介面卡 adapteriOSAPT
- Flutter 中 ListView 的使用FlutterView