1. 程式人生 > >使用RecyclerView實現多行水平分頁的GridView效果和ViewPager效果

使用RecyclerView實現多行水平分頁的GridView效果和ViewPager效果

前些天看到有人在論壇上問這種效果怎麼實現,沒寫過也沒用過這個功能,網上查了一下,大多是使用ViewPager+GridView或者HorizontalScrollView+GridView實現,不過貌似有點複雜,太懶,沒仔細看。這兩天學習RecyclerView的使用(網上有很多文章,建議大家閱讀本部落格的時候先去了解一下),發現RecyclerView可以實現GridView 的橫向滾動效果,不過沒有分頁,因此決定自己寫一個。

Demo已上傳到GitHubCSDN下載頻道,是AS專案,使用AS的同學可以直接下載或者clone,博文最後也有貼出完整程式碼,使用Eclipse的同學可以自己新建專案並Copy程式碼。

效果圖:

(由於這裡每個Item都很相像,所以效果看起來不是很好,請見諒)
圖1:多行水平分頁的GridView效果

圖2:ViewPager效果
(刪除的操作是在長按事件中寫的)
圖1是帶頁碼指示器的多行橫向分頁的GridView效果,拖動距離不足時,還可以滾動回原來的位置(類似於ViewPager拖動距離不足的效果);
圖2是和ViewPager一模一樣的效果,實現此效果只要設定行數和列數都為1即可。

使用以下程式碼,需要匯入RecyclerView的jar包或者依賴

程式碼結構:

程式碼結構

  • AutoGridLayoutManager繼承自GridLayoutManager並重寫了onMeasure方法,目的是使RecyclerView的高度自適應內容高度。
  • DimensionConvert是一個用來轉換px和pd的工具類。
  • MainActivity是一個使用示例。
  • PageIndicatorView繼承自LinearLayout,存放一些小圓點作為頁碼指示器。
  • PageRecyclerView繼承自RecyclerView,用來完成分頁等功能。

先簡單講一下實現步驟,之後貼完整的程式碼

第一步: 實現橫向滾動的GridView效果

    這個很簡單,只要給RecyclerView設定橫向的GridLayoutManager就可以了。但是使用過程中發現,RecyclerView並不會自適應內容的高度,因此重寫了GridLayoutManager的onMeasure

方法(MyGridLayoutManager.java);

@Override
public void onMeasure(RecyclerView.Recycler recycler, RecyclerView.State state, int widthSpec, int heightSpec) {
    View view = recycler.getViewForPosition(0);
    if (view != null) {
        measureChild(view, widthSpec, heightSpec);
        int measuredWidth = View.MeasureSpec.getSize(widthSpec);
        int measuredHeight = view.getMeasuredHeight() * getSpanCount();
        setMeasuredDimension(measuredWidth, measuredHeight);
    }
}

第二步:實現自定義行數和列數功能

    實現此功能需要重寫RecyclerView(MyRecyclerView.java),並新增兩個成員變數spanRowspanColumn和一個設定行數列數的方法setPageSize(int spanRow, int spanColumn)
    之後,在Adapter中生成Item的時候就可以根據設定好的PageRecyclerView的寬度和列數計算單個Item的寬度,以達到一頁正好顯示固定列數的目的:

@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (itemWidth <= 0) {
        // 計算Item的寬度
        itemWidth = (parent.getWidth() - pageMargin * 2) / spanColumn;
    }

    RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType);

    holder.itemView.measure(0, 0);
    holder.itemView.getLayoutParams().width = itemWidth;
    holder.itemView.getLayoutParams().height = holder.itemView.getMeasuredHeight();

    return holder;
}

    可以看到上面程式碼中有一個mCallBack變數,這是一個介面的實現類的例項,我們需要建立Adapter例項的時候傳入一個此介面的子類例項。

public interface CallBack {

    /**
     * 建立VieHolder
     *
     * @param parent
     * @param viewType
     */
    RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

    /**
     * 繫結資料到ViewHolder
     *
     * @param holder
     * @param position
     */
    void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

}

此介面共有兩個方法,這兩個方法和Adapter中需要重寫的兩個方法一樣,用法也一樣,分別用來建立ViewHolder例項和給ViewHolder中的控制元件繫結資料。

第三步:開始分頁滾動

