前些天看到有人在论坛上问这种效果怎么实现,没写过也没用过这个功能,网上查了一下,大多是使用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. windows文件同步工具

    windows 文件同步工具: realTimesync freefilesync second copy   second copy注册码: Name:爱学府软件园 注册码:15BF-E46C-67 ...

  2. android apk 防止反编译技术第二篇-运行时修改字节码

    上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...

  3. Robot FrameWork基础学习(四) 元素定位

    元素定位 对于web自动化测试来说,就是操作页面的各种元素,在操作元素之间需要先找到元素,换句话说就是定位元素. Selenium2Library提供了非常丰富的定位器: 虽然提供了这么多种定位方式, ...

  4. CF-822B

    B. Crossword solving time limit per test 1 second memory limit per test 256 megabytes input standard ...

  5. POJ - 2411 Mondriaan's Dream(轮廓线dp)

    Mondriaan's Dream Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One nig ...

  6. JAVA企业级开发-jdbc入门(09)

    一. jdbc介绍 JDBC全称为:Java DataBase Connectivity(java数据库连接). SUN公司为了简化.统一对数据库的操作,定义了一套Java操作数据库的规范,称之为JD ...

  7. JSP 标准标签库(JSTL)(菜鸟教程)

    菜鸟教程 JSTL 1.1 与 JSTL 1.2 之间的区别?如何下载 JSTL 1.2? JSTL 1.2 中不要求 standard.jar 包. 您可以在 Maven 中央仓库中找到它们. ht ...

  8. ue4 fuck

    https://answers.unrealengine.com/questions/661969/uspringarmcomponent-ucameracomponent-not-identifie ...

  9. Unite Europe案例项目《影子战术》层级优化经验分享

    http://forum.china.unity3d.com/thread-25087-1-9.html 在Unite Europe 2017的Keynote主题演讲中,我们为大家分享了将主机游戏&l ...

  10. pytest框架(一)

    代码示例一 # coding=utf-8 def func(x): return x + 1 def test_answer(): assert func(3) == 5 运行结果 E:\pyYouY ...