ViewPager,ScrollView 嵌套ViewPager滑动冲突解决,腾讯、阿里Android高级面试真题汇总

return false;

}

}

return super.onInterceptTouchEvent(ev);

}

内部解决法

从子View着手,父View先不要拦截任何事件,所有的事件传递给 子View,如果子View需要此事件就消费掉,不需要此事件的话就交给 父View处理。

实现思路 如下,重写子 View的dispatchTouchEvent方法,在Action_down 动作中通过方法 requestDisallowInterceptTouchEvent(true) 先请求 父 View不要拦截事件,这样保证子 View 能够接受到 Action_move 事件,再在 Action_move 动作中根据自己的逻辑是否要拦截事件,不需要拦截事件的话再交给 父 View 处理。

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

int x = (int) ev.getRawX();

int y = (int) ev.getRawY();

int dealtX = 0;

int dealtY = 0;

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

dealtX = 0;

dealtY = 0;

// 保证子View能够接收到Action_move事件

getParent().requestDisallowInterceptTouchEvent(true);

break;

case MotionEvent.ACTION_MOVE:

dealtX += Math.abs(x - lastX);

dealtY += Math.abs(y - lastY);

Log.i(TAG, “dealtX:=” + dealtX);

Log.i(TAG, “dealtY:=” + dealtY);

// 这里是够拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截

if (dealtX >= dealtY) {

getParent().requestDisallowInterceptTouchEvent(true);

} else {

getParent().requestDisallowInterceptTouchEvent(false);

}

lastX = x;

lastY = y;

break;

case MotionEvent.ACTION_CANCEL:

break;

case MotionEvent.ACTION_UP:

break;

}

return super.dispatchTouchEvent(ev);

}


ScrollView 里面嵌套ViewPager导致的滑动冲突


外部解决法

如上面所述,从 父View ScrollView着手,重写 OnInterceptTouchEvent方法,在上下滑动的时候拦截事件,在左右滑动的时候不拦截事件,返回 false,这样确保子View 的dispatchTouchEvent方法会被调用,代码 如下

/**

  • @ explain:这个ScrlloView不拦截水平滑动事件,

  • 是用来解决 ScrollView里面嵌套ViewPager使用的

  • @ author:xujun on 2016/10/25 15:28

  • @ email:gdutxiaoxu@163.com

*/

public class VerticalScrollView extends ScrollView {

public VerticalScrollView(Context context) {

super(context);

}

public VerticalScrollView(Context context, AttributeSet attrs) {

super(context, attrs);

}

public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

}

@TargetApi(21)

public VerticalScrollView(Context context, AttributeSet attrs, int defStyleAttr, int

defStyleRes) {

super(context, attrs, defStyleAttr, defStyleRes);

}

private float mDownPosX = 0;

private float mDownPosY = 0;

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

final float x = ev.getX();

final float y = ev.getY();

final int action = ev.getAction();

switch (action) {

case MotionEvent.ACTION_DOWN:

mDownPosX = x;

mDownPosY = y;

break;

case MotionEvent.ACTION_MOVE:

final float deltaX = Math.abs(x - mDownPosX);

final float deltaY = Math.abs(y - mDownPosY);

// 这里是否拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截

if (deltaX > deltaY) {// 左右滑动不拦截

return false;

}

}

return super.onInterceptTouchEvent(ev);

}

}

内部解决法

如上面上述,通过requestDisallowInterceptTouchEvent(true)方法来影响父View是否拦截事件,我们通过重写ViewPager的 dispatchTouchEvent()方法,在左右滑动的时候请求父View ScrollView不要拦截事件,其他的时候由子View 拦截事件

/**

  • @ explain:这个 ViewPager是用来解决ScrollView里面嵌套ViewPager的 内部解决法的

  • @ author:xujun on 2016/10/25 16:38

  • @ email:gdutxiaoxu@163.com

*/

