1. 程式人生 > >Android之ViewPager實現圖片無限迴圈輪播

Android之ViewPager實現圖片無限迴圈輪播

很久沒有寫部落格了,之前花時間寫了一個Viewpager實現的無限圖片輪播,個人感覺還是很好用的QAQ,原始碼和思路都還算清晰

實現的效果圖如下:


這裡要補充一下,在這個專案中我把圖片輪播寫進了一個Viewholder裡內嵌在了Recyclelistview裡。但本文只介紹圖片輪播部分的實現:

整體思路:使用handler的延時傳送方法(sendEmptyMessageDelayed)實現在adapter中控制Viewpager圖片輪播

具體實現分為三個部分:

首先是輪播圖片的佈局檔案main_home_picturecarousel.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="130dp"
    android:orientation="vertical"
     >

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="130dp">

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

        </android.support.v4.view.ViewPager>

        <LinearLayout
            android:layout_width="98dp"
            android:layout_height="7dp"
            android:orientation="horizontal"
            android:layout_marginBottom="10dp"
            android:layout_gravity="center_horizontal|bottom">
            <ImageView
                android:id="@+id/picturecarousel_img0"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian0"
                />
            <ImageView
                android:id="@+id/picturecarousel_img1"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img2"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img3"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img4"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img5"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
            <ImageView
                android:id="@+id/picturecarousel_img6"
                android:layout_marginLeft="7dp"
                android:layout_width="7dp"
                android:layout_height="7dp"
                android:src="@drawable/dian1"
                />
        </LinearLayout>
    </FrameLayout>
</LinearLayout>

Step1:ViewPager實現部分:

    Viewpager需要繫結一個PagerAdapter,所以首先需要自定義一個PagerAdapter:

public class ImageAdapter extends PagerAdapter {
    private Context context;
    //輪播需要的圖片
    public ArrayList<ImageView> imgs;

    public ImageAdapter(Context context, ArrayList<ImageView> imgs) {
        this.context = context;
        this.imgs = imgs;
    }

    /**
     * ViewPager的邊界
     * @return
     */
    @Override
    public int getCount() {
        //設定成最大,使無限迴圈
        return 10000;
    }

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

    /**
     * 由於我們在instantiateItem()方法中已經處理了remove的邏輯,
     * 因此這裡並不需要處理。實際上,實驗表明這裡如果加上了remove的呼叫,
     * 則會出現ViewPager的內容為空的情況。
     * @param container
     * @param position
     * @param object
     */
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        //警告:不要在這裡呼叫removeView
    }

    /**
     * @param container
     * @param position
     * @return
     * 對position進行求模操作
     * 因為當用戶向左滑時position可能出現負值,所以必須進行處理
     */
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        //對Viewpager頁號求模去除View列表中要顯示的項
        position %= imgs.size();
        if (position<0) {
            position = imgs.size() + position;
        }
        ImageView view = imgs.get(position);
        //如果View已經在之前新增到了一個父元件,則必須先remove,否則會丟擲IllegalStateException。
    
        ViewParent viewParent = view.getParent();
        if (viewParent!=null){
            ViewGroup parent = (ViewGroup)viewParent;
            parent.removeView(view);
        }
        container.addView(view);


        return view;
    }
}
這裡需要注意幾個方面:1、最大值設定:可以設定為Interger.MAXVALUE實現無限(不過我覺得1W已經很多了。。)

2、instantiateItem方法中的頁號顯示邏輯處理部分,這裡要考慮如果一開始位置設定為0往左滑會出現position為負值的情況

3、輪播之後View的移除

Step2Handler實現計時輪播部分:

這裡需要實現一個自定義的圖片輪播用到的handler:

public class ImageCarouseHandler extends Handler {
    /**
     * 請求更新顯示的View
     */
    public static final int MSG_UPDATE_IMAGE = 1;
    /**
     * 請求暫停輪播
     */
    public static final int MSG_KEEP_SILENT   = 2;
    /**
     * 請求恢復輪播。
     */
    public static final int MSG_BREAK_SILENT  = 3;
    /**
     * 記錄最新的頁號,當用戶手動滑動時需要記錄新頁號,否則會使輪播的頁面出錯。
     * 例如當前如果在第一頁,本來準備播放的是第二頁,而這時候使用者滑動到了末頁,
     * 則應該播放的是第一頁,如果繼續按照原來的第二頁播放,則邏輯上有問題。
     */
    public static final int MSG_PAGE_CHANGED  = 4;
    //輪播間隔時間
    public static final long MSG_DELAY = 3000;

