前些天看到有人在论坛上问这种效果怎么实现,没写过也没用过这个功能,网上查了一下,大多是使用ViewPager+GridView或者HorizontalScrollView+GridView实现,不过貌似有点复杂,太懒,没仔细看。这两天学习RecyclerView的使用(网上有很多文章,建议大家阅读本博客的时候先去了解一下),发现RecyclerView可以实现GridView 的横向滚动效果,不过没有分页,因此决定自己写一个。

Demo已上传到GitHubCSDN下载频道,是AS项目,使用AS的同学可以直接下载或者clone,博文最后也有贴出完整代码,使用Eclipse的同学可以自己新建项目并Copy代码。

效果图:

(由于这里每个Item都很相像,所以效果看起来不是很好,请见谅) 
图1:

图2: 
(删除的操作是在长按事件中写的) 
图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();
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 <= ) {
// 计算Item的宽度
itemWidth = (parent.getWidth() - pageMargin * ) / spanColumn;
} RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType); holder.itemView.measure(, );
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() / ;
}

还需要其他的几个变量:

private float downX = ; // 手指按下的X轴坐标
private float slideDistance = ; // 滑动的距离
private float scrollX = ; // 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 > ) {
// 上一页
currentPage = currentPage == ? : currentPage - ;
} else {
// 下一页
currentPage = currentPage == totalPage ? totalPage : currentPage + ;
}
}
// 执行滚动
smoothScrollBy((int) ((currentPage - ) * getWidth() - scrollX), );
return true;
default:
break;
} return super.onTouchEvent(event);

2> 页间距

为了分页更加清晰,还需要给页与页添加间距: 
    首先添加一个成员变量,和set方法

private int pageMargin = ; // 页间距
/**
* 设置页间距
*
* @param pageMargin 间距(px)
*/
public void setPageMargin(int pageMargin) {
this.pageMargin = pageMargin;
}

然后重写Adapter的onBindViewHolder方法调整页间距:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (spanColumn == ) {
// 每个Item距离左右两侧各pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin * ;
holder.itemView.setPadding(pageMargin, , pageMargin, );
} else {
int m = position % (spanRow * spanColumn);
if (m < spanRow) {
// 每页左侧的Item距离左边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(pageMargin, , , );
} else if (m >= spanRow * spanColumn - spanRow) {
// 每页右侧的Item距离右边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(, , pageMargin, );
} else {
// 中间的正常显示
holder.itemView.getLayoutParams().width = itemWidth;
holder.itemView.setPadding(, , , );
}
} }

3> 占位Item