1> 分頁:

    完成第二步之後,佈局就調整好了,之後我們實現分頁滾動的功能。要分頁就肯定需要總頁數(totalPage)和當前頁碼(currentPage),我們需要在設定Adapter介面卡之後根據Item的總數和每頁的Item數計算總頁數:

@Override
public void setAdapter(Adapter adapter) {
    super.setAdapter(adapter);
    // 計算總頁數
    totalPage = ((int) Math.ceil(adapter.getItemCount() / (double) (spanRow * spanColumn)));
    mIndicatorView.initIndicator(totalPage);
}

    然後就可以重寫RecyclerView的onTouchEvent方法實現分頁,根據ACTION_DOWNACTION_UP時候的座標計算滑動方向,在ACTION_UP的時候根據滑動的方向使用smoothScrollBy方法向左或向右滑動一個MyRecyclerView的寬度就可以了。
    不過這種切換頁面的方式很生硬,我們要實現的ViewPager的滑動效果:要滑動超過一定的距離才能切換頁碼,否則滾回原來的位置。實現此功能需要一個常量,不過為了適應各種寬度的MyRecyclerView,這裡根據MyRecyclerView的寬度動態設定最小滾動距離:

private int shortestDistance; // 超過此距離的滑動才有效

@Override
protected void onMeasure(int widthSpec, int heightSpec) {
    super.onMeasure(widthSpec, heightSpec);
    shortestDistance = getMeasuredWidth() / 3;
}

還需要其他的幾個變數:

private float downX = 0; // 手指按下的X軸座標
private float slideDistance = 0; // 滑動的距離
private float scrollX = 0; // X軸當前的位置

scrollX為當前滾動的位置,重寫onScrolled計算滾動到的位置:

@Override
public void onScrolled(int dx, int dy) {
    scrollX += dx;
    super.onScrolled(dx, dy);
}

之後就可以編寫完整的onTouchEvent方法:

@Override
public boolean onTouchEvent(MotionEvent event) {

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            downX = event.getX();
            break;
        case MotionEvent.ACTION_UP:
            slideDistance = event.getX() - downX;
            if (Math.abs(slideDistance) > shortestDistance) {
                // 滑動距離足夠,執行翻頁
                if (slideDistance > 0) {
                    // 上一頁
                    currentPage = currentPage == 1 ? 1 : currentPage - 1;
                } else {
                    // 下一頁
                    currentPage = currentPage == totalPage ? totalPage : currentPage + 1;
                }
            }
            // 執行滾動
            smoothScrollBy((int) ((currentPage - 1) * getWidth() - scrollX), 0);
            return true;
        default:
            break;
    }

    return super.onTouchEvent(event);
}

2> 頁間距

為了分頁更加清晰,還需要給頁與頁新增間距:
    首先新增一個成員變數,和set方法

private int pageMargin = 0; // 頁間距
/**
 * 設定頁間距
 *
 * @param pageMargin 間距(px)
 */
public void setPageMargin(int pageMargin) {
    this.pageMargin = pageMargin;
}

然後重寫Adapter的onBindViewHolder方法調整頁間距:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    if (spanColumn == 1) {
        // 每個Item距離左右兩側各pageMargin
        holder.itemView.getLayoutParams().width = itemWidth + pageMargin * 2;
        holder.itemView.setPadding(pageMargin, 0, pageMargin, 0);
    } else {
        int m = position % (spanRow * spanColumn);
        if (m < spanRow) {
            // 每頁左側的Item距離左邊pageMargin
            holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
            holder.itemView.setPadding(pageMargin, 0, 0, 0);
        } else if (m >= spanRow * spanColumn - spanRow) {
            // 每頁右側的Item距離右邊pageMargin
            holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
            holder.itemView.setPadding(0, 0, pageMargin, 0);
        } else {
            // 中間的正常顯示
            holder.itemView.getLayoutParams().width = itemWidth;
            holder.itemView.setPadding(0, 0, 0, 0);
        }
    }

}

3> 佔位Item

為了最後不足一頁時也能完整顯示,還需要在最後不足一頁時,生成佔位的View,因此修改Adapter的onBindViewHolder方法和getItemCount方法:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {

    ...

    if (position < dataList.size()) {
        holder.itemView.setAlpha(1);
        mCallBack.onBindViewHolder(holder, position);
    } else {
        holder.itemView.setAlpha(0);
    }
}

@Override
public int getItemCount() {
    int m = dataList.size() % (spanRow * spanColumn);
    if (m == 0) {
        return dataList.size();
    } else {
       return dataList.size() + (spanRow * spanColumn - m);
   }
}

