1. 程式人生 > >Android 輪播圖 實現 一 :三方框架 自定義viewPager (CircleViewPager.)實現無限輪播。

Android 輪播圖 實現 一 :三方框架 自定義viewPager (CircleViewPager.)實現無限輪播。

使用流程:

1  。 gradle中新增依賴


compile 'com.zhpan.library:viewpager:1.0.3'

2.在xml檔案中新增如下程式碼:

<com.zhpan.viewpager.view.CircleViewPager
        android:id="@+id/viewpager2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginEnd="35dp"
        android:layout_marginStart="35dp"
app:interval="5000" />
3.CircleViewPager屬性
mList.add(R.drawable.books_image_1);
mList.add(R.drawable.books_image_2);
mList.add(R.drawable.books_image_3);
mList.add(R.drawable.books_image_4);
mList.add(R.drawable.books_image_5);

//  是否顯示指示器
mViewpager.isShowIndicator(true);
//  設定指示器位置
mViewpager.setIndicatorGravity(CircleViewPager.IndicatorGravity.CENTER
); // 設定指示器圓點半徑 mViewpager.setIndicatorRadius(6); // 設定圓點指示器顏色 可以更改指示器顏色。 mViewpager.setIndicatorColor(getResources().getColor(R.color.colorAccent), getResources().getColor(R.color.colorPrimary)); // 設定是否無限迴圈 mViewpager.setCanLoop(true); // 設定是否自動輪播 mViewpager.setAutoPlay(true); // 設定圖片切換時間間隔
mViewpager.setInterval(3000); // 設定頁面點選事件 mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() { @Override public void onPageClick(int position) { UIUtils.showToast(" 我是輪播圖 我點選了!"+ position + "張"); } }); // 設定資料 mViewpager.setPages(mList, new HolderCreator<ViewHolder>() { @Override public ViewHolder createViewHolder() { return new MyViewHolder(); } });

4.自定義 ViewHolder 

public class MyViewHolder implements ViewHolder<Integer> {
    private ImageView mImageView;

    @Override
public View createView(Context context, int position) {
        // 返回頁面佈局檔案
View view = LayoutInflater.from(context).inflate(R.layout.banner_item, null);
        mImageView = (ImageView) view.findViewById(R.id.banner_image);
        return view;
    }

    @Override
public void onBind(Context context, Integer data, int position, int size) {
//        ImageLoaderUtil.loadImg(mImageView, (String) data);
Glide.with(context).load(data).into(mImageView);

    }

}

5.為防止記憶體洩露在onDestory()中停止圖片輪播

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mViewpager.stopLoop();
    }

二、CircleViewPager的實現思路 
頁面迴圈切換最容易出現問題的地方就是在最後一頁向第一頁切換或者第一頁向最後一頁切換時,在這個切換過程中很容易出現空白頁面。怎麼解決這個問題? 
CircleViewPager的實現的思路是在第一張圖片前和最後一張圖片後分別新增一個ImageView,最前邊的ImageView背景設定為最後一張圖片,最後一個ImageView背景設定第一張圖片。當我們判斷滑動到最後一個ImageView時則設定ViewPager.setCurrentItem(1),讓其自動切換到第一張圖片,這樣在從最後一頁切換到第一頁時由於圖片是用的同一張圖片,所以就會使切換效果顯得很流暢自然。同理,當向左滑動到第0個ImageView時用ViewPager.setCurrentItem(length)自動切換到倒數第二張圖片,第0個ImageView和倒數第二個ImageView圖片相同,這樣就使滑動效果顯得很自然。

三、CircleViewPager具體實現 
1.新增自定義屬性。以在value目錄下建立attrs.xml檔案,檔案中我們可以定義一些用到的屬性,attrs.xml中定義的屬性如下:

<resources>
    <declare-styleable name="MyViewPager">
        <!--選中時的圓點圖片-->
        <attr name="lightDotRes" format="reference"/>
        <!--未選中時的圓點圖片-->
        <attr name="darkDotRes" format="reference"/>
        <!--圓點半徑-->
        <attr name="dotWidth" format="dimension"/>
        <!--頁面切換時間間隔-->
        <attr name="interval" format="integer"/>
    </declare-styleable>
</resources>

2.新建CircleViewPager的佈局檔案view_pager_layout.xml,程式碼如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

    <LinearLayout
        android:id="@+id/ll_main_dot"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@id/vp_main"
        android:layout_marginBottom="10dp"
        android:gravity="center_horizontal"
        android:orientation="horizontal" />
</RelativeLayout>

