天天看點

Android 循環廣告位輪播圖的封裝實作

描述

輪播圖,也有叫Banner圖,基礎上大部分項目都會用到,它可以在有限的螢幕上盡可能的展示比較多的資源,剛開始的時候知道用ViewPager做,最近的項目中将其封裝成一個獨立View,友善複用!

實作

1. 擴充卡

主要是利用ViewPager,其是support-v4中的一個類,主要用于左右滑屏,因為主要是顯示圖檔,是以直接選用PagerAdapter作為擴充卡(其子類FragmentPagerAdapter可以和Fragment組合使用使用頁面滑動);ViewPager有左右預加載的功能,這樣的實作方式,不至于滑到下一頁的時候,不至于出現空白;PagerAdapter主要需要實作4個方法,用于控制圖檔的顯示和銷毀,還有控制起無限循壞的條件 :

private class MyAdapter extends PagerAdapter {

        //為了複用
        private List<ImageView> imgCache = new ArrayList<ImageView>();

        @Override
        public int getCount() {
            //無限滑動
            return Integer.MAX_VALUE;
        }

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

        @Override
        public Object instantiateItem(ViewGroup container,final int position) {

            ImageView iv;

            //擷取ImageView對象
            if (imgCache.size() > ) {
                iv = imgCache.remove();
            } else {
                iv = new ImageView(mContext);
            }
            iv.setScaleType(ScaleType.FIT_XY);

            iv.setOnTouchListener(new OnTouchListener() {
                private int downX = ;
                private long downTime = ;

                @Override
                public boolean onTouch(View v,MotionEvent event) {
                    switch (event.getAction()) {
                        case MotionEvent.ACTION_DOWN:
                            mAutoRollRunnable.stop();
                            //擷取按下的x坐标
                            downX = (int) v.getX();
                            downTime = System.currentTimeMillis();
                            break;
                        case MotionEvent.ACTION_UP:
                            mAutoRollRunnable.start();
                            int moveX = (int) v.getX();
                            long moveTime = System.currentTimeMillis();
                            if (downX == moveX && (moveTime - downTime < )) {//點選的條件
                                //輪播圖回調點選事件
                                headerViewClickListener.HeaderViewClick(position % mUrlList.size());
                            }
                            break;
                        case MotionEvent.ACTION_CANCEL://快速滑動不執行up
                            mAutoRollRunnable.start();
                            break;
                    }
                    return true;
                }
            });

            //加載圖檔
            Glide.with(mContext).load(mUrlList.get(position % mUrlList.size()))
                    .error(R.mipmap.ic_launcher).into(iv);

            ((ViewPager) container).addView(iv);

            return iv;
        }

        @Override
        public void destroyItem(ViewGroup container,int position,Object object) {
            if (object != null && object instanceof ImageView) {
                ImageView iv = (ImageView) object;
                ((ViewPager) container).removeView(iv);
                imgCache.add(iv);
            }
        }
    }
           

注意 :

(1) position % mUrlList.size(),因為getCount方法擷取的是Integer.MAX_VALUE,是以這裡所有的position都不是真正意義上的position;

(2) 之是以使用setOnTouchListener,沒有用setOnClickListener主要是為了控制當手指觸摸到輪播圖上時停止輪播,過一定時間離開後繼續輪播.

2. 自動輪播

每個一段時間做一個操作,有很多方式實作如Timer和TimerTask的組合,這裡利用的是Handler的 boolean postDelayed(Runnable r,long delayMillis) 方法,此方法可以延時執行任務:

private class AutoRollRunnable implements Runnable {
        // 标志
        boolean isRunning = false;

        public void start() {
            if (!isRunning) {
                isRunning = true;
                mHandler.removeCallbacks(this);
                mHandler.postDelayed(this,);
            }
        }

        public void stop() {
            if (isRunning) {
                mHandler.removeCallbacks(this);
                isRunning = false;
            }
        }

