VerticalGridView适配触摸屏踩坑,触摸滑动时位置重置/闪烁问题

VerticalGridView是什么?

VerticalGridView是安卓leanback库的列表组件,用于支持使用遥控器(按键事件)浏览列表。

它与RecyclerView的继承关系是:VerticalGridView→BaseGridView→RecyclerView

首先我想吐槽一下leanback的BaseGridView相关组件,耦合度较高,并且不允许开发者替换其LayoutManager,且该GridLayoutManager是final修饰,不允许继承。

这样出了bug都没法给他改。

异常现象

如图所示的VerticalGridView中,使用手指触屏滑动时,会出现滑出一定距离就被重置回起点的问题,永远无法滑动到界面底部。

原因

起初是怀疑和布局刷新有关,因为在bindView中不给ImageView设置图片就没有该问题了。

后来看了下GridLayoutManager的代码,log日志打开后发现,每当触控下滑到一定距离,就会触发onLayoutChildren()方法,在这个方法中,会调用focusToViewInLayout()方法,该方法是用于,当用户使用遥控器上下左右移动焦点时,将VerticalGridView滚动至以焦点View为中心的位置。

这个逻辑在使用遥控器时是没有问题的,是需要的,但是在进行触摸事件时,却是会导致问题的。

因为触摸滑动过程中是没有产生焦点,没有移动焦点的,此时如果按照原逻辑,就会出现滑动时被无限重置回焦点位置的问题。

解决方案

问题原因是GridLayoutManager在onLayoutChildren时,会将视图定位到焦点所在的view,导致无法往下滑动。

解决方案就是区分出当前的onLayoutChildren方法是由于按键事件导致的,还是触摸事件导致的,

如果是触摸事件导致的,那么就不需要调用focusToViewInLayout()方法。

但是此处代码无法获取事件的来源信息,我们只能自行监听触摸事件,在用户触摸中不允许focusToViewInLayout()方法的调用即可。

代码修改

我们需要将该组件抽取到自己的项目中,才能修改里面代码。

    implementation 'androidx.leanback:leanback-grid:1.0.0-alpha03'

先把上面库的文件copy到自己项目中。

然后在GridLayoutManager中声明变量

boolean isDragging = false;

Handler mDragHandler = new Handler();

初始化时监听触摸事件

@SuppressLint("WrongConstant")
GridLayoutManager(@Nullable BaseGridView baseGridView) {
    mBaseGridView = baseGridView;
    mChildVisibility = -1;
    // disable prefetch by default, prefetch causes regression on low power chipset
    setItemPrefetchEnabled(false);
    setOnTouchInterceptListener();
}

void setGridView(BaseGridView baseGridView) {
    mBaseGridView = baseGridView;
    mGrid = null;
    setOnTouchInterceptListener();
}

void setOnTouchInterceptListener(){
    mBaseGridView.setOnTouchInterceptListener(event -> {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            Log.i(TAG, "mBaseGridView OnTouchInterceptListener: isDragging = true");
            mDragHandler.removeCallbacksAndMessages(null);
            isDragging = true;
        }
        if (event.getAction() == MotionEvent.ACTION_UP) {
            mDragHandler.postDelayed(() -> {
                Log.i(TAG, "mBaseGridView OnTouchInterceptListener: isDragging = false");
                isDragging = false;
            }, 1000);
        }
        return false;
    });
}

在焦点定位的方法中,判断该变量进行拦截

private void focusToViewInLayout(boolean hadFocus, boolean alignToView, int extraDelta,
        int extraDeltaSecondary) {
    if (isDragging) {
        // 说明是触摸事件导致的重新布局,此时并不需要将视图定位到焦点View
        return;
    }
    ...
}

除此之外,GridLayoutManager在触摸屏环境下还存在其他问题,比如你设置了固定的方向,但VerticalGridView仍然可以水平方向拖动。

该问题的解决方法:修改canScrollVertically和canScrollHorizontally方法,去掉后面的条件,只判断方向即可。

可以看出leanback库中的这些组件是完全没有考虑触控环境下的使用的,如果想要在触屏下使用,必须自行修改。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值