为了最后不足一页时也能完整显示,还需要在最后不足一页时,生成占位的View,因此修改Adapter的onBindViewHolder方法和getItemCount方法:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { ... if (position < dataList.size()) {
holder.itemView.setAlpha();
mCallBack.onBindViewHolder(holder, position);
} else {
holder.itemView.setAlpha();
}
} @Override
public int getItemCount() {
int m = dataList.size() % (spanRow * spanColumn);
if (m == ) {
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 = ; // 滚动状态
@Override
public void onScrollStateChanged(int state) {
switch (state) {
case :
scrollState = ;
break;
case :
scrollState = ;
break;
case :
if (slideDistance == ) {
break;
}
scrollState = ;
if (slideDistance < ) { // 上页
currentPage = (int) Math.ceil(scrollX / getWidth());
if (currentPage * getWidth() - scrollX < shortestDistance) {
currentPage += ;
}
} else { // 下页
currentPage = (int) Math.ceil(scrollX / getWidth()) + ;
if (currentPage <= totalPage) {
if (scrollX - (currentPage - ) * getWidth() < shortestDistance) {
// 如果这一页滑出距离不足,则定位到前一页
currentPage -= ;
}
} else {
currentPage = totalPage;
}
}
// 执行自动滚动
smoothScrollBy((int) ((currentPage - ) * getWidth() - scrollX), );
// 修改指示器选中项
mIndicatorView.setSelectedPage(currentPage - );
slideDistance = ;
break;
}
super.onScrollStateChanged(state);
} @Override
public void onScrolled(int dx, int dy) {
scrollX += dx;
if (scrollState == ) {
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 :
case :
realPosition = position + ;
break;
case :
case :
realPosition = position - ;
break;
case :
realPosition = position + ;
break;
case :
realPosition = position - ;
break;
case :
case :
case :
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 = ;
private int measuredHeight = ; 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 <= ) {
View view = recycler.getViewForPosition();
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 = ; // 手指按下的X轴坐标
private float slideDistance = ; // 滑动的距离
private float scrollX = ; // X轴当前的位置 private int spanRow = ; // 行数
private int spanColumn = ; // 每页列数
private int totalPage = ; // 总页数
private int currentPage = ; // 当前页 private int pageMargin = ; // 页间距 private PageIndicatorView mIndicatorView = null; // 指示器布局 public PageRecyclerView(Context context) {
this(context, null);
} public PageRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, );
} 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 <= ? this.spanRow : spanRow;
this.spanColumn = spanColumn <= ? 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() / ;
} @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(), );
}
mIndicatorView.setSelectedPage(currentPage - );
totalPage = temp;
}
} @Override
public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) {
case MotionEvent.ACTION_MOVE:
if (currentPage == totalPage && downX - event.getX() > ) {
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 > ) {
// 上一页
currentPage = currentPage == ? : currentPage - ;
} else {
// 下一页
currentPage = currentPage == totalPage ? totalPage : currentPage + ;
}
// 修改指示器选中项
mIndicatorView.setSelectedPage(currentPage - );
}
// 执行滚动
smoothScrollBy((int) ((currentPage - ) * getWidth() - scrollX), );
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 = ;
private int itemCount = ; /**
* 实例化适配器
*
* @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 <= ) {
// 计算Item的宽度
itemWidth = (parent.getWidth() - pageMargin * ) / spanColumn;
} RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType); holder.itemView.measure(, );
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 == ) {
// 每个Item距离左右两侧各pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin * ;
holder.itemView.setPadding(pageMargin, , pageMargin, );
} else {
int m = position % (spanRow * spanColumn);
if (m < spanRow) {
// 每页左侧的Item距离左边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(pageMargin, , , );
} else if (m >= spanRow * spanColumn - spanRow) {
// 每页右侧的Item距离右边pageMargin
holder.itemView.getLayoutParams().width = itemWidth + pageMargin;
holder.itemView.setPadding(, , pageMargin, );
} else {
// 中间的正常显示
holder.itemView.getLayoutParams().width = itemWidth;
holder.itemView.setPadding(, , , );
}
} 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 = ; // 指示器的大小(dp)
private int margins = ; // 指示器间距(dp)
private List<View> indicatorViews = null; // 存放指示器 public PageIndicatorView(Context context) {
this(context, null);
} public PageIndicatorView(Context context, AttributeSet attrs) {
this(context, attrs, );
} 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 = ; i < count; i++) {
view = new View(mContext);
view.setBackgroundResource(android.R.drawable.presence_invisible);
addView(view, params);
indicatorViews.add(view);
}
if (indicatorViews.size() > ) {
indicatorViews.get().setBackgroundResource(android.R.drawable.presence_online);
}
} /**
* 设置选中页
*
* @param selected 页下标,从0开始
*/
public void setSelectedPage(int selected) {
for (int i = ; 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(, );
// 设置页间距
mRecyclerView.setPageMargin();
// 设置数据
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 = ; i < ; 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>

Android 使用RecyclerView实现多行水平分页的GridView效果和ViewPager效果的更多相关文章

  1. Android 自定义View修炼-自定义HorizontalScrollView视图实现仿ViewPager效果

    开发过程中,需要达到 HorizontalScrollView和ViewPager的效果,于是直接重写了HorizontalScrollView来达到实现ViewPager的效果. 实际效果图如下: ...

  2. Android中如何实现多行、水平滚动的分页的Gridview?

    功能要求: (1)比如每页显示2X2,总共2XN,每个item显示图片+文字(点击有链接). 如果单行水平滚动,可以用Horizontalscrollview实现. 如果是多行水平滚动,则结合Grid ...

  3. Android Studio 单刷《第一行代码》系列 02 —— 日志工具 LogCat

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  4. Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld

    前言(Prologue) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Android ...

  5. Android Studio 单刷《第一行代码》系列 07 —— Broadcast 广播

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  6. Android Studio 单刷《第一行代码》系列 06 —— Fragment 生命周期

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  7. Android Studio 单刷《第一行代码》系列 05 —— Fragment 基础

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  8. Android Studio 单刷《第一行代码》系列 04 —— Activity 相关

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

  9. Android Studio 单刷《第一行代码》系列 03 —— Activity 基础

    前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...

随机推荐

  1. 杂项:OASIS(结构化信息标准促进组织)

    ylbtech-杂项:OASIS(结构化信息标准促进组织) 1.返回顶部 1. OASIS(结构化信息标准促进组织,Organization for the Advancement of Struct ...

  2. POI 中的CellType类型以及值的对应关系

    操作使用POI接口,了解CellType的类型和值的对应关系. CellType 类型 值 CELL_TYPE_NUMERIC 数值型 0 CELL_TYPE_STRING 字符串型 1 CELL_T ...

  3. AngularJs(Part 4)--Modules depending on other Modules

    Angular does an excellent job of managing object dependencies. it can even take care of module depen ...

  4. SQL Server(三)

    一.数据库操作 create database 数据库名称 ——创建drop database 数据库名称 ——删除use 数据库名称 ——使用go 两条SQL语句之间分隔 二.表的操作 create ...

  5. PHP RSA算法 HMAC-SHA1加密算法

    HMAC-SHA1加密算法 function getSignature($str, $key) { $signature = ""; if (function_exists('ha ...

  6. uoj#401. 【CTSC2018】青蕈领主(分治FFT)

    传送门 话说分治\(FFT\)是个啥子啊--还有题目里那字好像念(蕈xùn) 首先考虑无解的情况:区间相交或者\(L_n\neq n\) 这两个都可以感性理解一下 所以区间之间只会有包含关系,我们把每 ...

  7. 截取HTML中的JSON数据并利用GSON进行解析(Android)

    截取HTML中的JSON数据并利用GSON进行解析(Android) 前言 最近在做的一个Android项目,需要自行搭建服务器,队友选择买了阿里云的服务器ESC产品,在数据获取上,我们采用了Andr ...

  8. 洛谷 P1434 [SHOI2002]滑雪 解题报告

    这题方法有很多, 这里介绍2种: 方法1 很容易想到搜索, bfs或dfs应该都可以, 就不放代码了: 方法2 这题还可以用 dp 来做. 做法:先将每个点按照高度从小到大排序,因为大的点只能向小的点 ...

  9. urlencode()和urldecode()

    urlencode()函数原理就是首先把中文字符转换为十六进制,然后在每个字符前面加一个标识符%.一般用在url链接地址编码urldecode()函数与urlencode()函数原理相反,用于解码已编 ...

  10. mysql错误之errorcode 1215问题

    1.此类问题出现时我们往往会迫不及待的去百度,但是问题偶尔也不是我们想的那样,比如我本次犯下的错误如下: 上图是我给表添加外键约束的时候的sql.下图是我建的表,问题出哪里了?原来是我的数据库根本没有 ...