至此,分頁功能就完成了,為了功能更豐滿,還需要新增一個分頁指示器(就是效果圖中的小圓點),這個功能還是很簡單的,新建一個類繼承LinearLayout並根據總頁數生成一些小圓點的View,然後提供一個修改當前頁碼的方法就OK啦。

第四步:刪除Item

最後還有一個刪除Item的功能,實現方式還是使用系統的Adapter的notifyItemRemoved(int position);方法,由於前面分頁時給部分Item設定了padding,所以為了佈局不會錯亂,還需要更新其他改變的Item:

// 刪除Item
notifyItemRemoved(position);
// 更新介面上發生改變的Item
notifyItemRangeChanged(position, currentPage * spanRow * spanColumn);

然後還要更新頁碼指示器,這裡就不貼程式碼了,直接看下面的類就可以了。
使用的時候只要把指示器和MyRecyclerView按照自己的需求佈局,並在切換頁面的時候更新指示器就完成了。

改:
1. 上面分頁滑動是在onTouchEvent()方法中實現的,但是後來發現,這種實現方式會導致給Item新增onClickListeneronLongClickListeneronTouchListener的時候會產生事件衝突,因此修改為在onScrollStateChanged()方法中實現,程式碼如下:

/*
     * 0: 停止滾動且手指移開; 1: 開始滾動; 2: 手指做了拋的動作(手指離開螢幕前,用力滑了一下)
     */
private int scrollState = 0; // 滾動狀態
@Override
public void onScrollStateChanged(int state) {
    switch (state) {
        case 2:
            scrollState = 2;
            break;
        case 1:
            scrollState = 1;
            break;
        case 0:
            if (slideDistance == 0) {
                break;
            }
            scrollState = 0;
            if (slideDistance < 0) { // 上頁
                currentPage = (int) Math.ceil(scrollX / getWidth());
                if (currentPage * getWidth() - scrollX < shortestDistance) {
                    currentPage += 1;
                }
            } else { // 下頁
                currentPage = (int) Math.ceil(scrollX / getWidth()) + 1;
                if (currentPage <= totalPage) {
                    if (scrollX - (currentPage - 2) * getWidth() < shortestDistance) {
                        // 如果這一頁滑出距離不足,則定位到前一頁
                        currentPage -= 1;
                    }
                } else {
                    currentPage = totalPage;
                }
            }
            // 執行自動滾動
            smoothScrollBy((int) ((currentPage - 1) * getWidth() - scrollX), 0);
            // 修改指示器選中項
            mIndicatorView.setSelectedPage(currentPage - 1);
            slideDistance = 0;
            break;
    }
    super.onScrollStateChanged(state);
}

@Override
public void onScrolled(int dx, int dy) {
    scrollX += dx;
    if (scrollState == 1) {
        slideDistance += dx;
    }

    super.onScrolled(dx, dy);
}
  1. RecyclerView的GridLayoutManager是從上到下從左到右排列的,而我們分頁時大多需要的是從左到右從上到下排列,因此增加一個方法調整位置(此方法只適用於3*3排列的,還沒有找到通用的方法,如果那位同學有方法,麻煩分享一下,先謝過)
private void countRealPosition(int position) {
    // 為了使Item從左到右從上到下排列,需要position的值
    int m = position % (spanRow * spanColumn);
    switch (m) {
        case 1:
        case 5:
            realPosition = position + 2;
            break;
        case 3:
        case 7:
            realPosition = position - 2;
            break;
        case 2:
            realPosition = position + 4;
            break;
        case 6:
            realPosition = position - 4;
            break;
        case 0:
        case 4:
        case 8:
            realPosition = position;
            break;
    }
}

<<<<<<<<<<<<<<<<<<<<<<使用方法參考MainActivity.java>>>>>>>>>>>>>>>>>>>>

上面講的不夠詳細,具體見程式碼>>>>>>>>>>>>>>

完整程式碼:

AutoGridLayoutManager.java

    使用這個類替代GridLayoutManager是為了使RecyclerView及其子類能夠自適應內容的高度。

import android.content.Context;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;

/**
 * Created by shichaohui on 2015/7/9 0009.
 * <p>
 * 重寫GridLayoutManager,在{@link RecyclerView#setLayoutManager(RecyclerView.LayoutManager)}使用
 * 此類替換{@link GridLayoutManager},使{@link RecyclerView}能夠自使用內容的高度
 * </p>
 */
