Android 使用RecyclerView实现多行水平分页的GridView效果和ViewPager效果
前些天看到有人在论坛上问这种效果怎么实现,没写过也没用过这个功能,网上查了一下,大多是使用ViewPager+GridView或者HorizontalScrollView+GridView实现,不过貌似有点复杂,太懒,没仔细看。这两天学习RecyclerView的使用(网上有很多文章,建议大家阅读本博客的时候先去了解一下),发现RecyclerView可以实现GridView 的横向滚动效果,不过没有分页,因此决定自己写一个。
Demo已上传到GitHub,CSDN下载频道,是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),并添加两个成员变量spanRow
、spanColumn
和一个设置行数列数的方法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_DOWN
和ACTION_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添加onClickListener
、onLongClickListener
、onTouchListener
的时候会产生事件冲突,因此修改为在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);
- }
- 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效果的更多相关文章
- Android 自定义View修炼-自定义HorizontalScrollView视图实现仿ViewPager效果
开发过程中,需要达到 HorizontalScrollView和ViewPager的效果,于是直接重写了HorizontalScrollView来达到实现ViewPager的效果. 实际效果图如下: ...
- Android中如何实现多行、水平滚动的分页的Gridview?
功能要求: (1)比如每页显示2X2,总共2XN,每个item显示图片+文字(点击有链接). 如果单行水平滚动,可以用Horizontalscrollview实现. 如果是多行水平滚动,则结合Grid ...
- Android Studio 单刷《第一行代码》系列 02 —— 日志工具 LogCat
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
- Android Studio 单刷《第一行代码》系列 01 —— 第一战 HelloWorld
前言(Prologue) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Android ...
- Android Studio 单刷《第一行代码》系列 07 —— Broadcast 广播
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
- Android Studio 单刷《第一行代码》系列 06 —— Fragment 生命周期
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
- Android Studio 单刷《第一行代码》系列 05 —— Fragment 基础
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
- Android Studio 单刷《第一行代码》系列 04 —— Activity 相关
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
- Android Studio 单刷《第一行代码》系列 03 —— Activity 基础
前情提要(Previously) 本系列将使用 Android Studio 将<第一行代码>(书中讲解案例使用Eclipse)刷一遍,旨在为想入坑 Android 开发,并选择 Andr ...
随机推荐
- Jasper-Api:接口测试
ylbtech-Jasper-Api:接口测试 1. EditTerminal返回顶部 1. /// <remarks/> [System.Web.Services.Protocols.S ...
- 面向对象(static关键字)
static关键字:用于修饰成员(成员变量和成员函数) 被修饰后的成员具备以下特点: 随着类的加载而加载 优先于对象存在 被所有的对象共享 可以直接被类名调用 使用注意: 静态方法只能访问静态成员 静 ...
- python2 + selenium + eclipse 中,通过django生产数据库表的时候报错
python2 + selenium + eclipse 中,通过django生产数据库表的时候报错 解决: 1.查看自己电脑中,“开始-->控制面板-->管理工具-->服务--&g ...
- web前端之Html和Css应用中的细节问题
1.居中的n种方法:①.margin: 0 20%; ——设置margin上下外边距的值设置为0,左右外边距设置成相同的百分比,既可将盒子居中. ②.margin: 0 auto;width: 100 ...
- 《剑指offer》面试题22—栈的压入、弹出序列
<程序员面试宝典>上也有经典的火车进站问题,类似. 如果12345是压栈顺序,序列45321可能是出栈顺序,但序列43512不可能. 规律:对序列中任意元素n,排在n后且比n小的元素一定是 ...
- 深入剖析ASP.NET Core2.1部署模型,你会大吃一惊
---------------------------- 以下内容针对 ASP.NET Core2.1版本,2.2推出windows IIS进程内寄宿 暂不展开讨论---------------- ...
- 无监督学习:Deep Generative Mode(深度生成模型)
一 前言 1.1 Creation 据说在费曼死后,人们在他生前的黑板上拍到如图画片,在左上角有道:What i cannot create ,I do not understand. Generat ...
- [转] 深度探索Hyperledger技术与应用之超级账本的典型交易流程
转自: https://blog.csdn.net/HiBlock/article/details/80212499 个人感觉对交易流程描述的比较清楚,转载以备查看. 1 典型交易流程 下图所示为Hy ...
- [Xcode 实际操作]六、媒体与动画-(13)使用UIImageView制作帧动画
目录:[Swift]Xcode实际操作 本文将演示如何将导入的序列图片,转换为帧动画. 在项目导航区打开资源文件夹[Assets.xcassets] [+]->[Import]->选择图片 ...
- java利用URL发送get和post请求
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import ...