• Override ListView getAdapter造成的后果


    近期工作中,发现了一个bug,是和ListView Adapter有关的。产生了FC,描写叙述信息大约是

    "The content of the adapter has changed but ListView did not receive a notification. Make sure the content of  your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(xxx) with Adapter(HeaderViewListAdapter)]"

    它的大意是,Adapter内的数据发生了变化,可是UI却没有更新,您是否忘记调用了notifyDataSetChanged?


    这实际上是一个很有误导的信息。普通情况下,我们不会忘记调用该函数的。可是假设我们不小心,从listview继承一个新的类,并override它的getAdapter方法,就可能会出问题了。


    ListView是支持HeaderView和footerView的,即在listview的最初和最末尾的位置加入�一些特殊的view。它的实现方法,就是通过一个HeaderViewListAdapter。


    HeaderViewListAdapter会包装一个Adapter,这个是由用户自己设置的。ListView中相应的代码是

        @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;
            }
    

    ListView的getAdapter返回的是mAdapter,就可以能是一个HeaderViewListAdapter.


    假设override getAdapter,并返回HeaderViewListAdapter内部包装的Adapter,就会出问题。也就是上面提到的FC.


    这样的问题是怎么出现呢?

    首先,这个异常抛出的位置,是在函数layoutChildren中,抛出的条件是mItemCount != mAdapter.getCount(),代码例如以下:

    else if (mItemCount != mAdapter.getCount()) {
                    throw new IllegalStateException("The content of the adapter has changed but "
                            + "ListView did not receive a notification. Make sure the content of "
                            + "your adapter is not modified from a background thread, but only from "
                            + "the UI thread. Make sure your adapter calls notifyDataSetChanged() "
                            + "when its content changes. [in ListView(" + getId() + ", " + getClass()
                            + ") with Adapter(" + mAdapter.getClass() + ")]");
                }
    

    那么mItemCount的值是在哪里赋值呢?mItemCount不是ListView的成员,而是ListView的超超类:AdapterView的成员,这个值也是在DataObserver.onChanged中设置的,您可參考AdapterView的源代码:

    class AdapterDataSetObserver extends DataSetObserver {
    
            private Parcelable mInstanceState = null;
    
            @Override
            public void onChanged() {
                mDataChanged = true;
                mOldItemCount = mItemCount;
                mItemCount = getAdapter().getCount(); //这里!注意使用方法getAdapter()
    
                // Detect the case where a cursor that was previously invalidated has
                // been repopulated with new data.
                if (AdapterView.this.getAdapter().hasStableIds() && mInstanceState != null
                        && mOldItemCount == 0 && mItemCount > 0) {
                    AdapterView.this.onRestoreInstanceState(mInstanceState);
                    mInstanceState = null;
                } else {
                    rememberSyncState();
                }
                checkFocus();
                requestLayout();
            }
    

    假设 getAdapter() != mAdapter就会发生故障:getAdatper返回的是mAdapter(即HeaderListViewAdapter),那么,mAdapter.getCount() == getAdapter().getCount() + header view count + footer view count.

    出现上面的问题就在所难免了。



  • 相关阅读:
    移动网页如何只调出数字键盘
    把HTML5网页封装成APP,APK的方法
    HTML5,微信开发原码社区
    display:table 水平居中
    input在苹果浏览器下变成圆角的解决方案
    四种方法解决DIV高度自适应问题
    jquery prop和attr的区别
    移动端网页JS框架-手机触摸事件框架,日历框架带滑动效果
    meta viewport标签的使用说明(手机浏览缩放控制)
    javascript json格式解析方法
  • 原文地址:https://www.cnblogs.com/yxwkf/p/3841240.html
Copyright © 2020-2023  润新知