public class AutoGridLayoutManager extends GridLayoutManager {

    private int measuredWidth = 0;
    private int measuredHeight = 0;

    public AutoGridLayoutManager(Context context, AttributeSet attrs,
                                 int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public AutoGridLayoutManager(Context context, int spanCount) {
        super(context, spanCount);
    }

    public AutoGridLayoutManager(Context context, int spanCount,
                                 int orientation, boolean reverseLayout) {
        super(context, spanCount, orientation, reverseLayout);
    }

    @Override
    public void onMeasure(RecyclerView.Recycler recycler,
                          RecyclerView.State state, int widthSpec, int heightSpec) {
        if (measuredHeight <= 0) {
            View view = recycler.getViewForPosition(0);
            if (view != null) {
                measureChild(view, widthSpec, heightSpec);
                measuredWidth = View.MeasureSpec.getSize(widthSpec);
                measuredHeight = view.getMeasuredHeight() * getSpanCount();
            }
        }
        setMeasuredDimension(measuredWidth, measuredHeight);
    }

}

PageRecyclerView.java

    重寫RecyclerView實現分頁

import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.PagerAdapter;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;

import java.util.List;
import java.util.Objects;

/**
 * Created by shichaohui on 2015/7/9 0009.
 * <p>
 * 橫向分頁的GridView效果
 * </p>
 * <p>
 * 預設為1行,每頁3列,如果要自定義行數和列數,請在呼叫{@link PageRecyclerView#setAdapter(Adapter)}方法前呼叫
 * {@link PageRecyclerView#setPageSize(int, int)}方法自定義行數
 * </p>
 */
public class PageRecyclerView extends RecyclerView {

    private Context mContext = null;

    private PageAdapter myAdapter = null;

    private int shortestDistance; // 超過此距離的滑動才有效
    private float downX = 0; // 手指按下的X軸座標
    private float slideDistance = 0; // 滑動的距離
    private float scrollX = 0; // X軸當前的位置

    private int spanRow = 1; // 行數
    private int spanColumn = 3; // 每頁列數
    private int totalPage = 0; // 總頁數
    private int currentPage = 1; // 當前頁

    private int pageMargin = 0; // 頁間距

    private PageIndicatorView mIndicatorView = null; // 指示器佈局

    public PageRecyclerView(Context context) {
        this(context, null);
    }

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

    public PageRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        defaultInit(context);
    }

    // 預設初始化
    private void defaultInit(Context context) {
        this.mContext = context;
        setLayoutManager(new AutoGridLayoutManager(
                mContext, spanRow, AutoGridLayoutManager.HORIZONTAL, false));
        setOverScrollMode(OVER_SCROLL_NEVER);
    }

    /**
     * 設定行數和每頁列數
     *
     * @param spanRow    行數,<=0表示使用預設的行數
     * @param spanColumn 每頁列數,<=0表示使用預設每頁列數
     */
    public void setPageSize(int spanRow, int spanColumn) {
        this.spanRow = spanRow <= 0 ? this.spanRow : spanRow;
        this.spanColumn = spanColumn <= 0 ? this.spanColumn : spanColumn;
        setLayoutManager(new AutoGridLayoutManager(
                mContext, this.spanRow, AutoGridLayoutManager.HORIZONTAL, false));
    }

    /**
     * 設定頁間距
     *
     * @param pageMargin 間距(px)
     */
    public void setPageMargin(int pageMargin) {
        this.pageMargin = pageMargin;
    }

    /**
     * 設定指示器
     *
     * @param indicatorView 指示器佈局
     */
    public void setIndicator(PageIndicatorView indicatorView) {
        this.mIndicatorView = indicatorView;
    }

    @Override
    protected void onMeasure(int widthSpec, int heightSpec) {
        super.onMeasure(widthSpec, heightSpec);
        shortestDistance = getMeasuredWidth() / 3;
    }

    @Override
    public void setAdapter(Adapter adapter) {
        super.setAdapter(adapter);
        this.myAdapter = (PageAdapter) adapter;
        update();
    }

