LinearHorizontalRecyclerView

探讨如何在安卓应用中实现界面模仿与事件拦截,确保用户在滑动操作时获得期望的体验,通过重写RecyclerView类来控制事件传递,避免不必要的页面切换。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在学习安卓,找点应用模仿一下界面


LinearHorizontalRecyclerView只是RecyclerView + LinearLayout + Horizontal而已

想要达到的效果是这样的先上图:


大家先看番剧更新这一区域,它是由一个RecyclerView + 水平线性管理器 组成的


现在我们向左滑一点点 假设滑动距离是10  此时手不要放开


这样第一个Item就有部分被遮住了, 然后再向右滑动超过10的距离,

就会发现现在变成ViewPager在滑动了 原因是因为 RV(RecyclerView的简称)已经滑到尽头了 所以事件就被扔给了父元素(ViewPager,VP) 而且以后RV再也捕获收到事件了(除非释放又开始)

这样的结果有时候是我们想要的效果 不过有的时候不是我们想要的

我现在想达到的效果是:"如果一开始RV就是在最左边并且你要向右滑,那么我才允许ViewPager捕获事件" 否则的话请先滑动到最左边 然后再次向右滑    然后对于最右边的情况也是一样

这样可以避免 用户滑动距离太大 或 RV里面的项比较少 而直接切换到下一个Page


于是我们需要重写RV


	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
			case MotionEvent.ACTION_DOWN:
				//一开始我们要无条件的请求不要拦截该事件 这样我们才有处理的机会
				getParent().requestDisallowInterceptTouchEvent(true);
				mLastX = ev.getX();
				mLastY = ev.getY();
				mMoveInited = false;//还没第一次move
				mParentCanIntercept = false;//父亲不允许拦截我
				break;
			case MotionEvent.ACTION_MOVE:
				float x = ev.getX();
				float y = ev.getY();
				if (!mMoveInited) {
					mMoveInited = true;//标记
					if (Math.abs(x - mLastX) > Math.abs(y - mLastY)) {//水平移动比较多
<span style="white-space:pre">						</span>//判断一下该RV还能不能向左移动
						boolean canLeft =...
<span style="white-space:pre">						</span>//判断一下该RV还能不能向右移动
						boolean canRight = ...
<span style="white-space:pre">						</span>
						if ((x > mLastX && !canLeft) || (x < mLastX && !canRight)) {//当且仅当我不能向左走而你又要让我向左走(手右滑) 或 相反...
							getParent().requestDisallowInterceptTouchEvent(false);
							mParentCanIntercept = true;
						}
					} else {//垂直比移动比较多
						getParent().requestDisallowInterceptTouchEvent(false);
						mParentCanIntercept = true;
					}
				} else {//这里是第二次move及其以后的move
					if (mParentCanIntercept)//mParentCanIntercept=true的话只有上面2个来源 都是可以允许父亲拦截我的
						getParent().requestDisallowInterceptTouchEvent(false);
				}
				mLastX = x;
				mLastY = y;
				break;

		}
		return super.dispatchTouchEvent(ev);
	}
引入了若干个

mLastX 记录上一次的X, mLastY 记录上一次的Y, mMoveInited 用于是否已经过了第一个ACTION_MOVE,  mParentCanIntercept用于表示是否该让父元素拦截

然后还有两个临时变量 canLeft 用于表示该RV能不能向左移动  canRight:表示该RV能不能向右移动

整体的判断逻辑还是比较简单的

有一个关键是如何获得

canLeft和canRight, 可以使用LinearLayoutManager的findFirstCompletelyVisibleItemPosition()和findLasttCompletelyVisibleItemPosition()方法

canLeft=((LinearLayoutManager)getLayoutManager()).findFirstCompletelyVisibleItemPosition()>0; 如果第一个元素不是完全可见的 那么就还有向左边走的空间
canRight = ((LinearLayoutManager) getLayoutManager()).findLastCompletelyVisibleItemPosition() < getLayoutManager().getItemCount() - 1; 如果最后一个元素不是完全可见的 那么还有向右走的空间
这样就OK了
上面代码还同时解决了一个问题 就是:
当你第一个ACTION_MOVE的时候 如果 做的比较多的是水平的移动 那么你以后只能进行水平移动 如果你做的比较多的是垂直移动 那么你以后只能做垂直移动(其实这个垂直移动很有可能被父元素捕获 从而RV失去事件 不过这一般是我们想要的)
否则会出现一个情况就是:
你左右滑动你的RV, 手不放, 然后变成上下滑动,手不放 这时候你就会丢失你的触摸事件 因为我们的RV是水平的 不允许上下滑动 所以一旦有上下滑动 就会被父元素捕获 从而导致RV失去事件(意思就是: 即使你从上下滑动再变成水平滑动,RV也不会理你了)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值