当前位置:   article > 正文

RecycleView实现横向带指示器翻页滑动,一行两列自定义布局

created by shichaohui on 2015/7/10 0010.

* * 1AutoGridLayoutManager * 2PageIndicatorView * 3PageRecyclerView * 4使用

首先看需求效果\ 这里写图片描述\ 在acvitity中有一个控件,需要实现这种分页效果,还要指示器,并且每页的两列不能太分散,使用GridView就很不好实现,这里用RecycleView展示,先看成型后需要用到的结构(需要用到哪些自定义的东西)\ 这里写图片描述\ 自己在Java下建一个新的包,需要这三个自定义的东西,一个用来展示GridView布局样式自定义的GridViewLayoutManger,一个指示器的自定义控件,第三个就是自定义RecleView,实现翻页效果,另外还有一个工具类,实现获取屏幕尺寸,并且让px和dp相互转换的功能,网上有很多,没有几行代码,之前也专门写过,不再赘述,接下来就看着三个自定义东西怎么写

1、AutoGridLayoutManager

``` package inditor.recycleview.horizontal;

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 YTF on 2017/9/16. */

public class AutoGridLayoutManager extends GridLayoutManager { private int measuredWidth = 0; private int measuredHeight = 0;

  1. public AutoGridLayoutManager(Context context, AttributeSet attrs,
  2. int defStyleAttr, int defStyleRes) {
  3. super(context, attrs, defStyleAttr, defStyleRes);
  4. }
  5. public AutoGridLayoutManager(Context context, int spanCount) {
  6. super(context, spanCount);
  7. }
  8. public AutoGridLayoutManager(Context context, int spanCount,
  9. int orientation, boolean reverseLayout) {
  10. super(context, spanCount, orientation, reverseLayout);
  11. }
  12. @Override
  13. public void onMeasure(RecyclerView.Recycler recycler,
  14. RecyclerView.State state, int widthSpec, int heightSpec) {
  15. if (measuredHeight <= 0) {
  16. int count = state.getItemCount();
  17. if(count>0){
  18. View view = recycler.getViewForPosition(0);
  19. if (view != null) {
  20. measureChild(view, widthSpec, heightSpec);
  21. measuredWidth = View.MeasureSpec.getSize(widthSpec);
  22. measuredHeight = view.getMeasuredHeight() * getSpanCount();
  23. }
  24. }
  25. }
  26. setMeasuredDimension(measuredWidth, measuredHeight);
  27. }

} ```

2、PageIndicatorView

``` package inditor.recycleview.horizontal;

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

import com.lab.web.entity.UtilDynamicWidth;

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

/** * Created by shichaohui on 2015/7/10 0010. *

public class PageIndicatorView extends LinearLayout {

  1. private Context mContext = null;
  2. private int dotSize = 10; // 指示器的大小(dp)
  3. private int margins = 10; // 指示器间距(dp)
  4. private List<View> indicatorViews = null; // 存放指示器
  5. public PageIndicatorView(Context context) {
  6. this(context, null);
  7. }
  8. public PageIndicatorView(Context context, AttributeSet attrs) {
  9. this(context, attrs, 0);
  10. }
  11. public PageIndicatorView(Context context, AttributeSet attrs, int defStyleAttr) {
  12. super(context, attrs, defStyleAttr);
  13. init(context);
  14. }
  15. private void init(Context context) {
  16. this.mContext = context;
  17. setGravity(Gravity.CENTER);
  18. setOrientation(HORIZONTAL);
  19. dotSize = UtilDynamicWidth.dip2px(context, dotSize);
  20. margins = UtilDynamicWidth.dip2px(context, margins);
  21. }
  22. /**
  23. * 初始化指示器,默认选中第一页
  24. *
  25. * @param count 指示器数量,即页数
  26. */
  27. public void initIndicator(int count) {
  28. if (indicatorViews == null) {
  29. indicatorViews = new ArrayList<>();
  30. } else {
  31. indicatorViews.clear();
  32. removeAllViews();
  33. }
  34. View view;
  35. LayoutParams params = new LayoutParams(dotSize, dotSize);
  36. params.setMargins(margins, margins, margins, margins);
  37. for (int i = 0; i < count; i++) {
  38. view = new View(mContext);
  39. view.setBackgroundResource(android.R.drawable.presence_invisible);
  40. addView(view, params);
  41. indicatorViews.add(view);
  42. }
  43. if (indicatorViews.size() > 0) {
  44. indicatorViews.get(0).setBackgroundResource(android.R.drawable.presence_online);
  45. }
  46. }
  47. /**
  48. * 设置选中页
  49. *
  50. * @param selected 页下标,从0开始
  51. */
  52. public void setSelectedPage(int selected) {
  53. for (int i = 0; i < indicatorViews.size(); i++) {
  54. if (i == selected) {
  55. indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_online);
  56. } else {
  57. indicatorViews.get(i).setBackgroundResource(android.R.drawable.presence_invisible);
  58. }
  59. }
  60. }

} ```

