时隔许久,我又要更博了,含蓄的话不多说了,今天我们的主题是这个RecyclerView。至于为什么要加个scrollBar?因为我的业务需求是需要一个实现成这样的,
效果图:(可能看起来比较粗糙,但功能实现了,你可以自定义修改嘛~哈哈哈)
可能我比较老派,我的app 中list的垂直布局还是喜欢用listView ,使用起来简单(可能博主我之前有个通用的adapter不舍得放弃吧,虽然RecyclerView也有通用的adapter,但总感觉显示简单的列表要多写几行代码,有点麻烦,哈哈哈,可能我就想偷懒吧)
但既然今天的是这个RecyclerView那么我们就开始详细的说明下吧~废话不多说切入正题。
注意:我这个View暂时只实现了垂直的View的ScrollBar的滚动,暂时没有横向实现,
未来可能我会拓展,敬请期待吧(大佬可能觉得太简单,勿喷,求教,谢谢)
先说下我的思路:
一、初始思路(想一个新玩意总得有个过程和弯路嘛),可能跟你想法一样哦
- 用一个LinearLayout 布局套住 RecyclerView 和一个自定义的View
- 自定义一个可以上下滚动的scrollBar
- 滚动的ScrollBar绑定RecyclerView的滚动事件
- Ok
二、现在的实现的思路 (我想了下如果用上面的方案实现的话就,必须写个xml文件,自定义一个ViewGroup,别人使用起来会很麻烦,当然你把他当成类库来用也很简单,而我就不想写xml)
- HobbyRecyclerView继承RecyclerView, 扩展这个功能
- 在HobbyRecyclerView 中先让子view的宽度缩小 ,留出给scrollBar的宽度
- 在HobbyRecyclerView中画一个ScrollBar ,这个bar的高度 = (view的可见高度 / view的所有子View的高度)* view可见高度 。
- 给HobbyRecyclerView加上滚动事件监听,监听滚动距离dy*(view的可见高度 / view的所有子View的高度)= scrollbar 的滚动距离
- 给scrollBar加上监听,监听拖动距离转化为 RecyclerView的滚动距离 = (scrollBar的拖动距离/scrollBar可滚动的区域高度(这里的区域高度等于View的高度)) * view的所有子View的高度
- 测试ok
思路也大概说了,那么就来看代码的实现吧。
/** * @author mdm * @Description HobbyRecyclerView * @Version 1.0 */ public class HobbyRecyclerView extends RecyclerView { private RectF mRangeRectf; private Paint mRangPaint; private int showMode = 1; //滚动条宽高 private float scrollBarHeight; private float scrollBarWidth; //柱间隙 private float scrollWidthSpace ; //滚动条宽度的等分比例 private int scrollWidthWeight = 10; //Y轴的偏移值 private float yScrollOffset = 0; //所有的子view的总高度 也就是这个 private float childViewAllHeight; //可视区域的高度 其实这里就是View高度 private float visualHeight; //可视区域的高度/所有的子view的总高度 得出的比例 float range; //recyclerView的每个Item项的宽度 private int childWidth; //判断触摸焦点 private boolean isFocus = false; //手触摸时点的x,y坐标 float x = 0; float y = 0; public HobbyRecyclerView(@NonNull Context context) { super(context); } public HobbyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context,attrs); } public HobbyRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context,attrs); } /** * 初始化 * @param context * @param attrs */ private void init(Context context, AttributeSet attrs) { mRangeRectf = new RectF(); // region = new Region(); mRangPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mRangPaint.setStyle(Paint.Style.FILL); TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.HobbyRecyclerView); showMode = ta.getInteger(R.styleable.HobbyRecyclerView_scrollBarMode,1); ta.recycle(); addOnScrollListener(onScrollListener); } @Override protected void onMeasure(int widthSpec, int heightSpec) { if(showMode != 1){ int width = MeasureSpec.getSize(widthSpec); scrollBarWidth = width / scrollWidthWeight;//取10分之一 scrollWidthSpace = scrollBarWidth / 10; //获取间隙 childWidth = (int) (width - scrollBarWidth); scrollBarWidth = scrollBarWidth - 2 * scrollWidthSpace; for (int i = 0; i < getChildCount(); i++) { measureChild(getChildAt(i),childWidth,heightSpec); getChildAt(i).getLayoutParams().width = childWidth; } setMeasuredDimension(width,heightSpec); }else { super.onMeasure(widthSpec, heightSpec); } } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); visualHeight = getMeasuredHeight(); childViewAllHeight = getChildAt(2).getHeight() * getAdapter().getItemCount(); range = 0; if(childViewAllHeight != 0){ range = visualHeight / childViewAllHeight; } scrollBarHeight = range * visualHeight; } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); drawRange(canvas); } private void drawRange(Canvas canvas){ if(canvas == null) return; mRangeRectf.set(childWidth + scrollWidthSpace,yScrollOffset,childWidth + scrollBarWidth ,yScrollOffset + scrollBarHeight); if(isFocus) { mRangPaint.setColor(Color.parseColor("#2386BF")); }else{ mRangPaint.setColor(Color.parseColor("#2EB3FF")); } canvas.drawRect(mRangeRectf,mRangPaint); Log.i("tag" , "yScrollOffset ------- " + yScrollOffset); Log.i("tag" , "scrollBarHeight ------- " + scrollBarHeight); Log.i("tag" , "yScrollOffset ------- " + yScrollOffset); } private RecyclerView.OnScrollListener onScrollListener = new OnScrollListener() { @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); } @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); yScrollOffset += dy * range; } }; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //获取屏幕上点击的坐标 x = event.getX(); y = event.getY(); if(x >= mRangeRectf.left && x <= mRangeRectf.right && y >= mRangeRectf.top && y <= mRangeRectf.bottom){ isFocus = true; invalidate(); } break; case MotionEvent.ACTION_MOVE: if(x >= mRangeRectf.left && x <= mRangeRectf.right && y >= mRangeRectf.top && y <= mRangeRectf.bottom){ float diffValue = event.getY() - y; scrollBy(0, (int) ((diffValue/visualHeight) * childViewAllHeight)); y = event.getY(); } break; case MotionEvent.ACTION_UP: isFocus = false; invalidate(); break; } if(x >= childWidth && x <= getMeasuredWidth() && y >= 0 && y <= getMeasuredHeight()){ return true; }else return super.onTouchEvent(event); } /** * //当前RcyclerView显示区域的高度。水平列表屏幕从左侧到右侧显示范围 int extent = this.computeHorizontalScrollExtent(); //整体的高度,注意是整体,包括在显示区域之外的。 int range = this.computeHorizontalScrollRange(); //已经向下滚动的距离,为0时表示已处于顶部。 int offset = this.computeHorizontalScrollOffset(); */ }
是不是很简单呢?
ok有什么问题自己看吧。如果你看到最后几行注释了,那么你会疑问,那是因为我看了这个文章
仿拼多多可水平滚动RecyclerView,自定义滚动条滚动距离
原文地址:https://www.cnblogs.com/woaixingxing/p/10783104.html
时间: 2024-10-10 17:41:23