自己動手寫一個簡單圖片輪播的控件

簡單圖片輪播的控件的實現

本人菜鳥。
之前想找個輪播的控件,網上找了幾篇文章,覺得搞的很複雜,思路也不是很清晰。所以乾脆就自己寫了。總的來說,是比較簡單的。


思路:

圖片輪播,其實就是滑動圖片由手動改爲自動而已。針對滑動圖片,我立刻想到兩個方案:

  1. horizontalScrollView
    水平scrollView,本來就是滑動的,可以在它的childView中添加要顯示的視圖。
  2. viewPager
    viewPager是一個很好用的視圖容器,相信很多人已經用過了,添加視圖、管理pager狀態都很方便。

雖然兩個都能用,但是使用horizontalScrollView有幾個麻煩的地方:
- 要在horizontalScrollView裏顯示視圖,需要
container.addView(childView);如果只add不remove,那麼就會造成oom。所以需要我們自己去管理。
- 子視圖的切換和響應需要自己捕獲和定義。

使用viewPager就沒有上面的問題。viewPager默認維護3個childView。後面會給出驗證。


廢話不多說了,直接上代碼分析吧

package android.com.loopview.view;

import android.content.Context;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.MotionEvent;

import java.lang.ref.WeakReference;

/**
 * Created by yueshaojun on 16/7/one.
 */
public class LoopViewPager extends ViewPager {
    private static int LEFTTORIGHT = 0;
    private static int RIGHTTOLEFT = 1;
    private long mtime;

    Run r =new Run(this);
    public LoopViewPager(Context context) {
        super(context);
    }

    public LoopViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    /**
     * 讓子view動起來的操作
     * 默認滾動間隔時間爲1s
     * 不設置默認不滾動
     * @param time 滾動間隔時間
     * */
    public void loop(long time){
        if(null == getAdapter()){
            return;
        }
        mtime = time;
        int item = getCurrentItem();
        if(time <= 0){
            time = 1000;
        }
        r.item = item;
        r.time = time;
        postDelayed(r, time);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        float oX = 0;
        float nX = 0;
        super.onTouchEvent(ev);
        switch (ev.getAction()){
            case MotionEvent.ACTION_MOVE :
                break;
            case MotionEvent.ACTION_DOWN:
                oX= ev.getX();//捕獲down事件的X座標
                break;
            case MotionEvent.ACTION_UP:
                nX = ev.getX();//捕獲up事件的X座標
                break;
        }
        //判斷手勢方向,以相差50爲準
        if(nX == oX){
            return false;
        }
        if(nX-oX>50){
            handleGesture(LEFTTORIGHT);
        }else {
            handleGesture(RIGHTTOLEFT);
        }
        return true;
    }

    private void handleGesture(int gesture){
        if(gesture == LEFTTORIGHT){
            //TODO 處理從左到右的手勢
        }
        if(gesture == RIGHTTOLEFT){
            //TODO 處理從右到左的手勢
        }
        if (!looping){
            return;
        }
        //移除callBack
        removeCallbacks(r);
        r.item = getCurrentItem();
        r.time = mtime;
        postDelayed(r, mtime);
    }

    static class  Run implements Runnable{
        WeakReference<LoopViewPager> wloopView;
        int item;
        long time;
        Run(LoopViewPager lp){
            wloopView = new WeakReference<LoopViewPager>(lp);
        }
        @Override
        public void run() {
            LoopViewPager loopView = wloopView.get();
            if(item<loopView.getAdapter().getCount()-1) {
                ++item;
            }else{
                item = 0;
            }
            loopView.setCurrentItem(item);
            loopView.postDelayed(this, time);
        }
    }
}

代碼並不複雜(我之前網上看到的都是好大一坨),思路也很清晰:
調用loop方法開啓輪播後,postDelay一下,在runnable中設置當前子視圖。重寫onTouchEvent捕獲手勢,如果有左右滑動,就移除當前runnable,重新postDelay。(其實左右滑動的手勢操作,viewpager已經幫我們做好了,在ViewPager.OnPageChangeListener中響應處理)


佈局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".activity.MainActivity">
    <android.com.loopview.view.LoopViewPager
        android:id="@+id/loop_view"
        android:layout_width="match_parent"
        android:layout_height="500dp">
    </android.com.loopview.view.LoopViewPager>
    <TextView
        android:id="@+id/indicator"
        android:layout_below="@id/loop_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textColor="#000000"
        android:text="0" />
</RelativeLayout>

適配器

package android.com.loopview.view;

import android.support.v4.view.PagerAdapter;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

/**
 * Created by yueshaojun on 16/six/29.
 */
public class MyAdapter extends PagerAdapter {
    private Object[] objs;
    public MyAdapter(Object[] inflateObjs){
        objs = inflateObjs;
    }
    @Override
    public int getCount() {
        return objs.length;
    }

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

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Log.i("viewpager","instantiateItem:"+position);
        container.addView((View) objs[position]);
        return objs[position];
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Log.i("viewpager","destroyItem:"+position);
        container.removeView((View) objs[position]);
    }

}

activity

package android.com.loopview.activity;

