ViewPager + Volley 异步多线程图片加载实现Banner效果

前提:使用过Volley,熟悉实现PagerAdapter
引用:http://www.it165.net/pro/html/201406/15240.html

首先,布局文件与图形

  • 主布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <android.support.v4.view.ViewPager android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/view_pager">

            </android.support.v4.view.ViewPager>
        <LinearLayout
            android:id="@+id/indicator"
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="100px"
            android:layout_alignParentBottom="true"
            android:gravity="center_horizontal"></LinearLayout>
        </RelativeLayout>
</LinearLayout>
  • ViewPager的Item布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>
  • 指示图标(小点点) - 未选择状态
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size android:width="20dp" android:height="20dp"/>
    <solid android:color="#949693"/>
</shape>
  • 指示图标(小点点) - 选择状态
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size android:width="20dp" android:height="20dp"/>
    <solid android:color="#97C606"/>
</shape>

第二步,实现PagerAdpater

    private class MyAdapter extends PagerAdapter{

        //这个列表中的View是实例化后的view_pager_item,
        // 只加载了文字,尚未加载图片,因为图片采用异步的方式加载
        private List<View> mList;
        //Volley的请求队列
        private RequestQueue mRequestQueue;
        //上下文,这个建立请求必须
        private Context mContext;


        public MyAdapter(Context context,List<View> list){
            mContext = context;
            //建立requestQueue
            mRequestQueue = Volley.newRequestQueue(context);
            mList = list;
        }

        @Override
        public Object instantiateItem(final ViewGroup container, final int position) {
            //取余才能正确获取到urls和mList
            final int cur_position = position%urls.length;
            //实例化ImageLoader,其中CustomeImageCache为自定义的Cache,下面会给出
            ImageLoader loader = new ImageLoader(mRequestQueue,new CustomeImageCache());
            //发送请求,如果有缓存,则直接返回,如果没有则发送网络请求加载图片
            ImageLoader.ImageContainer imageContainer = loader.get(urls[cur_position], new ImageLoader.ImageListener() {
                @Override
                public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                    //isImmediate 为 true 可以设置默认图片
                    View view = mList.get(cur_position);
                    ImageView image = (ImageView) view.findViewById(R.id.image);
                    image.setImageBitmap(response.getBitmap());
                    //先移除,在添加,不然就会报出 已存在父容器的异常
                    container.removeView(mList.get(cur_position));
                    container.addView(mList.get(cur_position)); 
                }

                @Override
                public void onErrorResponse(VolleyError error) {
                    //这里可以加载错误图片
                }
            });
            //如果有缓存
            if(imageContainer.getBitmap() != null){
                View view = mList.get(cur_position);
                ImageView image = (ImageView) view.findViewById(R.id.image);
                image.setImageBitmap(imageContainer.getBitmap());
                container.removeView(mList.get(cur_position));
                container.addView(mList.get(cur_position));
            }
            return mList.get(cur_position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            final int cur_position = position%urls.length;
            container.removeView(mList.get(cur_position));
        }

        @Override
        public int getCount() {
            //让ViewPager无限滚动
            return Integer.MAX_VALUE;
        }

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

自定义图片缓存类

    static class CustomeImageCache implements ImageLoader.ImageCache{

        LruCache<String,Bitmap> mCache;
        public CustomeImageCache(){
            int max =1024 * 1024 * 1024;
            mCache = new LruCache<>(max);
        }

        @Override
        public Bitmap getBitmap(String url) {
            return mCache.get(url);
        }

        @Override
        public void putBitmap(String url, Bitmap bitmap) {
            mCache.put(url,bitmap);
        }
    }

在实现的过程中,我有个疑问,看下面的代码(不完整)。

 ImageLoader.ImageContainer imageContainer = loader.get(urls[cur_position], new ImageLoader.ImageListener() {
                @Override
                public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                    //isImmediate 为 true 可以设置默认图片
                    View view = mList.get(cur_position);
                    ImageView image = (ImageView) view.findViewById(R.id.image);
                    image.setImageBitmap(response.getBitmap());
                    //先移除,在添加,不然就会报出 已存在父容器的异常
                    container.removeView(mList.get(cur_position));
                    container.addView(mList.get(cur_position));
                }

显而易见,我们对UI进行了操作,但是这并不是在主线程中,可是却有效果。带着疑问去看了ImageLoader的源码。看到了这个。。

    /** Handler to the main thread. */
    private final Handler mHandler = new Handler(Looper.getMainLooper());

原来这里获取了主线程的Looper,所以能去更新UI.下面给出那个方法。

   /**
     * Starts the runnable for batched delivery of responses if it is not already started.
     * @param cacheKey The cacheKey of the response being delivered.
     * @param request The BatchedImageRequest to be delivered.
     */
    private void batchResponse(String cacheKey, BatchedImageRequest request) {
        mBatchedResponses.put(cacheKey, request);
        // If we don't already have a batch delivery runnable in flight, make a new one.
        // Note that this will be used to deliver responses to all callers in mBatchedResponses.
        if (mRunnable == null) {
            mRunnable = new Runnable() {
                @Override
                public void run() {
                    for (BatchedImageRequest bir : mBatchedResponses.values()) {
                        for (ImageContainer container : bir.mContainers) {
                            // If one of the callers in the batched request canceled the request
                            // after the response was received but before it was delivered,
                            // skip them.
                            if (container.mListener == null) {
                                continue;
                            }
                            if (bir.getError() == null) {
                                container.mBitmap = bir.mResponseBitmap;
                                container.mListener.onResponse(container, false);
                            } else {
                                container.mListener.onErrorResponse(bir.getError());
                            }
                        }
                    }
                    mBatchedResponses.clear();
                    mRunnable = null;
                }

            };
            // Post the runnable.
            mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
        }
    }

Volley确实很好用啊,省了点事。

下一步,初始化Layout与ViewPager

onCreate方法

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        List<View> list = new ArrayList<>();
        mInflater = LayoutInflater.from(this);

        /*
        * 创建多个item 每一条都是一个item
        * 从服务器获取完数据(文章标题、url地址)后,在设置适配器
        * */
        View item = null;
         for(int i=0; i<urls.length; i++){
            item = mInflater.inflate(R.layout.view_pager_item,null);
             ((TextView)item.findViewById(R.id.text_view)).setText("第" + i +"个" );
             list.add(item);
        }

        //创建适配器,把组装完的组件传递进去
        MyAdapter myAdapter = new MyAdapter(this,list);
        mViewPager.setAdapter(myAdapter);

        //绑定监听,Viewpager的页面监听
        mViewPager.setOnPageChangeListener(new MyListener());

        //初始化指示器(下方给出代码)
        //将第一个点点选中,其他的不选中
        initIndicator();

        //设置定时器,延时两秒后,每两秒进行一次滚动
        Timer timer = new Timer();
        timer.schedule( new TimerTask(){

            @Override
            public void run() {
                handler.sendEmptyMessage(0x123);
            }
        },2000,2000);
     }

Handler

    Handler handler = new Handler(){
        private boolean mIsPause = false;
        @Override
        public void handleMessage(Message msg) {

            if(msg.what == 0x123) {
                //滚动
                if (!mIsPause) {
                    mViewPager.setCurrentItem(mViewPager.getCurrentItem() + 1);
                }
            }else if(msg.what == 0x234){
                //停止滚动
                mIsPause = true;
            }else if(msg.what == 0x345){
                //开始滚动
                mIsPause = false;
            }
        }
    };

initIndicator() 初始化指示器

    /**
     * 初始化引导图标
     * 动态创建多个小圆点,然后组装到线性布局里
     */
    private void initIndicator() {
        ImageView imageView;
        View v = findViewById(R.id.indicator);
        for(int i = 0 ; i< mIndicatorImgs.length; i++){
            imageView = new ImageView(this);
            LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(10,10);
            layoutParams.setMargins(7,10,7,10);
            imageView.setLayoutParams(layoutParams);
            mIndicatorImgs[i] = imageView;
            if(i==0){
                mIndicatorImgs[i].setBackgroundResource(R.drawable.indicator_selected);
            }else{
                mIndicatorImgs[i].setBackgroundResource(R.drawable.indicator);
            }
            ((ViewGroup)v).addView(mIndicatorImgs[i]);
        }
    }

ViewPager的页面变化监听实现

    private class MyListener implements ViewPager.OnPageChangeListener{

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            int cur_position = position%urls.length;
            for(int i=0; i<mIndicatorImgs.length; i++){
                mIndicatorImgs[i].setBackgroundResource(R.drawable.indicator);
            }
            mIndicatorImgs[cur_position].setBackgroundResource(R.drawable.indicator_selected);

        }

        @Override
        public void onPageScrollStateChanged(int state) {
            if(ViewPager.SCROLL_STATE_DRAGGING == state){
                //用户碰到了
                handler.sendEmptyMessage(0x234);
            }else{
                //用户离开了
                handler.sendEmptyMessage(0x345);
            }
        }
    }

