ViewPager 适配器优化,避免重复创建布局


在项目中,我们经常用到轮播图,前几天项目中再次遇到了这个需求。需求比较简单,三张图片无限循环,每隔2秒自动滑到下一页。需求不难,很快就写出来了。分析思路:

一、既然是无限制的,那么就适配器的 getCount() 方法返回值,返回一个 int 范围允许的最大值;
二、适配器中创建对象的 instantiateItem() 方法,我们展示图片,加载 url 时,对当前页 position 对 3 取余,这样获取集合中的图片就会对应;
三、既然可以左右滑动,如果开始位置是0,则向左滑动不了,所以一开始,就调用 ViewPager 的 setCurrentItem() 方法,直接跳转到一些页数,比如 3 * 1000 ,页数是3的倍数;
四、每隔2秒自动滑动到下一页,则我们需要定期调用 ViewPager 的 getCurrentItem() 方法获取当前的位置 mCurItem,然后调用 setCurrentItem() 方法,参数在原先的基础上加1;
五、这个属于细节优化,轮播图自动滑到了新页面,过了1秒,手指按下,这时候需要取消自动滑动功能,手指松开时再执行自动滑动功能,否则手指按下时图片自动滑到下一页,或者刚手动滑到下一页,马上又自动滑动了一页,体验效果很差;

既然有思路了,就按照思路来写代码


Activity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        initView();
    }

    String[] srcs = {
            "http://02.imgmini.eastday.com/mobile/20180528/20180528170057_5b2bd0949a9a84cde18b82c8dc649bb3_6_mwpm_03200403.jpg",
            "http://00.imgmini.eastday.com/video/vdianying/20180525/20180525213221696817529_1_mwpm_03200403.jpeg",
            "http://02.imgmini.eastday.com/video/vyule/20180525/20180525211750122871425_1_mwpm_03200403.jpg"};
            
    private ViewPager mViewPagerView;

    private void initView() {
        mViewPagerView = (ViewPager) mRootView.findViewById(R.id.viewpager);
        List<String> list = Arrays.asList(srcs);
        GameAdPager adp = new GameAdPager(list);
        mViewPagerView.setAdapter(adp);
        mViewPagerView.setCurrentItem(1000 * list.size());
        
        mViewPagerView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()){
                    case MotionEvent.ACTION_DOWN:
                        stopCurrent();
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        startCurrent();
                        break;
                }
                return false;
            }
        });
        startCurrent();
    }

    Handler mHandler = new Handler();
    int delayTime = 2;
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            mViewPagerView.setCurrentItem(mViewPagerView.getCurrentItem() + 1);
            mHandler.postDelayed(this, delayTime);
        }
    };
    
    private void startCurrent() {
        stopCurrent();
        mHandler.postDelayed(runnable, delayTime);
    }

    private void stopCurrent(){
        mHandler.removeCallbacks(runnable);
    }

    @Override
    public void onResume() {
        super.onResume();
        startCurrent();
    }

    @Override
    public void onPause() {
        super.onPause();
        stopCurrent();
    }


xml 布局 activity_test

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
    />

</FrameLayout>


适配器 

public class GamePager extends PagerAdapter {

    private List<String> mList;
    int mCount;

    private LinkedList<View> mRecycledViews = new LinkedList<>();

    public GamePager(List<String> list) {
        this.mList = list;
        mCount = mList.size();
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ImageView iv = new ImageView(container.getContext());
        iv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,               ViewGroup.LayoutParams.MATCH_PARENT));
        ImageLoaderUtils.with(container.getContext(), iv, mList.get(position % mCount));
        container.addView(iv);
        return iv;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

}


代码写的比较随意,命名各方面都不规范,大家谅解。到此,基本功能实现,感觉好像万事了,但这个代码还停留在初级阶段,试想一下,如果有用户不停的滑动,或者说让它自己滑动半个小时,虽说 ViewPager 有自动回收机制,最多加载3个页面,但我们这个功能是接近无限循环,这样适配器的 instantiateItem() 和 destroyItem() 方法不停的创建和销毁,就导致其中代码ImageView 也不停的创建,ImageLoaderUtils 是加载图片的工具类,有缓存机制,这个还好。但是不停的创建 ImageView 就浪费了,我们能想办法把它优化吗?

所谓优化性能,在我等菜鸟的水平上,也就是缓存和复用了,或者预加载数据,就这三板斧,在这个地方既然是不停的创建对象,那么咱们就把回收的象给缓存起来,下次需要创建对象时,优先去缓存中去找,如果缓存没有对象再创建,这样就能避免不必要的对象创建。 


public class GamePager extends PagerAdapter {

    private List<String> mList;
    int mCount;

    private LinkedList<View> mRecycledViews = new LinkedList<>();

    public GamePager(List<String> list) {
        this.mList = list;
        mCount = mList.size();
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        ImageView iv;
        if (mRecycledViews != null && mRecycledViews.size() > 0) {
            iv = (ImageView) mRecycledViews.getFirst();
            mRecycledViews.removeFirst();
        } else {
            iv = new ImageView(container.getContext());
            iv.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
        }

        ImageLoaderUtils.with(container.getContext(), iv, mList.get(position % mCount));
        container.addView(iv);
        return iv;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
        if (object != null) {
            mRecycledViews.addLast((View) object);
        }
    }

}


适配器中添加一个集合,destroyItem() 方法中,系统需要我们把view布局给移除掉,这时候我们把它装到集合中;instantiateItem() 方法中是创建view,我们先去集合中获取,如果没有再 new 布局,就这么简单的添加个集合,就避免了布局view的重复创建。


进一步优化,我们可以仿照 RecyclerView 的方式,抽取一个抽象的适配器基类,创建 ViewHolder 辅助类,在它里面保存布局view的各种控件,instantiateItem() 中创建 ViewHolder,老规矩,还是先从集合中获取,如果没有就 new 一个,对外暴露 onCreateViewHolder() 方法,创建 holder 后,把它通过view的 setTag() 方法保存到view中;对外暴露 onBindViewHolder() 方法绑定数据;在 destroyItem() 中,通过view的 getTag() 方法获取到 holder,添加到集合中。 这样再写适配器就像写RecyclerView的适配器一样了。

发布了176 篇原创文章 · 获赞 11 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/99463214