    // 更新頁碼指示器和相關資料
    private void update() {
        // 計算總頁數
        int temp = ((int) Math.ceil(myAdapter.dataList.size() / (double) (spanRow * spanColumn)));
        if (temp != totalPage) {
            mIndicatorView.initIndicator(temp);
            // 頁碼減少且當前頁為最後一頁
            if (temp < totalPage && currentPage == totalPage) {
                currentPage = temp;
                // 執行滾動
                smoothScrollBy(-getWidth(), 0);
            }
            mIndicatorView.setSelectedPage(currentPage - 1);
            totalPage = temp;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                if (currentPage == totalPage && downX - event.getX() > 0) {
                    return true;
                }
                break;
            case MotionEvent.ACTION_DOWN:
                downX = event.getX();
                break;
            case MotionEvent.ACTION_UP:
                slideDistance = event.getX() - downX;
                if (Math.abs(slideDistance) > shortestDistance) {
                    // 滑動距離足夠,執行翻頁
                    if (slideDistance > 0) {
                        // 上一頁
                        currentPage = currentPage == 1 ? 1 : currentPage - 1;
                    } else {
                        // 下一頁
                        currentPage = currentPage == totalPage ? totalPage : currentPage + 1;
                    }
                    // 修改指示器選中項
                    mIndicatorView.setSelectedPage(currentPage - 1);
                }
                // 執行滾動
                smoothScrollBy((int) ((currentPage - 1) * getWidth() - scrollX), 0);
                return true;
            default:
                break;
        }

        return super.onTouchEvent(event);
    }

    @Override
    public void onScrolled(int dx, int dy) {
        scrollX += dx;
        super.onScrolled(dx, dy);
    }

    /**
     * 資料介面卡
     */
    public class PageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

        private List<?> dataList = null;
        private CallBack mCallBack = null;
        private int itemWidth = 0;
        private int itemCount = 0;

        /**
         * 例項化介面卡
         *
         * @param data
         * @param callBack
         */
        public PageAdapter(List<?> data, CallBack callBack) {
            this.dataList = data;
            this.mCallBack = callBack;
            itemCount = dataList.size() + spanRow * spanColumn;
        }

        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (itemWidth <= 0) {
                // 計算Item的寬度
                itemWidth = (parent.getWidth() - pageMargin * 2) / spanColumn;
            }

            RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType);

            holder.itemView.measure(0, 0);
            holder.itemView.getLayoutParams().width = itemWidth;
            holder.itemView.getLayoutParams().height = holder.itemView.getMeasuredHeight();

            return holder;
        }

        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
            if (spanColumn == 1) {
                // 每個Item距離左右兩側各pageMargin
                holder.itemView.getLayoutParams().width = itemWidth + pageMargin * 2;
                holder.itemView.setPadding(pageMargin, 0, pageMargin, 0);
            } else {
                int m = position % (spanRow * spanColumn);
                if (m < spanRow) {
                    // 每頁左側的Item距離左邊pageMargin
                    holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
                    holder.itemView.setPadding(pageMargin, 0, 0, 0);
                } else if (m >= spanRow * spanColumn - spanRow) {
                    // 每頁右側的Item距離右邊pageMargin
                    holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
                    holder.itemView.setPadding(0, 0, pageMargin, 0);
                } else {
                    // 中間的正常顯示
                    holder.itemView.getLayoutParams().width = itemWidth;
                    holder.itemView.setPadding(0, 0, 0, 0);
                }
            }

            if (position < dataList.size()) {
                holder.itemView.setVisibility(View.VISIBLE);
                mCallBack.onBindViewHolder(holder, position);
            } else {
                holder.itemView.setVisibility(View.INVISIBLE);
            }

        }

        @Override
        public int getItemCount() {
            return itemCount;
        }

        /**
         * 刪除Item
         * @param position 位置
         */
        public void remove(int position) {
            if (position < dataList.size()) {
                // 刪除資料
                dataList.remove(position);
                itemCount--;
                // 刪除Item
                notifyItemRemoved(position);
                // 更新介面上發生改變的Item
                notifyItemRangeChanged(position, currentPage * spanRow * spanColumn);
                // 更新頁碼指示器
                update();
            }
        }

    }

    public interface CallBack {

        /**
         * 建立VieHolder
         *
         * @param parent
         * @param viewType
         */
        RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);

        /**
         * 繫結資料到ViewHolder
         *
         * @param holder
         * @param position
         */
        void onBindViewHolder(RecyclerView.ViewHolder holder, int position);

    }

}

PageIndicatorView.java

    頁碼指示器 ,此類可以作為一個工具類,在ViewPager做的輪播圖上也可以使用