3.定義CircleViewPager類並繼承FrameLayout,並在構造方法中初始化資料,程式碼如下:

 public CircleViewPager(Context context) {
        super(context);
        init(null);
    }

    public CircleViewPager(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
        init(attrs);
    }

    public CircleViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            initData();
            setIndicatorImage();
            setViewPager();
            setIndicatorLocation();
        }
    }

    private void init(AttributeSet attrs) {
        if (attrs != null) {
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CircleViewPager);
            mLightIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_lightDotRes, R.drawable.red_dot);
            mDarkIndicator = typedArray.getResourceId(R.styleable.CircleViewPager_darkDotRes, R.drawable.red_dot_night);
            mDotWidth = typedArray.getDimension(R.styleable.CircleViewPager_dotWidth, 20);
            interval = typedArray.getInteger(R.styleable.CircleViewPager_interval, 3000);
            typedArray.recycle();
        }
         mView = LayoutInflater.from(getContext()).inflate(R.layout.view_pager_layout, this);
        mLlDot = (LinearLayout) mView.findViewById(R.id.ll_main_dot);
        mViewPager = (ViewPager) mView.findViewById(R.id.vp_main);
        mList = new ArrayList<>();
        mListAdd = new ArrayList<>();
        mIvDotList = new ArrayList<>();
    }

4.重寫onLayout()方法,根據圖片URL集合建立圖片對應的ImageVIew和小圓點對應的ImageView.

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (changed) {
            initData();
            setIndicatorImage();
            setViewPager();
            setIndicatorLocation();
        }
    }

    //  根據mList資料集構造mListAdd
    private void initData() {
        if (mList.size() == 0) {
            mView.setVisibility(GONE);
        } else if (mList.size() == 1) {
            mListAdd.add(mList.get(0));
        } else if (mList.size() > 1) {
            for (int i = 0; i < mList.size() + 2; i++) {
                if (i == 0) {   //  判斷當i=0為該處的mList的最後一個數據作為mListAdd的第一個資料
                    mListAdd.add(mList.get(mList.size() - 1));
                } else if (i == mList.size() + 1) {   //  判斷當i=mList.size()+1時將mList的第一個資料作為mListAdd的最後一個數據
                    mListAdd.add(mList.get(0));
                } else {  //  其他情況
                    mListAdd.add(mList.get(i - 1));
                }
            }
        }
    }

   //  設定輪播小圓點
    private void setIndicatorImage() {
        //  設定LinearLayout的子控制元件的寬高,這裡單位是畫素。
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams((int) mDotWidth, (int) mDotWidth);
        params.rightMargin = (int) (mDotWidth / 1.5);
        if (mList.size() > 1) {
            //  for迴圈建立mUrlList.size()個ImageView(小圓點)
            for (int i = 0; i < mList.size(); i++) {
                ImageView imageViewDot = new ImageView(getContext());
                imageViewDot.setLayoutParams(params);
                //  設定小圓點的背景為暗紅圖片
                imageViewDot.setBackgroundResource(mDarkIndicator);
                mLlDot.addView(imageViewDot);
                mIvDotList.add(imageViewDot);
            }
        }
        //設定第一個小圓點圖片背景為紅色
        if (mList.size() > 1) {
            mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

5.為ViewPager適配資料

 private void setViewPager() {
        CirclePagerAdapter<T> adapter = new CirclePagerAdapter<>(mListAdd, this, holderCreator);
        mViewPager.setAdapter(adapter);
        mViewPager.setCurrentItem(currentPosition);

        setPageChangeListener();
        startLoop();
        setTouchListener();
        if (showIndicator) {
            mLlDot.setVisibility(VISIBLE);
        } else {
            mLlDot.setVisibility(GONE);
        }
    }

6.接下來為ViewPager新增頁面改變的監聽事件。

//  ViewPager頁面改變監聽
    private void setPageChangeListener() {
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                pageSelected(position);
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                //  當state為SCROLL_STATE_IDLE即沒有滑動的狀態時切換頁面
                if (state == ViewPager.SCROLL_STATE_IDLE) {
                    mViewPager.setCurrentItem(currentPosition, false);
                }
            }
        });
    }

private void pageSelected(int position) {
        if (position == 0) {    //判斷當切換到第0個頁面時把currentPosition設定為list.size(),即倒數第二個位置,小圓點位置為length-1
            currentPosition = mList.size();
            dotPosition = mList.size() - 1;
        } else if (position == mList.size() + 1) {    //當切換到最後一個頁面時currentPosition設定為第一個位置,小圓點位置為0
            currentPosition = 1;
            dotPosition = 0;
        } else {
            currentPosition = position;
            dotPosition = position - 1;
        }
        //  把之前的小圓點設定背景為暗紅,當前小圓點設定為紅色
        mIvDotList.get(prePosition).setBackgroundResource(mDarkIndicator);
        mIvDotList.get(dotPosition).setBackgroundResource(mLightIndicator);
        prePosition = dotPosition;
    }