3、PageRecyclerView

``` package inditor.recycleview.horizontal;

import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.View; import android.view.ViewGroup; import android.view.WindowManager;

import com.lab.web.entity.UtilDynamicWidth;

import java.util.List; import java.util.Map;

/** * Created by shichaohui on 2015/7/9 0009. *

* 横向分页的GridView效果 *

*

* 默认为1行,每页3列,如果要自定义行数和列数,请在调用{@link PageRecyclerView#setAdapter(Adapter)}方法前调用 * {@link PageRecyclerView#setPageSize(int, int)}方法自定义行数 *

*/ public class PageRecyclerView extends RecyclerView {
  1. private Context mContext = null;
  2. private PageAdapter myAdapter = null;
  3. private int shortestDistance; // 超过此距离的滑动才有效
  4. private float slideDistance = 0; // 滑动的距离
  5. private float scrollX = 0; // X轴当前的位置
  6. private int spanRow = 1; // 行数
  7. private int spanColumn = 2; // 每页列数
  8. private int totalPage = 0; // 总页数
  9. private int currentPage = 1; // 当前页
  10. private int pageMargin = 0; // 页间距
  11. private PageIndicatorView mIndicatorView = null; // 指示器布局
  12. private int realPosition = 0; // 真正的位置(从上到下从左到右的排列方式变换成从左到右从上到下的排列方式后的位置)
  13. /*
  14. * 0: 停止滚动且手指移开; 1: 开始滚动; 2: 手指做了抛的动作(手指离开屏幕前,用力滑了一下)
  15. */
  16. private int scrollState = 0; // 滚动状态
  17. public PageRecyclerView(Context context) {
  18. this(context, null);
  19. }
  20. public PageRecyclerView(Context context, AttributeSet attrs) {
  21. this(context, attrs, 0);
  22. }
  23. public PageRecyclerView(Context context, AttributeSet attrs, int defStyle) {
  24. super(context, attrs, defStyle);
  25. defaultInit(context);
  26. }
  27. // 默认初始化
  28. private void defaultInit(Context context) {
  29. this.mContext = context;
  30. setLayoutManager(new AutoGridLayoutManager(
  31. mContext, spanRow, AutoGridLayoutManager.HORIZONTAL, false));
  32. setOverScrollMode(OVER_SCROLL_NEVER);
  33. }
  34. /**
  35. * 设置行数和每页列数
  36. *
  37. * @param spanRow 行数,<=0表示使用默认的行数
  38. * @param spanColumn 每页列数,<=0表示使用默认每页列数
  39. */
  40. public void setPageSize(int spanRow, int spanColumn) {
  41. this.spanRow = spanRow <= 0 ? this.spanRow : spanRow;
  42. this.spanColumn = spanColumn <= 0 ? this.spanColumn : spanColumn;
  43. setLayoutManager(new AutoGridLayoutManager(
  44. mContext, this.spanRow, AutoGridLayoutManager.HORIZONTAL, false));
  45. }
  46. /**
  47. * 设置页间距
  48. *
  49. * @param pageMargin 间距(px)
  50. */
  51. public void setPageMargin(int pageMargin) {
  52. this.pageMargin = pageMargin;
  53. }
  54. /**
  55. * 设置指示器
  56. *
  57. * @param indicatorView 指示器布局
  58. */
  59. public void setIndicator(PageIndicatorView indicatorView) {
  60. this.mIndicatorView = indicatorView;
  61. }
  62. @Override
  63. protected void onMeasure(int widthSpec, int heightSpec) {
  64. super.onMeasure(widthSpec, heightSpec);
  65. shortestDistance = getMeasuredWidth() / 3;
  66. }
  67. @Override
  68. public void setAdapter(Adapter adapter) {
  69. super.setAdapter(adapter);
  70. this.myAdapter = (PageAdapter) adapter;
  71. update();
  72. }
  73. // 更新页码指示器和相关数据
  74. private void update() {
  75. // 计算总页数
  76. int temp = ((int) Math.ceil(myAdapter.dataList.size() / (double) (spanRow * spanColumn)));
  77. if (temp != totalPage) {
  78. mIndicatorView.initIndicator(temp);
  79. // 页码减少且当前页为最后一页
  80. if (temp < totalPage && currentPage == totalPage) {
  81. currentPage = temp;
  82. // 执行滚动
  83. smoothScrollBy(-getWidth(), 0);
  84. }
  85. mIndicatorView.setSelectedPage(currentPage - 1);
  86. totalPage = temp;
  87. }
  88. }
  89. @Override
  90. public void onScrollStateChanged(int state) {
  91. switch (state) {
  92. case 2:
  93. scrollState = 2;
  94. break;
  95. case 1:
  96. scrollState = 1;
  97. break;
  98. case 0:
  99. if (slideDistance == 0) {
  100. break;
  101. }
  102. scrollState = 0;
  103. if (slideDistance < 0) { // 上页
  104. currentPage = (int) Math.ceil(scrollX / getWidth());
  105. if (currentPage * getWidth() - scrollX < shortestDistance) {
  106. currentPage += 1;
  107. }
  108. } else { // 下页
  109. currentPage = (int) Math.ceil(scrollX / getWidth()) + 1;
  110. if (currentPage <= totalPage) {
  111. if (scrollX - (currentPage - 2) * getWidth() < shortestDistance) {
  112. // 如果这一页滑出距离不足,则定位到前一页
  113. currentPage -= 1;
  114. }
  115. } else {
  116. currentPage = totalPage;
  117. }
  118. }
  119. // 执行自动滚动
  120. smoothScrollBy((int) ((currentPage - 1) * getWidth() - scrollX), 0);
  121. // 修改指示器选中项
  122. mIndicatorView.setSelectedPage(currentPage - 1);
  123. slideDistance = 0;
  124. break;
  125. }
  126. super.onScrollStateChanged(state);
  127. }
  128. @Override
  129. public void onScrolled(int dx, int dy) {
  130. scrollX += dx;
  131. if (scrollState == 1) {
  132. slideDistance += dx;
  133. }
  134. super.onScrolled(dx, dy);
  135. }
  136. /**
  137. * 数据适配器
  138. */
  139. public class PageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
  140. private List<Map<String, Object>> dataList = null;
  141. private CallBack mCallBack = null;
  142. private int itemWidth = 0;
  143. private int itemCount = 0;
  144. /**
  145. * 实例化适配器
  146. *
  147. * @param data
  148. * @param callBack
  149. */
  150. public PageAdapter(List<Map<String, Object>> data, CallBack callBack) {
  151. this.dataList = data;
  152. this.mCallBack = callBack;
  153. itemCount = dataList.size(); //+ spanRow * spanColumn;
  154. }
  155. @Override
  156. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  157. if (itemWidth <= 0) {
  158. // 计算Item的宽度

// parent.post(new Runnable() { // @Override // public void run() { // itemWidth = (getWidth() - pageMargin * 2) / spanColumn; // } // }); // itemWidth = (parent.getWidth() - pageMargin * 2) / spanColumn; //获取手机屏幕宽度px WindowManager wm = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); int withScreen=outMetrics.widthPixels; itemWidth = withScreen/6; }

  1. RecyclerView.ViewHolder holder = mCallBack.onCreateViewHolder(parent, viewType);
  2. holder.itemView.measure(0, 0);
  3. holder.itemView.getLayoutParams().width = itemWidth;
  4. holder.itemView.getLayoutParams().height = holder.itemView.getMeasuredHeight();
  5. return holder;
  6. }
  7. @Override
  8. public void onBindViewHolder(ViewHolder holder, int position) {
  9. WindowManager wm = (WindowManager) mContext
  10. .getSystemService(Context.WINDOW_SERVICE);
  11. DisplayMetrics outMetrics = new DisplayMetrics();
  12. wm.getDefaultDisplay().getMetrics(outMetrics);
  13. int withScreen=outMetrics.widthPixels;
  14. if (spanColumn == -1) {
  15. // 每个Item距离左右两侧各pageMargin

// holder.itemView.getLayoutParams().width = itemWidth + pageMargin * 2; // holder.itemView.setPadding(pageMargin, 0, pageMargin, 0); } else {

  1. int m = position % (spanRow * spanColumn);
  2. if (m < spanRow) {
  3. // 每页左侧的Item距离左边pageMargin

// holder.itemView.getLayoutParams().width = itemWidth + pageMargin; // mContext.getWindowManager().getDefaultDisplay().getWidth();//获取手机屏幕宽度px

  1. holder.itemView.getLayoutParams().width = withScreen/2;
  2. holder.itemView.setPadding(pageMargin*7, 0, 0, 0);
  3. } else if (m >= spanRow * spanColumn - spanRow) {
  4. // 每页右侧的Item距离右边pageMargin

// holder.itemView.getLayoutParams().width = itemWidth + pageMargin; holder.itemView.getLayoutParams().width = withScreen/2; holder.itemView.setPadding(0, 0, pageMargin*7, 0); } else { // 中间的正常显示 // holder.itemView.getLayoutParams().width = itemWidth; // holder.itemView.setPadding(0, 0, 0, 0); } }

  1. countRealPosition(position);
  2. holder.itemView.setTag(realPosition);
  3. setListener(holder);
  4. mCallBack.onBindViewHolder(holder, position);

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

  1. }
  2. @Override
  3. public int getItemCount() {
  4. return itemCount;
  5. }
  6. private void countRealPosition(int position) {
  7. // 为了使Item从左到右从上到下排列,需要position的值

// int m = position % (spanRow * spanColumn); realPosition = position; // 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; // } }

  1. private void setListener(ViewHolder holder) {
  2. // 设置监听
  3. holder.itemView.setOnClickListener(new OnClickListener() {
  4. @Override
  5. public void onClick(View v) {
  6. mCallBack.onItemClickListener(v, (Integer) v.getTag());
  7. }
  8. });
  9. holder.itemView.setOnLongClickListener(new OnLongClickListener() {
  10. @Override
  11. public boolean onLongClick(View v) {
  12. mCallBack.onItemLongClickListener(v, (Integer) v.getTag());
  13. return true;
  14. }
  15. });
  16. }
  17. /**
  18. * 删除Item
  19. *
  20. * @param position 位置
  21. */
  22. public void remove(int position) {
  23. if (position < dataList.size()) {
  24. // 删除数据
  25. dataList.remove(position);
  26. itemCount--;
  27. // 删除Item
  28. notifyItemRemoved(position);
  29. // 更新界面上发生改变的Item
  30. notifyItemRangeChanged((currentPage - 1) * spanRow * spanColumn, currentPage * spanRow * spanColumn);
  31. // 更新页码指示器
  32. update();
  33. }
  34. }
  35. }
  36. public interface CallBack {
  37. /**
  38. * 创建VieHolder
  39. *
  40. * @param parent
  41. * @param viewType
  42. */
  43. RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
  44. /**
  45. * 绑定数据到ViewHolder
  46. *
  47. * @param holder
  48. * @param position
  49. */
  50. void onBindViewHolder(RecyclerView.ViewHolder holder, int position);
  51. void onItemClickListener(View view, int position);
  52. void onItemLongClickListener(View view, int position);
  53. }

} ```

