android 的ViewPager的预加载机制,特殊的需求。

这年头,做过android的基本上都用过ViewPager,稍微熟悉的人都知道要配合PagerAdapter适配器,实现适配器的四个方法。再深入一点的知道ViewPager的预加载机制,也就是说,如果集合数据大于1,那么ViewPager刚开始时是一次加载两个View,里面最多时有3个View,不停地创建和销毁。大部分时候,控件本省提供的方法够用,怕就怕有一些特殊的需求,比如说刚进入一个ViewPager,点击一下第一个View,改变了它的样式,同时要求后面的所有View都改变。除了预加载的第二张,其他的都是重新生成的,可以在适配器里加个标示控制一下,那么,预加载的第二张怎么办?下面有几种思路。

产品有个需求,ViewPager里面包含8个页面,页面格式一致。每个页面都有 textview文字 和 imagView图片及button按钮。viewpager产生,点击imagView,textview文字隐藏,再点击,出现;如果无网络,当前图片没有加载出来,点击button重新加载,恰好有网络,图片加载出来,同时它前面和后面的包括预加载的都要加载出来。

一、修改预加载机制,使其每次加载一张,不用预加载。ViewPager的setOffscreenPageLimit(int limit),来设置ViewPager预加载的数量,但可惜的是,如果传入的参数小于1时,会自动设置为1。所以只能设置为2、3、4等等,增加预加载的数量,通过此方法禁止预加载恐怕行不通。那么,肿么办?干脆,修改源码吧,由于每个版本都有v4包,ViewPager里卖的内容也各有不同,所以,使用低版本v4包里的ViewPager,完全copy出来一份,将其中的DEFAULT_OFFSCREEN_PAGES值改为0即可,这种方法和源码网上一搜一大把,不是本文的重点,所以还需要各位百度去。这样每次都是加载一张,可以解决产品的问题。

二、在原基础上,根据适配器里面的方法,View的生成和销毁,写代码,通过逻辑,手动刷新页面。
instantiateItem(ViewGroup container,final int position)添加元素,destroyItem(ViewGroup container, int position, Object object)里面移除元素。

第一种方法,把ViewPager以参数的形式传递给适配器,在适配器里面要用到。ViewPager的集合元素大于等于3个元素时,如果当前页面在ViewPager的首页或者末尾页面,ViewPager其实是包含2个childView,处于非头部和尾部时,ViewPager含有3个childView。这是由于上述两个方法控制的,所以,此时,当点击imagV

iew,textview文字需要隐藏,我们可以在imagView的点击事件里面加入以下代码, 

 private void explainClickGone() { 

         for(int x = 0, childNum = mImageNewsViewPager.getChildCount();x < childNum; x++){ 

             tvGoneOrVisible(x); 

         } 

     } 


     /** 

      * 找到文字说明的textview,使其隐藏或出现 

      * @param index 

      */ 

     private void tvGoneOrVisible(int index) { 

         View view = mImageNewsViewPager.getChildAt(index); 

         if (view instanceof ViewGroup) { 

             TextView tv = (TextView) view.findViewById(R.id.text); 

             if (isComment) { 

                 tv.setVisibility(View.VISIBLE); 

             } else { 

                 tv.setVisibility(View.GONE); 

             } 

         } 

     }



isComment是个boolean值,通过它判断文字是隐藏还是展示;mImageNewsViewPager是ViewPager,通过遍历,拿到每一个它包含的子元素,然后拿出来,在通过findViewById找到textview文字的控件,然后就是隐藏或展示,别忘了在imagView的点击事件里,对isComment值取反。这样,预加载的图片也就被改变了,其他的都是新生产的View,可以在生产布局时根据isComment来判断文字是显示还是隐藏。
最开始是想根据是否是头部或尾部,单独改变当前页面后面一张或前面一张,但viewpager的滑动的话,来回添加移除,根据索引已经不好判断了,所以干脆整体遍历以便,反正数据量也不大。

第二种方法:以图片为例,也是把ViewPager以参数的形式传递给适配器,在适配器里面要用到。
android提供了一种优化的Map集合,SparseArray。可以写一个类,PagerImage,里面包含两个属性,String imageUrl和ImageView imageView;
在适配器里定义成员变量private SparseArray<PagerImage> mNoLoaderImages = new SparseArray<PagerImage>();// 无图模式下,记录没有被加载的图片的信息。在instantiateItem(ViewGroup container,final int position)里面对图片加载进行监听,如果图片加载失败了,记得在回调方法添加以下代码,mNoLoaderImages.put(position,new PagerImage(url,imageView));在图片成功加载的回调里添加mNoLoaderImages.delete(position);简单解释以下,当图片下载失败了,把图片的url和ImageView控件封装到一个bean里面,然后以position为key,存储到SparseArray集合,如果再次加载图片成功了,就把它从集合中移除出去。对SparseArray不了解的可以百度,如果还不理解,就简单的把它看成是个key和value的map集合,就像HashMap一样就行了。下一步是在button按钮的点击回调事件里,添加图片加载的逻辑。
for(int x = 0,count = mNoLoaderImages.size(); x< count; x++ ) {
                        PagerImage pagerImage = mNoLoaderImages.get(x);
                        if(pagerImage != null){
                            ImageLoader.withFitCenterForSource(mContext,pagerImage.getImageView(), pagerImage.getImageUrl());
                        }
                    }
同时,记得在
@Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        mNoLoaderImages.delete(position);
        container.removeView((View) object);
    }
否则就和容易遭成内存泄露了。 集合里都是加载图片失败的,根据position来记录的信息,然后遍历,更具position拿出url和ImageView,再次加载。逻辑理顺后,就这样了。