自己實現notifyDatasetChanged,notifydatasetchanged

來源:互聯網
上載者:User

自己實現notifyDatasetChanged,notifydatasetchanged

今天這篇部落格,我們來實現一下adapter那個最常用的notifyDatasetChanged功能,我們利用一個繼承一個LinearLayout來實現一個可能在日常工作中很常用的功能。
大家在工作中可能經常遇到這樣的功能:

需要定義一個列表來展示菜單,但是這個菜單並不一定適合ListView,然後,我們可能就通過一個LinearLayout來實現。

如何讓我們的LinearLayout使用起來更像ListView呢? 那就是設定Adapter。那你的代碼可能是這樣的。
擴充LinearLayout:

public class MyLinearLayout extends LinearLayout {    private Adapter mAdapter;    public MyLinearLayout(Context context, AttributeSet attrs) {        super(context, attrs);        setOrientation(VERTICAL);    }    public void setAdapter(Adapter adapter) {        removeAllViews();        mAdapter = adapter;        int itemCount = mAdapter.getItemCount();        View child;        for (int i = 0; i < itemCount; i++) {            child = mAdapter.getView(this, i);            addView(child);        }    }}

自己動手實現一個簡單的Adapter:

public abstract class Adapter {    public abstract int getItemCount();    public abstract View getView(View parent, int position);}

在我們的代碼如何使用呢?
首先繼承咱們定義的Adapter

public class MyAdapter extends Adapter {    private ArrayList<String> mData;    public MyAdapter(ArrayList<String> data) {        mData = data;    }    @Override    public int getItemCount() {        return mData.size();    }    @Override    public View getView(View parent, int position) {        TextView textView = new TextView(parent.getContext());        textView.setText(mData.get(position));        return textView;    }}

給我們的LinearLayout設定Adapter。

mLinearLayout = (MyLinearLayout) findViewById(R.id.linear);mAdapter = new MyAdapter(mData);mLinearLayout.setAdapter(mAdapter);

這些都是最基礎的代碼,可能很多朋友在工作中也會有這麼辦的時候,所以,我們只是展示一下代碼,至於效果,肯定就是一個普通的豎向的LinearLayout。這裡也不再多提。
現在,我們來假設這樣的一個情形,假如我們的資料是變化的,上面的代碼該如何去做呢?
1. 手動調用LinearLayout.getChildAt(index)去設定資料。
2. 重新設定adapter,可以看到在MyLinearLayout.setAdapter開始,我們調用了removeAllViews()。

對於第一種方法,我們感覺有悖於我們的封裝,每次資料變化都要手動去更新item;而第二種方式肯定會影響效能,畢竟每次都重新添加view,感覺這肯定是沒必要的。
那麼我們該怎麼辦呢? 看看android的adapter是如何?的,我們只需要調用notifyDatasetChanged就ok。
在開始實現notifyDatasetChanged之前,我們來引入一個概念,那就是觀察者模式,java本身提供了觀察者模式的實現,但是使用起來有點小麻煩,所以android SDK給我們提供了另一種方式:DataSetObserver。DataSetObserver其實就是一個簡單的抽象類別,在原理上也很簡單,可以這麼說,我們完全可以自己定義一個DataSetObserver。但是我們還是選擇使用原生的DataSetObserver,畢竟android已經給我們提供好了。
這裡需要說明一下,代碼不具備任何價值,只有參考意義,而且,我們代碼有很大的局限性,因為我們只考慮了TextView。
先來看看我們的Adapter吧,在上面的基礎上加了幾行代碼:

public abstract class Adapter {    private DataSetObserver mDataSetObserver;    public void registerObserver(DataSetObserver observer) {        mDataSetObserver = observer;    }    public void unregisterObserver() {        mDataSetObserver = null;    }    public void notifyDataSetChanged() {        if(mDataSetObserver != null) mDataSetObserver.onChanged();    }    public void notifyDataSetInvalidate() {        if(mDataSetObserver != null) mDataSetObserver.onInvalidated();    }    public abstract String getItem(int position);    public abstract int getItemCount();    public abstract View getView(View parent, int position);}