    //這裡使用弱引用避免Handler洩露
    private WeakReference<HomeFrag> weakReference;
    private int currentItem = Integer.MAX_VALUE/2;

    public ImageCarouseHandler(WeakReference<HomeFrag> wk) {
         weakReference = wk;
    }

    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        HomeFrag homeFrag = weakReference.get();
        if (homeFrag == null) {
            //HomeFrag已經回收,無需繼續處理UI
            return;
        }
        //檢查訊息佇列並移除未傳送的訊息,這主要是避免在複雜環境下訊息出現重複等問題。
        /**
         * 這段會把第一次的自動輪播事件吃掉,所以可以加個條件,Position!=Max/2的時候才清除事件.因為第一次Position一定等於Max/2
         */

       if ((  homeFrag.handler.hasMessages(MSG_UPDATE_IMAGE))&&(currentItem!=Integer.MAX_VALUE/2)){
           homeFrag.handler.removeMessages(MSG_UPDATE_IMAGE);
       }
        switch (msg.what) {
            case MSG_UPDATE_IMAGE:
                currentItem++;
                homeFrag.ChanggeViewPagerCurrentItem(currentItem);
                //準備下次播放
                homeFrag.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
                break;
            case MSG_KEEP_SILENT:
                //只要不傳送訊息就暫停了
                break;
            case MSG_BREAK_SILENT:
                homeFrag.handler.sendEmptyMessageDelayed(MSG_UPDATE_IMAGE, MSG_DELAY);
                break;
            case MSG_PAGE_CHANGED:
                //記錄當前的頁號,避免播放的時候頁面顯示不正確。
                currentItem = msg.arg1;
                break;
            default:
                break;
        }
    }
}

這裡要注意設定不同狀態的幾種情況,以及要特別注意使用者進行拖拽操作時要考慮到輪播暫停和拖拽事件結束後的恢復輪播的問題。

最後就是具體在內容中的程式碼實現部分了:

            View view = LayoutInflater.from(context).inflate(R.layout.main_home_picturecarousel,null);
            vp = (ViewPager) view.findViewById(R.id.main_picturecarousel_vp);
            views = new ArrayList<>();
            LayoutInflater inflater = LayoutInflater.from(context);
            for (int i = 0;i<7; i++) {
                ImageView imgv = (ImageView) inflater.inflate(R.layout.picturecarouse_item,null);
                imgv.setImageResource(imgres[i]);
                views.add(imgv);
                //初始化點點
                pointimgs[i] = (ImageView) view.findViewById(pointimgsres[i]);
            }
            vp.setAdapter(new ImageAdapter(context,views));
    vp.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                showpoint(position);
                handler.sendMessage(Message.obtain(handler, ImageCarouseHandler.MSG_PAGE_CHANGED, position, 0));

            }

            //覆寫該方法實現輪播效果
            @Override
            public void onPageScrollStateChanged(int state) {
                switch (state) {
                    case ViewPager.SCROLL_STATE_DRAGGING:
                        handler.sendEmptyMessage(ImageCarouseHandler.MSG_KEEP_SILENT);
                        break;
                    case ViewPager.SCROLL_STATE_IDLE:
                        handler.sendEmptyMessageDelayed(ImageCarouseHandler.MSG_UPDATE_IMAGE, ImageCarouseHandler.MSG_DELAY);
                        break;
                    default:
                        break;
                }
            }
        });
        vp.setCurrentItem(4998);
        //開始輪播效果
        handler.sendEmptyMessageDelayed(ImageCarouseHandler.MSG_UPDATE_IMAGE, ImageCarouseHandler.MSG_DELAY);
        //注意,設定Page 即快取頁面的個數,數過小時會出現fragment重複載入的問題

Step3:小圓點實現:

在佈局檔案中已經佈置過小圓點的位置了,所以這裡只要實現一個改變小圓點狀態的方法:

<span style="font-family:宋體;">    private void showpoint(int position) {
        //dian0-白色 dian1-灰色
        for (int i = 0;i<7;i++) {
            pointimgs[i].setImageResource(R.drawable.dian1);
            pointimgs[position%7].setImageResource(R.drawable.dian0);
        }
    }</span>
然後在viewpager的onPageSelected()方法中呼叫:
<span style="font-family:宋體;">            @Override
            public void onPageSelected(int position) {
                showpoint(position);
                handler.sendMessage(Message.obtain(handler, ImageCarouseHandler.MSG_PAGE_CHANGED, position, 0));

            }</span>

QAQ上次寫部落格已經過去很久了,下次也不知道是什麼時候。。。事情太多,生活太複雜╮(╯▽╰)╭

想買好多書QAQ。。。但是好窮。。╮(╯▽╰)╭