Android之ViewPager.getChildCount()无限自增问题解析

来源:互联网 发布:赚钱宝监控软件 编辑:程序博客网 时间:2024/04/29 20:28

在最近的一次工作中,要做一个广告展示栏。果断使用ViewPager来实现,但是,在实现玩效果之后,打印日志发现一个巨大的问题。ViewPager在多次划动后 ViewPager.getChildCount(),竟然会无限自增


好在老夫花了2个小时研究了一下ViewGroup的源码,总算把问题解决了。接下来进入正题。


首先:

ViewPager是ViewGroup的子类,getChildCount()的作用并不是说获取该GroupView下有多少个子视图,而是有多少个可见视图


好,第二步,我们查看源码:

/*由此可以确定,mChildrenCount就是我们的线索*/    public int getChildCount() {        return mChildrenCount; }

/*自增*/    private void addInArray(View child, int index) {        View[] children = mChildren;        final int count = mChildrenCount;        final int size = children.length;        if (index == count) {            if (size == count) {                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];                System.arraycopy(children, 0, mChildren, 0, size);                children = mChildren;            }            children[mChildrenCount++] = child;        } else if (index < count) {            if (size == count) {                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];                System.arraycopy(children, 0, mChildren, 0, index);                System.arraycopy(children, index, mChildren, index + 1, count - index);                children = mChildren;            } else {                System.arraycopy(children, index, children, index + 1, count - index);            }            children[index] = child;            mChildrenCount++;            if (mLastTouchDownIndex >= index) {                mLastTouchDownIndex++;            }        } else {            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);        }    }

/*自减*/    private void removeFromArray(int index) {        final View[] children = mChildren;        if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {            children[index].mParent = null;        }        final int count = mChildrenCount;        if (index == count - 1) {            children[--mChildrenCount] = null;        } else if (index >= 0 && index < count) {            System.arraycopy(children, index + 1, children, index, count - index - 1);            children[--mChildrenCount] = null;        } else {            throw new IndexOutOfBoundsException();        }        if (mLastTouchDownIndex == index) {            mLastTouchDownTime = 0;            mLastTouchDownIndex = -1;        } else if (mLastTouchDownIndex > index) {            mLastTouchDownIndex--;        }    }

/*调用自减代码源码*/private void removeViewInternal(int index, View view) {      //...略}

然后,给大伙看下我的Adapter中是如何解决这个问题的:

/** * Created by yanjunhui * on 2016/9/10. * email:303767416@qq.com */public class RecomAdapter extends PagerAdapter {    static final String TAG = "RecomAdapter";    Context context;    LayoutInflater layoutInflater;    List<CourseInfo> dataList;    //网上经常看到的,自定义一个viewList来管理itemView,并直接在viewList内销毁视图做法是不正确的,    List<View> viewList = new ArrayList<>();    public RecomAdapter(Context context, List<CourseInfo> dataList) {        this.context = context;        this.dataList = dataList;        this.layoutInflater = LayoutInflater.from(context);    }    @Override    public int getCount() {        if (dataList == null) {            return 0;        }        return dataList.size();    }    /**     * 关联key 与 obj是否相等,即是否为同一个对象     * 如果一直返回false,界面不显示     */    @Override    public boolean isViewFromObject(View view, Object object) {        return view == object; // key    }    @Override    public void destroyItem(ViewGroup container, int position, Object object) {        //错误一:这里虽然销毁了viewList中的view,但是没有将ViewGroup内的view销毁,这会导致ViewGroup中的mChildren不能有效回收,并无限增长//        viewList.remove(position);        //错误二:这里的removeView,会在第一次遍历成功后发生错误,原因是因为执行ViewGroup中以下代码时返回false//       container.removeView(viewList.get(position));        /*注解:        因为我反射测试的时候,出了一点问题,没有确切证据论证。所以这是我大胆的猜测,viewList中的view对象指向内存发生变化导致。        如果它返回true的话,就可以顺利走到方法   removeViewInternal >> removeFromArray        而 removeFromArray 正是销毁 itemView 和管理mChildrenCount长度的源码处         private boolean removeViewInternal(View view) {            final int index = indexOfChild(view);            if (index >= 0) {                removeViewInternal(index, view);                return true;            }             return false;           }         */        //正确,不需判断,直接执行方法 removeViewInternal >> removeFromArray        container.removeViewAt(position);    }    @Override    public Object instantiateItem(ViewGroup container, int position) {        CourseInfo item = dataList.get(position);        View view = layoutInflater.inflate(R.layout.item_recomm, container, false);        TextView tvTheme = (TextView) view.findViewById(R.id.tv_theme);        SimpleDraweeView drawBg = (SimpleDraweeView) view.findViewById(R.id.draw_recomm);        tvTheme.setText(item.title);        FrescoUtils.getInstance().displayImage(drawBg, item.portraitpicid);        container.addView(view, position);        viewList.add(view);        return view;    }}

最后给大家贴一下,removeViewAt和removeView的区别

    public void removeViewAt(int index) {        removeViewInternal(index, getChildAt(index));        requestLayout();        invalidate(true);    }

    public void removeView(View view) {        if (removeViewInternal(view)) {            requestLayout();            invalidate(true);        }    }    private boolean removeViewInternal(View view) {        final int index = indexOfChild(view);        /*视图能在列表中找到,index >= 0才能往下执行*/        if (index >= 0) {            removeViewInternal(index, view);            return true;        }        return false;    }

好了,到这问题完美解决,赶紧去试一下吧。


0 0
原创粉丝点击