增加了一個DataSetObserver變數,對應的,定義了兩個方法來向這個DataSetObserver賦值,而且,我們新增了一個abstract方法
public abstract String getItem(int position);
最主要的,我們來看看兩個最重要的方法:

public void notifyDataSetChanged() {        if(mDataSetObserver != null) mDataSetObserver.onChanged();    }    public void notifyDataSetInvalidate() {        if(mDataSetObserver != null) mDataSetObserver.onInvalidated();    }

notifyDataSetChanged方法,我們直接調用了DataSetObserver.onChanged(),invalidate也是一樣,這裡我們就需要關心一下這個DataSetObserver是從哪註冊進來的。
下面公布經過修改過的MyLinearLayout:

public class MyLinearLayout extends LinearLayout {    private Adapter mAdapter;    public MyLinearLayout(Context context, AttributeSet attrs) {        super(context, attrs);        setOrientation(VERTICAL);    }    private DataSetObserver mObserver = new DataSetObserver() {        public void onChanged() {            onChange();        }        public void onInvalidated() {            removeAllViews();        }    };    private void onChange() {        int childCount = getChildCount();        int itemCount = mAdapter.getItemCount();        // 如果資料變少了,則移出多餘的view        if(childCount > itemCount) removeViews(itemCount, childCount - itemCount);        // 現在view的個數 <= 資料的個數        TextView child;        for (int i = 0; i < itemCount; i++) {            if(i < childCount) {                child = (TextView) getChildAt(i);                child.setText(mAdapter.getItem(i));            }else {                child = (TextView) mAdapter.getView(this, i);                addView(child);            }        }    }    public void setAdapter(Adapter adapter) {        removeAllViews();        mAdapter = adapter;        mAdapter.registerObserver(mObserver);        int itemCount = mAdapter.getItemCount();        View child;        for (int i = 0; i < itemCount; i++) {            child = mAdapter.getView(this, i);            addView(child);        }    }}

在MyLinearLayout中定義了一個DataSetObserver,並且在setAdapter中向Adapter註冊這個DataSetObserver。最主要的代碼是在DataSetObserver中那個onChanged方法中:

private void onChange() {        int childCount = getChildCount();        int itemCount = mAdapter.getItemCount();        // 如果資料變少了,則移出多餘的view        if(childCount > itemCount) removeViews(itemCount, childCount - itemCount);        // 現在view的個數 <= 資料的個數        TextView child;        for (int i = 0; i < itemCount; i++) {            if(i < childCount) {                child = (TextView) getChildAt(i);                child.setText(mAdapter.getItem(i));            }else {                child = (TextView) mAdapter.getView(this, i);                addView(child);            }        }    }

第5行代碼,如果發現新的資料集比以前少了,那麼我們去移除多餘的view,
接下來9行,還是去遍曆所有的資料集,接著一個if…else…是去判斷如果存在view,則只改變資料,如果資料量>子view個數,則添加新的view。
這樣,就做到了在最大程度上減少view的添加和移除操作。
當然,這段代碼也很有局限性,只能添加TextView,因為我們在這裡面強轉了一下。這段代碼只是去示範如何去實現一個資料觀察者。ok,最後再來看看我們的測試代碼和最後效果:

public class MainActivity extends Activity {    private ArrayList<String> mData = new ArrayList<String>() {        {            for (int i = 0; i < 10; i++) {                add("android " + i);            }        }    };    private MyLinearLayout mLinearLayout;    private MyAdapter mAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mLinearLayout = (MyLinearLayout) findViewById(R.id.linear);        mAdapter = new MyAdapter(mData);        mLinearLayout.setAdapter(mAdapter);        mLinearLayout.postDelayed(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 10; i++) {                    mData.add("loader " + i);                }                mAdapter.notifyDataSetChanged();            }        }, 2000);        mLinearLayout.postDelayed(new Runnable() {            @Override            public void run() {                for (int i = 0; i < 5; i++) {                    mData.remove(0);                }                mAdapter.notifyDataSetChanged();            }        }, 4000);        mLinearLayout.postDelayed(new Runnable() {            @Override            public void run() {                mAdapter.notifyDataSetInvalidate();            }        }, 6000);    }}

效果如下:

最後是代碼下載:代碼下載

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.