public class MyViewPager extends ViewPager {

private static final String TAG = “xujun”;

int lastX = -1;

int lastY = -1;

public MyViewPager(Context context) {

super(context);

}

public MyViewPager(Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

int x = (int) ev.getRawX();

int y = (int) ev.getRawY();

int dealtX = 0;

int dealtY = 0;

switch (ev.getAction()) {

case MotionEvent.ACTION_DOWN:

dealtX = 0;

dealtY = 0;

// 保证子View能够接收到Action_move事件

getParent().requestDisallowInterceptTouchEvent(true);

break;

case MotionEvent.ACTION_MOVE:

dealtX += Math.abs(x - lastX);

dealtY += Math.abs(y - lastY);

Log.i(TAG, “dealtX:=” + dealtX);

Log.i(TAG, “dealtY:=” + dealtY);

// 这里是否拦截的判断依据是左右滑动,读者可根据自己的逻辑进行是否拦截

if (dealtX >= dealtY) { // 左右滑动请求父 View 不要拦截

getParent().requestDisallowInterceptTouchEvent(true);

} else {

getParent().requestDisallowInterceptTouchEvent(false);

}

lastX = x;

lastY = y;

break;

case MotionEvent.ACTION_CANCEL:

break;

case MotionEvent.ACTION_UP:

break;

}

return super.dispatchTouchEvent(ev);

}

}

注意事项(坑)


当我们 ScrollView 的最上层的 Layout 里面多多个孩子的时候,当下面一个孩子是 RecyclerView 或者ListView 的时候,往往会自动滑动到 ListView 或者 RecyclerView 的第一个 item,导致进入界面的时候会导致 RecyclerView 上面的 View 被滑动到界面之外,看不见,这时候的用户体验是比较差的

即结构如下面的时候

在这里插入图片描述

在Activity中的相关解决方法

于是我查找了相关的资料,在Activity中完美解决,主要要一下两种方法

第一种方法,重写Activity的onWindowFocusChanged()方法,在里面调用mNoHorizontalScrollView.scrollTo(0,0);方法,滑动到顶部,因为onWindowFocusChanged是在所有View绘制完毕的时候才会回调的,不熟悉的话建议先回去看一下Activity的生命周期的相关介绍

private void scroll() {

mNoHorizontalScrollView.scrollTo(0,0);

}

@Override

public void onWindowFocusChanged(boolean hasFocus) {

super.onWindowFocusChanged(hasFocus);

if(hasFocus && first){

first=false;

scroll();

}

}

第二种解决方法,调用RecyclerView上面的View的一下方法,让其获取焦点

view.setFocusable(true);

view.setFocusableInTouchMode(true);

view.requestFocus();

这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了。

在Fragment中的相关解决方法

同样是调用第二种方法,调用RecyclerView上面的View的一下方法,让其获取焦点

view.setFocusable(true);

view.setFocusableInTouchMode(true);

view.requestFocus();

这段代码在初始化的时候就让该界面的顶部的某一个控件获得焦点,滚动条自然就显示到顶部了。但是该方法存在缺点,就是当我们上面的view如果滑动到一半的时候,切换到下一个Fragment,在切换回来的时候,RecyclerView的第一个item会自动滑动到顶部。目前我还没有找到相对比较好的解决这个问题的方法,大家知道相关解决方法的话也欢迎联系我,可以加我 微信或者在留言区评论,谢谢。

网友提供的解决方案

关于 ViewPagerActivity 在Fragment页面切换的时候,RecyclerView抢占焦点的问题已经解决,特别 感谢Jianqiu,他的博客地址:http://niorgai.github.io/

在 ViewPagerActivity 里面的 Fragment的 代码中加入以下代码,可以阻止 RecyclerView 的子 View 获得焦点,从而阻止 RecyclerView 抢占位置。

// 是为了确保mNoHorizontalScrollView他的子孙不能获得焦点

mNoHorizontalScrollView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);

详细代码见项目中的ListFragement

个人疑点

借鉴于解决Activity的方法,目前我还没有找到一个方法是在Fragemnt界面完全绘制完毕以后回调的方法,如果大家知道怎样处理的 话,欢迎大家提出来


自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

img
img

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
img

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

链图片转存中…(img-3XBvKCbw-1711588369346)]

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V:vip204888 备注Android获取(资料价值较高,非无偿)
[外链图片转存中…(img-6TYFB3U6-1711588369346)]

最后

这里我特地整理了一份《Android开发核心知识点笔记》,里面就包含了自定义View相关的内容

[外链图片转存中…(img-E0DMpZAh-1711588369346)]

除了这份笔记,还给大家分享 Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这几块的内容。非常适合近期有面试和想在技术道路上继续精进的朋友。

[外链图片转存中…(img-oWH3xyBM-1711588369347)]

分享上面这些资源,希望可以帮助到大家提升进阶,如果你觉得还算有用的话,不妨把它们推荐给你的朋友~

喜欢本文的话,给我点个小赞、评论区留言或者转发支持一下呗~

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值