4、使用

写完了上边的123,就是相当于实现了一整个自定义的控件,这个控件可以实现横向滑动翻页,可以设置行数和列数,并且可以自定义设置指定两个item之间的距离,避免一页中显示连个item显示间距太大的问题,并且翻页的同时会显示指示器\ 4-1:xml文件使用控件:

``` width="matchparent" android:layout height="wrapcontent">

  1. </inditor.recycleview.horizontal.PageRecyclerView>
  2. <inditor.recycleview.horizontal.PageIndicatorView
  3. android:id="@+id/indicator"
  4. android:layout_width="match_parent"
  5. android:layout_height="12dp"
  6. android:layout_alignParentBottom="true"
  7. android:layout_marginBottom="3dp"></inditor.recycleview.horizontal.PageIndicatorView>
  8. </RelativeLayout>

```

4-2 java 代码绑定数据并展示,添加item的点击和长按事件

``` mRecyclerView = (PageRecyclerView) findViewById(R.id.cusomswipeview); // 设置指示器 mRecyclerView.setIndicator((PageIndicatorView) findViewById(R.id.indicator)); // 设置行数和列数 mRecyclerView.setPageSize(1, 2);

  1. // 设置页间距
  2. mRecyclerView.setPageMargin(30);
  3. // 设置数据
  4. mRecyclerView.setAdapter(myAdapter=mRecyclerView.new PageAdapter(list_tequan1, new PageRecyclerView.CallBack() {
  5. @Override
  6. public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  7. View view = LayoutInflater.from(HuiYuanCenterActivity.this).inflate(R.layout.tequan_item, parent, false);
  8. return new MyHolder(view);
  9. }
  10. @Override
  11. public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
  12. Map<String,Object> map= (Map<String, Object>) list_tequan1.get(position);
  13. Glide.with(HuiYuanCenterActivity.this).load(map.get("img").toString()).into( ((MyHolder) holder).iv_huanyuan_zuo);
  14. ((MyHolder) holder).gv_text.setText((String) map.get("txt"));
  15. }
  16. @Override
  17. public void onItemClickListener(View view, int position) {

// Toast.makeText(HuiYuanCenterActivity.this, "点击:" // + listtequan1.get(position), Toast.LENGTHSHORT).show();

  1. }
  2. @Override
  3. public void onItemLongClickListener(View view, int position) {

// Toast.makeText(HuiYuanCenterActivity.this, "删除:" // + listtequan1.get(position), Toast.LENGTHSHORT).show(); // myAdapter.remove(position); } })); ```

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/174738
推荐阅读
相关标签
  

闽ICP备14008679号