import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by shichaohui on 2015/7/10 0010.
 * <p/>
 * 頁碼指示器類,獲得此類例項後,可通過{@link PageIndicatorView#initIndicator(int)}方法初始化指示器
 * </P>
 */
public class PageIndicatorView extends LinearLayout {

    private Context mContext = null;
    private int dotSize = 15; // 指示器的大小(dp)
    private int margins = 4; // 指示器間距(dp)
    private List<View> indicatorViews = null; // 存放指示器

    public PageIndicatorView(Context context) {
        this(context, null);
    }

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

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

    private void init(Context context) {
        this.mContext = context;

        setGravity(Gravity.CENTER);
        setOrientation(HORIZONTAL);

        dotSize = DimensionConvert.dip2px(context, dotSize);
        margins = DimensionConvert.dip2px(context, margins);
    }

    /**
     * 初始化指示器,預設選中第一頁
     *
     * @param count 指示器數量,即頁數
     */
    public void initIndicator(int count) {

        if (indicatorViews == null) {
            indicatorViews = new ArrayList<>();
        } else {
            indicatorViews.clear();
            removeAllViews();
        }
        View view;
        LayoutParams params = new LayoutParams(dotSize, dotSize);
        params.setMargins(margins, margins, margins, margins);
        for (int i = 0; i < count; i++) {
            view = new View(mContext);
            view.setBackgroundResource(android.R.drawable.presence_invisible);
            addView(view, params);
            indicatorViews.add(view);
        }
        if (indicatorViews.size() > 0) {
            indicatorViews.get(0).setBackgroundResource(android.R.drawable.presence_online);
        }
    }

    /**
     * 設定選中頁
     *
     * @param selected 頁下標,從0開始
     */
    public void setSelectedPage(int selected) {
        for (int i = 0; i < indicatorViews.size(); i++) {
            if (i == selected) {
                indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_online);
            } else {
                indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_invisible);
            }
        }
    }

}

DimensionConvert.java

    用來轉換dip和px的工具類

import android.content.Context;

/**
 * Created by shichaohui on 2015/7/10 0010.
 */
public class DimensionConvert {

    /**
     * 根據手機的解析度從 dp 的單位 轉成為 px(畫素)
     *
     * @param context
     * @param dpValue 要轉換的dp值
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根據手機的解析度從 px(畫素) 的單位 轉成為 dp
     *
     * @param context
     * @param pxValue 要轉換的px值
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }
}

MainActivity.java

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private PageRecyclerView mRecyclerView = null;
    private List<String> dataList = null;
    private PageRecyclerView.PageAdapter myAdapter = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        initData();

        mRecyclerView = (PageRecyclerView) findViewById(R.id.cusom_swipe_view);
        // 設定指示器
        mRecyclerView.setIndicator((PageIndicatorView) findViewById(R.id.indicator));
        // 設定行數和列數
        mRecyclerView.setPageSize(3, 3);
        // 設定頁間距
        mRecyclerView.setPageMargin(30);
        // 設定資料
        mRecyclerView.setAdapter(myAdapter = mRecyclerView.new PageAdapter(dataList, new PageRecyclerView.CallBack() {
            @Override
            public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
                View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
                return new MyHolder(view);
            }

            @Override
            public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
                ((MyHolder)holder).tv.setText(dataList.get(position));
            }
        }));

    }

    private void initData() {
        dataList = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            dataList.add(String.valueOf(i));
        }
    }

    public class MyHolder extends RecyclerView.ViewHolder {

        public TextView tv = null;

        public MyHolder(View itemView) {
            super(itemView);
            tv = (TextView) itemView.findViewById(R.id.text);
            tv.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(MainActivity.this, getAdapterPosition() + "", Toast.LENGTH_SHORT).show();
                }
            });
            tv.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    myAdapter.remove(getAdapterPosition());
                    return true;
                }
            });
        }
    }

}

最後是兩個佈局檔案:

activity_main.xml

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

        <com.example.sch.myapplication.PageRecyclerView
            android:id="@+id/cusom_swipe_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.example.sch.myapplication.PageIndicatorView
            android:id="@+id/indicator"
            android:layout_width="match_parent"
            android:layout_marginBottom="20dp"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"/>

</LinearLayout>

item.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="match_parent">

    <TextView
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_margin="10dp"
        android:background="#770000ff"
        android:gravity="center" />

</LinearLayout>

    END

    歡迎評論吐槽……