RecycleView实现横向带指示器翻页滑动,一行两列自定义布局
2017-09-18 14:03
549 查看
1AutoGridLayoutManager
2PageIndicatorView
3PageRecyclerView
4使用
首先看需求效果
在acvitity中有一个控件,需要实现这种分页效果,还要指示器,并且每页的两列不能太分散,使用GridView就很不好实现,这里用RecycleView展示,先看成型后需要用到的结构(需要用到哪些自定义的东西)
自己在Java下建一个新的包,需要这三个自定义的东西,一个用来展示GridView布局样式自定义的GridViewLayoutManger,一个指示器的自定义控件,第三个就是自定义RecleView,实现翻页效果,另外还有一个工具类,实现获取屏幕尺寸,并且让px和dp相互转换的功能,网上有很多,没有几行代码,之前也专门写过,不再赘述,接下来就看着三个自定义东西怎么写
4-1:xml文件使用控件:
4-2 java 代码绑定数据并展示,添加item的点击和长按事件
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; 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) { int count = state.getItemCount(); if(count>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); } }
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. * <p/> * 页码指示器类,获得此类实例后,可通过{@link PageIndicatorView#initIndicator(int)}方法初始化指示器 * </P> */ public class PageIndicatorView extends LinearLayout { private Context mContext = null; private int dotSize = 10; // 指示器的大小(dp) private int margins = 10; // 指示器间距(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 = UtilDynamicWidth.dip2px(context, dotSize); margins = UtilDynamicWidth.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); } } } }
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. * <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 slideDistance = 0; // 滑动的距离 private float scrollX = 0; // X轴当前的位置 private int spanRow = 1; // 行数 private int spanColumn = 2; // 每页列数 private int totalPage = 0; // 总页数 private int currentPage = 1; // 当前页 private int pageMargin = 0; // 页间距 private PageIndicatorView mIndicatorView = null; // 指示器布局 private int realPosition = 0; // 真正的位置(从上到下从左到右的排列方式变换成从左到右从上到下的排列方式后的位置) /* * 0: 停止滚动且手指移开; 1: 开始滚动; 2: 手指做了抛的动作(手指离开屏幕前,用力滑了一下) */ private int scrollState = 0; // 滚动状态 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 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); } /** * 数据适配器 */ public class PageAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { private List<Map<String, Object>> dataList = null; private CallBack mCallBack = null; private int itemWidth = 0; private int itemCount = 0; /** * 实例化适配器 * * @param data * @param callBack */ public PageAdapter(List<Map<String, Object>> 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的宽度 // 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; } 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(ViewHolder holder, int position) { WindowManager wm = (WindowManager) mContext .getSystemService(Context.WINDOW_SERVICE); DisplayMetrics outMetrics = new DisplayMetrics(); wm.getDefaultDisplay().getMetrics(outMetrics); int withScreen=outMetrics.widthPixels; 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; // mContext.getWindowManager().getDefaultDisplay().getWidth();//获取手机屏幕宽度px holder.itemView.getLayoutParams().width = withScreen/2; holder.itemView.setPadding(pageMargin*7, 0, 0, 0); } else if (m >= spanRow * spanColumn - spanRow) { // 每页右侧的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); } } countRealPosition(position); holder.itemView.setTag(realPosition); setListener(holder); mCallBack.onBindViewHolder(holder, position); // if (realPosition < dataList.size()) { // holder.itemView.setVisibility(View.VISIBLE); // mCallBack.onBindViewHolder(holder, realPosition); // } else { // holder.itemView.setVisibility(View.INVISIBLE); // } } @Override public int getItemCount() { return itemCount; } private void countRealPosition(int position) { // 为了使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; // } } private void setListener(ViewHolder holder) { // 设置监听 holder.itemView.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { mCallBack.onItemClickListener(v, (Integer) v.getTag()); } }); holder.itemView.setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { mCallBack.onItemLongClickListener(v, (Integer) v.getTag()); return true; } }); } /** * 删除Item * * @param position 位置 */ public void remove(int position) { if (position < dataList.size()) { // 删除数据 dataList.remove(position); itemCount--; // 删除Item notifyItemRemoved(position); // 更新界面上发生改变的Item notifyItemRangeChanged((currentPage - 1) * spanRow * spanColumn, 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); void onItemClickListener(View view, int position); void onItemLongClickListener(View view, int position); } }
4、使用
写完了上边的123,就是相当于实现了一整个自定义的控件,这个控件可以实现横向滑动翻页,可以设置行数和列数,并且可以自定义设置指定两个item之间的距离,避免一页中显示连个item显示间距太大的问题,并且翻页的同时会显示指示器4-1:xml文件使用控件:
<!--自定义RecycleView加指示器横向滑动布局--> <RelativeLayout android:layout_width="match_parent" android:layout_height="85dp"> <inditor.recycleview.horizontal.PageRecyclerView android:id="@+id/cusom_swipe_view" android:layout_width="match_parent" android:layout_height="wrap_content"> </inditor.recycleview.horizontal.PageRecyclerView> <inditor.recycleview.horizontal.PageIndicatorView android:id="@+id/indicator" android:layout_width="match_parent" android:layout_height="12dp" android:layout_alignParentBottom="true" android:layout_marginBottom="3dp"></inditor.recycleview.horizontal.PageIndicatorView> </RelativeLayout>
4-2 java 代码绑定数据并展示,添加item的点击和长按事件
mRecyclerView = (PageRecyclerView) findViewById(R.id.cusom_swipe_view); // 设置指示器 mRecyclerView.setIndicator((PageIndicatorView) findViewById(R.id.indicator)); // 设置行数和列数 mRecyclerView.setPageSize(1, 2); // 设置页间距 mRecyclerView.setPageMargin(30); // 设置数据 mRecyclerView.setAdapter(myAdapter=mRecyclerView.new PageAdapter(list_tequan1, new PageRecyclerView.CallBack() { @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(HuiYuanCenterActivity.this).inflate(R.layout.tequan_item, parent, false); return new MyHolder(view); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { Map<String,Object> map= (Map<String, Object>) list_tequan1.get(position); Glide.with(HuiYuanCenterActivity.this).load(map.get("img").toString()).into( ((MyHolder) holder).iv_huanyuan_zuo); ((MyHolder) holder).gv_text.setText((String) map.get("txt")); } @Override public void onItemClickListener(View view, int position) { // Toast.makeText(HuiYuanCenterActivity.this, "点击:" // + list_tequan1.get(position), Toast.LENGTH_SHORT).show(); } @Override public void onItemLongClickListener(View view, int position) { // Toast.makeText(HuiYuanCenterActivity.this, "删除:" // + list_tequan1.get(position), Toast.LENGTH_SHORT).show(); // myAdapter.remove(position); } }));
相关文章推荐
- Android自定义ViewPager实现纵向滑动翻页效果
- 纯原生打造GridView单行横向滑动(宽度自适应在父布局居中),手机屏幕px和dp的互换方法,附禁止滑动的自定义方法
- 横向两列布局(左列固定,右列自适应)的4中实现方式
- Swift 自定义 UICollectionViewFlowLayout 实现横向布局分页
- CSS学习笔记:使用绝对定位实现横向两列布局
- Android自定义横向滑动菜单的实现
- Android自定义ViewGroup实现可滚动的横向布局(2)
- Android自定义ViewGroup自动换行实现滑动任意布局及事件处理效果
- Android 实现ScrollView自定义翻页宽度(每次滑动一页) - 类似钉钉工作台
- PullToRefreshScrollView+GridView,实现向下滑动中隐藏自定义Action布局
- android viewPager 实现图片无限循环滑动并带有进度的自定义布局
- 自定义ViewPagerIndicator:100行代码实现两种可滑动指示器
- 一个 ScrollView 里面包含 viewpager 嵌套 listview 或 RecyclerView 极少代码实现的流畅滑动效果 处理一个两层滑动 view 的自定义布局,以最少的代码实现,
- android自定义布局-ScrollLayout 实现左右滑动
- 安卓复杂滑动案例 自定义behavior源码分析 实现头布局图片的缩放透明度变化,RecycleView的滑动布局,坐标变化
- Android 自定义横向滑动菜单的实现
- Xamarin自定义布局系列——ListView的一个自定义实现ItemsControl(横向列表)
- android viewPager 实现图片无限循环滑动并带有进度的自定义布局
- 安卓复杂滑动案例 自定义behavior源码分析 实现头布局图片的缩放透明度变化,RecycleView的滑动布局,坐标变化
- 自定义布局实现左右或者上下翻页效果(借助Scroller类)