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
- Android之ViewPager.getChildCount()无限自增问题解析
- Android ViewPager无限自动轮播
- android之ViewPager无限轮播两种思路
- android viewPager网络加载图片无限自动轮播
- ViewPager.getChildCount() 含义
- ViewPager无限自动轮播
- ViewPager无限自动轮播
- ViewPager之无限轮播图
- Android无限循环ViewPager
- [Android]无限循环ViewPager
- Android之ViewPager实现图片无限循环轮播
- android之滑动悬浮tab&无限循环的viewPager
- android之自动轮播的ViewPager
- viewpager无限循环的问题
- ViewPager无限自动轮播+动画
- viewpager自添加指示器,无限轮播
- ViewPager无限自动轮播+动画
- ViewPager图片无限自动轮播
- android轻量级缓存框架ASimpleCache分析
- IntentService的使用方法和原理
- c++中如何写参数不定的函数
- 当一个Activity加多个Fragment的时候,点击导航返回按钮无效或者点击back键就把Activity关闭了的问题
- (读书笔记)mybatis (三)-----environments详解
- Android之ViewPager.getChildCount()无限自增问题解析
- android procrank
- 与nginx性能相关的linux内核参数
- windows select pipe
- iOS发布--静态库bitCode问题
- 判断String是否为空
- 今日头条 --新闻阅读器
- UVA-10387 Billiard
- ME2N标准报表中加EKKO/EKPO中没显示的字段