Android 使用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包或者依赖
  • 1
  • 2

代码结构:

代码结构

  • 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);

    }

}
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
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>

猜你喜欢

转载自www.cnblogs.com/zhujiabin/p/9046461.html