至此,ViewPager已經可以實現滑動,並且圓點也會跟隨頁面滑動而改變。 
四、CircleViewPager實現自動輪播 
1.自動輪播的實現,在第三節第5步的setViewPager()方法中呼叫下面startLoop()方法即可開啟自動輪播

 Handler mHandler = new Handler();
    Runnable mRunnable = new Runnable() {
        @Override
        public void run() {
            if (mViewPager.getChildCount() > 1) {
                mHandler.postDelayed(this, interval);
                currentPosition++;
                mViewPager.setCurrentItem(currentPosition, true);
            }
        }
    };

private void startLoop() {
        if (!isLoop && mViewPager != null) {
            mHandler.postDelayed(mRunnable, interval);// 每兩秒執行一次runnable.
            isLoop = true;
        }
    }

2.自動輪播實現後發現會有些問題,即在手動滑動頁面時,頁面仍然會自動切換。這樣體驗效果是非常不好的,因此我們需要在手動滑動時停止自動輪播,當手動滑動結束時再開啟自動輪播。因此我們可以重寫ViewPager的onTouch事件進行處理,當觸發ACTION_DOWN和ACTION_MOVE時停止自動輪播,當觸發ACTION_UP和ACTION_CANCEL時再開啟自動輪播。程式碼實現如下:

//  設定觸控事件,當滑動或者觸控時停止自動輪播
    private void setTouchListener() {
        mViewPager.setOnTouchListener(new OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                int action = event.getAction();
                switch (action) {
                    case MotionEvent.ACTION_DOWN:
                    case MotionEvent.ACTION_MOVE:
                        isLoop = true;
                        stopLoop();
                        break;
                    case MotionEvent.ACTION_UP:
                    case MotionEvent.ACTION_CANCEL:
                        isLoop = false;
                        startLoop();
                    default:
                        break;
                }
                return false;
            }
        });
    }

    private void startLoop() {
        if (!isLoop && mViewPager != null) {
            mHandler.postDelayed(mRunnable, interval);// 每interval秒執行一次runnable.
            isLoop = true;
        }
    }

    public void stopLoop() {
        if (isLoop && mViewPager != null) {
            mHandler.removeCallbacks(mRunnable);
            isLoop = false;
        }
    }

至此,CircleViewPager的核心功能已經實現。但是頁面的點選事件還未進行處理。接下來將在第五節中實現CircleViewPager的頁面點選事件。 
五、CircleViewPager頁面點選事件。 
1.頁面的點選事件是通過點選ImageView觸發的,因此我們可以首先考慮給頁面的ImageView設定點選事件的監聽。在哪裡設定點選事件?無疑在Adapter中設定是比較簡單的,因此我們在CircleViewPager的Adapter中對ImageView新增點選事件監聽。如下:

  @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        View view = getView(position, container);
        container.addView(view);
        return view;
    }

    //  根據圖片URL建立對應的ImageView並新增到集合
    private View getView(final int position, ViewGroup container) {
        ViewHolder holder = holderCreator.createViewHolder();
        if (holder == null) {
            throw new RuntimeException("can not return a null holder");
        }
        View view = holder.createView(container.getContext());
        if (list != null && list.size() > 0) {
            holder.onBind(container.getContext(), position, list.get(position));
        }
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewPager.imageClick(position - 1);
            }
        });
        return view;
    }

我們事先在CircleViewPager中定義imageClick()方法,在例項化Adapter的時候將CircleViewPager自身傳遞進來,然後就可以在Adapter呼叫imageClick()方法。即圖片被點選的時候會觸發到CircleViewPager中的imageClick()方法。 
2.模仿Android中View的監聽事件來為CircleViewPager設定點選頁面的事件監聽。在CircleViewPager中定義OnPageClickListener介面,並在介面中定義pageClickListener(int position)的抽象方法。然後在CircleViewPager中定義OnPageClickListener的成員變數mOnPageClickListener,併為其設定set()方法。然後在imageClick()方法中呼叫mOnPageClickListener.pageClickListener(int position)。程式碼實現如下:

    private OnPageClickListener mOnPageClickListener;

        public void setOnPageClickListener(OnPageClickListener onPageClickListener) {
        this.mOnPageClickListener = onPageClickListener;
    }

    //  adapter中圖片點選的回掉方法
    public void imageClick(int position) {
        mOnPageClickListener.pageClickListener(position);
    }

     //  監聽頁面點選的介面
     public interface OnPageClickListener {
        void pageClickListener(int position);
    }

3.MainActivity中設定頁面點選的監聽。通過CicleViewPager.setOnPageClickListener實現對頁面點選的監聽。

mViewpager.setOnPageClickListener(new CircleViewPager.OnPageClickListener() {
            @Override
            public void pageClickListener(int position) {
                Toast.makeText(MainActivity.this, "點選了第"+position+"個美眉 \nURL:"+mViewpager.getUrlList().get(position), Toast.LENGTH_SHORT).show();
            }
        });