ListView是在Android開發中用得很是多的控件之一,而且這些列表還常常須要咱們去對listView的數據進行刷新操做,在這種狀況下,咱們每每都會去調用adapter的notifyDataSetChanged()方法對listView的界面從新進行繪製。衆所周知,notifyDataSetChanged()這個方法是Adapter的觀察者模式的體現,它的實現原理就是對咱們的數據源進行監聽,一旦咱們的數據源發生了變化,就會去調用getView()方法對整個界面上可見的Item進行刷新。可是,這同時也對不少本不須要刷新的Item也進行了刷新,這樣的效率無疑是很低的,當數據量很大的時候還有可能會出現卡頓或者圖片閃爍等問題。這對於用戶體驗上來講,也是很不友好的。java
在下文中,我是以一個小的Demo來介紹怎麼用非notifyDataSetChanged()的方法來對listView的界面進行刷新,並利用Item的點擊來模擬數據源的變化。android
很少說,直接看代碼:(佈局簡單,就不放了)app
1. 用notifyDataSetChanged()方式刷新界面ide
package com.example.zohar.androidtest.listView; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; import com.example.zohar.androidtest.R; import java.util.ArrayList; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; public class UpdateSingleListViewActivity extends AppCompatActivity { @Bind(R.id.list_view) ListView listView; private List<String> dataList = new ArrayList<>(); private ListViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_update_single_list_view); ButterKnife.bind(this); initData(); initView(); } private void initData() { for (int i = 0; i < 20; i++) { dataList.add("第" + i + "個數據"); } } private void initView() { adapter = new ListViewAdapter(this, dataList); listView.setAdapter(adapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d("OnItemClick", "點擊了第" + position + "項"); dataList.set(position, "修改後的數據:position = " + position); adapter.notifyDataSetChanged(); } }); } }
package com.example.zohar.androidtest.listView; import android.content.Context; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import com.example.zohar.androidtest.R; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; public class ListViewAdapter extends BaseAdapter { private List<String> dataList; private LayoutInflater inflater; public ListViewAdapter(Context context, List<String> dataList) { this.dataList = dataList; inflater = LayoutInflater.from(context); } @Override public int getCount() { return dataList.size(); } @Override public Object getItem(int position) { return dataList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { convertView = inflater.inflate(R.layout.item_layout_update_single_list_view, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } Log.d("TAG", "getView ======> position = " + position); holder.tvItem.setText(dataList.get(position)); return convertView; } static class ViewHolder { @Bind(R.id.tv_item) TextView tvItem; ViewHolder(View view) { ButterKnife.bind(this, view); } } }
咱們來看看打印的Log佈局
11-06 00:36:09.419 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 0 11-06 00:36:09.419 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 1 11-06 00:36:09.419 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 2 11-06 00:36:09.419 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 3 11-06 00:36:09.429 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 4 11-06 00:36:09.429 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 5 11-06 00:36:09.429 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 6 11-06 00:36:09.429 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 7 11-06 00:36:09.429 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 8 11-06 00:36:09.439 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 9 11-06 00:36:09.439 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 10 11-06 00:36:09.439 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 11 11-06 00:36:09.439 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 12 11-06 00:36:09.459 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 13 11-06 00:36:09.459 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 14 11-06 00:36:11.789 24736-24736/com.example.zohar.androidtest D/TAG: 點擊了第5項 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 0 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 1 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 2 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 3 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 4 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 5 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 6 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 7 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 8 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 9 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 10 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 11 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 12 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 13 11-06 00:36:11.839 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 14
能夠看到,在我點擊了第5項item時,個人界面上可見的0~14項這15個item全都被刷新了,但實際上,我只須要它對第5項的item進行刷新就能夠的,這至關於我有15分之14的操做都是多餘的。this
2. 經過直接調用getView()方法來刷新對應item的界面。code
既然咱們看到Log中打印的是屢次調用getView()方法來對界面進行刷新,那麼咱們能夠想一想,可否直接經過position這一參數來直接調用對應的getView()方法來達到相同的效果呢?對象
咱們看到adapte的 getView(int position, View convertVie, ViewGroup parent) 中有三個參數,其中position是序號,convertView就是咱們item中的子View,parent是咱們須要刷新界面的控件。事件
因而,咱們將item的點擊事件方法修改成:圖片
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d("TAG", "點擊了第" + position + "項"); dataList.set(position, "修改後的數據:position = " + position); View item = listView.getChildAt(position); adapter.getView(position, item, listView); } });
此時,咱們在來看看Log,會發現,我點擊某一個item的時候,adapter只會去調用對應position的getView()方法來對界面進行刷新了
11-06 01:03:42.269 24736-24736/com.example.zohar.androidtest D/TAG: 點擊了第13項 11-06 01:03:42.269 24736-24736/com.example.zohar.androidtest D/TAG: getView ======> position = 13
可是當咱們看界面的時候,卻發現一個問題,就是當我可見的item不是從第一條數據開始時,我點擊的item的position是13,修改的確實position是18的item。
這是由於當item變成不可見時會回收掉對應的convertView的緣由,所以此時調用position爲13的getView()方法時,更新的是可見的序號爲13的item,也就是整個listView中的序號爲18的item。此時,咱們就須要去計算出對應item真正的position。
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d("TAG", "點擊了第" + position + "項"); dataList.set(position, "修改後的數據:position = " + position); notifyDataSetChanged(position, listView); } });
private void notifyDataSetChanged(int position, ListView listView) { int firstVisiblePosition = listView.getFirstVisiblePosition();//得到可見的第一個item的position int lastVisiblePosition = listView.getLastVisiblePosition();//得到可見的最後一個item的position if (position >= firstVisiblePosition && position <= lastVisiblePosition) { View view = listView.getChildAt(position - firstVisiblePosition); adapter.getView(position, view, listView); } }
至此,咱們便獲得了正確的結果,利用adapter的getView()方法,讓咱們在刷新界面的時候只須要去刷新須要刷新的item。
3. 接下來,假如咱們一個item中的控件較多,而咱們又只須要刷新其中的某一個控件,要怎麼辦呢?
經過Debug,咱們能夠看到在咱們經過listView.getChildAt(position)獲得的view的tag屬性是有值的,並且這個值其實就是adapter的getView()方法返回的子View的數據對象ViewHolder。
那麼,咱們的 notifyDataSetChanged(int position, ListView listView) 方法就能夠改成以下形勢,直接經過view.getTag() 方法獲得ViewHolder對象,而後就能夠修改咱們所但願的控件了。
private void notifyDataSetChanged(int position, ListView listView) { int firstVisiblePosition = listView.getFirstVisiblePosition(); int lastVisiblePosition = listView.getLastVisiblePosition(); if (position >= firstVisiblePosition && position <= lastVisiblePosition) { ListViewAdapter.ViewHolder holder = (ListViewAdapter.ViewHolder) listView.getChildAt(position - firstVisiblePosition).getTag(); holder.tvItem.setText(dataList.get(position)); } }
PS:若有發現本文內容錯誤或不足之處的,歡迎指正。