import android.app.Activity;
import android.com.loopview.R;
import android.com.loopview.view.LoopViewPager;
import android.com.loopview.view.MyAdapter;
import android.support.v4.view.ViewPager;
import android.os.Bundle;
import android.util.Log;
import android.widget.ImageView;
import android.widget.TextView;

public class MainActivity extends Activity implements ViewPager.OnPageChangeListener {
    private LoopViewPager loopViewPager;
    private TextView indicator;
    private ImageView[] imageViews;
    private MyAdapter myAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initData();
        initView();
        addListener();
    }

    private void initData() {
         int[] imgIds = new int []{
            R.drawable.one,
            R.drawable.two,
             R.drawable.three,
             R.drawable.four,
             R.drawable.five,
             R.drawable.six,
        };
        imageViews = new ImageView[imgIds.length];
        for (int i = 0;i<imgIds.length;i++){
            ImageView imageView = new ImageView(this);
            imageView.setImageResource(imgIds[i]);
            imageViews[i] = imageView;
        }
        myAdapter = new MyAdapter(imageViews);
    }

    private void initView() {
        loopViewPager = (LoopViewPager) findViewById(R.id.loop_view);
        indicator = (TextView) findViewById(R.id.indicator);
    }

    private void addListener() {
        loopViewPager.setAdapter(myAdapter);
        loopViewPager.addOnPageChangeListener(this);
        // 開啓輪播並設置輪播時間間隔
        loopViewPager.loop(3000);

    }

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

    }

    @Override
    public void onPageSelected(int position) {
        Log.i("viewpager", "onPageSelected:" + position);
        indicator.setText(""+position);
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

}

其實和普通viewPager的使用幾本沒有區別,只是需要用loop()方法開啓輪播,不開啓就是普通viewPager。
另外在添加子視圖instantiateItem、移除子視圖destroyItem和切換當前視圖onPageSelected裏打了日誌。日誌結果如下:

07-10 17:35:09.777 18715-18715/android.com.loopview I/viewpager: instantiateItem:0
07-10 17:35:09.778 18715-18715/android.com.loopview I/viewpager: instantiateItem:1
07-10 17:35:22.820 18715-18715/android.com.loopview I/viewpager: onPageSelected:1
07-10 17:35:23.345 18715-18715/android.com.loopview I/viewpager: instantiateItem:2
07-10 17:35:23.627 18715-18715/android.com.loopview I/viewpager: onPageSelected:2
07-10 17:35:24.111 18715-18715/android.com.loopview I/viewpager: destroyItem:0
07-10 17:35:24.113 18715-18715/android.com.loopview I/viewpager: instantiateItem:3
07-10 17:35:24.343 18715-18715/android.com.loopview I/viewpager: onPageSelected:3
07-10 17:35:24.844 18715-18715/android.com.loopview I/viewpager: destroyItem:1
07-10 17:35:24.844 18715-18715/android.com.loopview I/viewpager: instantiateItem:4

開始的時候添加0,1頁,換到1添加2頁,換到2頁銷燬0添加3。以此類推。說明viewpager只保存當前頁和它左右兩頁(如果有)的視圖。


改進:

之前用的是postDelay,可以用timer計時器去控制輪播。雖然timertask其實也是實現了runnable接口,但是由timer去管理線程完成調度顯然要好的多。
改進以後的代碼:

package android.com.loopview.view;

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;

import java.lang.ref.WeakReference;
import java.util.Timer;
import java.util.TimerTask;

/**
 * Created by yueshaojun on 16/7/one.
 */
public class LoopViewPager extends ViewPager {
    private Timer timer= new Timer();
    private MyHandler myHandler = new MyHandler(this);
    public LoopViewPager(Context context) {
        super(context);
    }

    public LoopViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    /**
     * 讓子view動起來的操作
     * 默認滾動間隔時間爲1s
     * 不設置默認不滾動
     * @param time 滾動間隔時間
     * */
    public void loop(long time){
        if(null == getAdapter()){
            return;
        }
        timer.schedule(new LooperTimerTask(this),1000,time);
    }

     class LooperTimerTask extends TimerTask{
         LoopViewPager loopViewPager;
         public LooperTimerTask(Object obj){
             loopViewPager = new WeakReference<>((LoopViewPager)obj).get();
         }
         @Override
         public void run() {
             int item = loopViewPager.getCurrentItem();
             if(item<loopViewPager.getAdapter().getCount()-1) {
                 ++item;
             }else{
                 item = 0;
             }
             Message message = new Message();
             Bundle bundle = new Bundle();
             bundle.putInt("item", item);
             message.setData(bundle);
             myHandler.sendMessage(message);
         }
     }
    class MyHandler extends Handler{
        LoopViewPager loopViewPager;
        public MyHandler(Object obj){
            loopViewPager = new WeakReference<>((LoopViewPager)obj).get();
        }
        @Override
        public void handleMessage(Message msg) {
            int item  = msg.getData().getInt("item");
            loopViewPager.setCurrentItem(item);
        }
    }
}

在timerTask中獲取當前viewpager的位置,然後通知Ui更新。

發佈了31 篇原創文章 · 獲贊 13 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章