        @Override
        public void run() {
            if (isRunning) {
                mViewPager.setCurrentItem(mViewPager.getCurrentItem() + );
                mHandler.postDelayed(this,);
            }
        }
    }

    //開始輪播
    public void startRoll() {
        mAutoRollRunnable.start();
    }

    // 停止輪播
    public void stopRoll() {
        mAutoRollRunnable.stop();
    }
           

當頁面離開時,停止輪播:

//停止輪播
@Override
protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    stopRoll();
}
           

3. 控制Banner的高度

由于手機的螢幕大小分别很多,如果直接寫死肯定不好,我的做法是讓Banner占整個手機螢幕高度的1/4,這樣的适配能滿足現在需求:

//初始化view
    private void initView() {
        View.inflate(mContext,R.layout.view_header,this);
        mViewPager = (ViewPager) findViewById(R.id.vp);
        mDotLl = (LinearLayout) findViewById(R.id.ll_dot);

        //讓banner的高度是螢幕的/
        ViewGroup.LayoutParams vParams = mViewPager.getLayoutParams();
        vParams.height = (int) (DisplayUtil.getMobileHeight(mContext) * );
        mViewPager.setLayoutParams(vParams);
    }
           

其中的view_header如下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" >

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:focusable="true"
        android:focusableInTouchMode="true" />

    <LinearLayout
        android:id="@+id/ll_dot"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|right"
        android:paddingRight="8dip"
        android:layout_marginBottom="8dip"
        android:gravity="center"
        android:orientation="horizontal" >
    </LinearLayout>

</FrameLayout>
           

4. 初始化Banner底部的點

這些點應該是有多少圖檔(圖檔連結),就應該有多少個點:

//設定資料
    public void setImgUrlData(List<String> urlList) {
        this.mUrlList = urlList;
        if (mUrlList != null && !mUrlList.isEmpty()) {
            //清空資料
            dotList.clear();
            mDotLl.removeAllViews();
            ImageView dotIv;
            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);
            for (int i = ; i < mUrlList.size(); i++) {
                dotIv = new ImageView(mContext);
                if (i == ) {
                    dotIv.setBackgroundResource(R.mipmap.banner_dot_select);
                } else {
                    dotIv.setBackgroundResource(R.mipmap.banner_dot_normal);
                }
                //設定點的間距
                params.setMargins(,,DisplayUtil.dip2px(mContext,),);
                dotIv.setLayoutParams(params);

                //添加點到view上
                mDotLl.addView(dotIv);
                //添加到集合中,以便控制其切換
                dotList.add(dotIv);
            }
        }

        mAdapter = new MyAdapter();
        mViewPager.setAdapter(mAdapter);

        //設定viewpager初始位置,+10000就夠了
        mViewPager.setCurrentItem(urlList.size() + );
        startRoll();
    }
           

5. 用法

在上面的 setImgUrlData 方法後面, 我是直接初始化并設定了Adapter,并直接讓輪播圖滾動了起來,這樣隻需要少量的代碼就可以用了,或者直接在布局檔案中使用 :

RollHeaderView rollHeaderView = new RollHeaderView(this);
        rollHeaderView.setImgUrlData(imgUrlList);
        rollHeaderView.setOnHeaderViewClickListener(new RollHeaderView.HeaderViewClickListener() {
            @Override
            public void HeaderViewClick(int position) {
                Toast.makeText(MainActivity.this,"點選 : " + position,Toast.LENGTH_SHORT).show();
            }
        });
           

比起之前直接在Activity裡面用ViewPager不知道簡潔了多少倍!

6. 效果

Android 循環廣告位輪播圖的封裝實作

7. 需改進

(1) 每次總是選中第一張圖檔和點;

(2) 可以加入文字資訊說明;

(3) 不用xml,直接用代碼寫布局;

(4) ……

8. 源碼:

點選檢視源碼