当用户碰到了ViewPager获取正在移动ViewPager,则向handler发送消息,让timerTask发送过来的定时任务不处理,当ViewPager闲置后,handler发送消息,开始处理任务。

声明的成员变量

  private ViewPager mViewPager;
    private LayoutInflater mInflater;
    private ImageView[] mIndicatorImgs = new ImageView[7];

    String[] urls = {

            "http://a.hiphotos.baidu.com/image/pic/item/3bf33a87e950352ad6465dad5143fbf2b2118b6b.jpg",
            "http://a.hiphotos.baidu.com/image/pic/item/c8177f3e6709c93d002077529d3df8dcd0005440.jpg",
            "http://f.hiphotos.baidu.com/image/pic/item/7aec54e736d12f2ecc3d90f84dc2d56285356869.jpg",
            "http://e.hiphotos.baidu.com/image/pic/item/9c16fdfaaf51f3de308a87fc96eef01f3a297969.jpg",
            "http://d.hiphotos.baidu.com/image/pic/item/f31fbe096b63f624b88f7e8e8544ebf81b4ca369.jpg",
            "http://h.hiphotos.baidu.com/image/pic/item/11385343fbf2b2117c2dc3c3c88065380cd78e38.jpg",
            "http://c.hiphotos.baidu.com/image/pic/item/3801213fb80e7bec5ed8456c2d2eb9389b506b38.jpg"

    };

完整代码详见:https://github.com/ding102992/scrollbannerdemo

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值