Android - 滚动 RecyclerView 后,其中的项目无法被点击

30

我刚升级到API 26和支持库26.0.2。但是发现我的RecyclerView在滚动后右侧的条目不能立即点击,需要等待一秒钟左右才行。即使RecyclerView没有滚动(例如已经滑动到顶部),也是如此。

当我降级到支持库25.4.0时,一切都恢复正常了。 关键是我的RecyclerView在一个CoordinatorLayout中,并且在AppBarLayoutToolbar上有一个SCROLL_FLAG_SCROLL标志。如果我不使用这个标志,那么这个问题将消失。所以我认为这是支持库26的一个隐藏行为更改。

我尝试向CoordinatorLayout添加focusable="false",但仍然没能解决问题。

是否有办法禁用此行为?因为需要双击才能触发单击事件真的很烦人。

 <android.support.design.widget.CoordinatorLayout
        android:id="@+id/coordinateLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
            android:id="@+id/fragmentAppBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:elevation="0dp"
            android:background="@null">
        <include
                android:id="@+id/dynamicActionBarHolder"
                layout="@layout/dynamic_action_bar"/>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/pullToRefreshMailRecycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <android.support.v7.widget.RecyclerView
                android:id="@+id/mailRecyclerView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

    </android.support.v4.widget.SwipeRefreshLayout>

</android.support.design.widget.CoordinatorLayout>

编辑

我认为问题在于RecyclerViewscrollState。当它停止滚动时,它不会立刻更改为SCROLL_STATE_IDLE。查看RecyclerView的源代码,我发现有一个ViewFlinger控制滚动状态。当我向下快速滑动到顶部时,它不会立即调用setScrollState(SCROLL_STATE_IDLE),而是等待一段时间才触发此方法。我滑动得越快,等待的时间就越长。就像RecyclerView仍在后台滚动一样。因为当RecyclerView接触顶部停止滚动时,scroller.isFinished()并不会立即返回true。也许这是RecyclerViewCoordinatorLayout中的一个bug。


你能删除 SwipeRefreshLayout 中的 app:layout_behavior 并再试一次吗? - Aytek Sökmen
滚动后仍无法点击。唯一的解决方法是将标志设置为0,setScrollFlags(0) - Kimi Chiu
3个回答

42

找到了一种强制滚动状态为空闲的方法。 等待谷歌修复此错误。

@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
    boolean requestCancelDisallowInterceptTouchEvent = getScrollState() == SCROLL_STATE_SETTLING;
    boolean consumed = super.onInterceptTouchEvent(event);
    final int action = event.getActionMasked();

    switch (action) {
        case MotionEvent.ACTION_DOWN:
            if( requestCancelDisallowInterceptTouchEvent ){
                getParent().requestDisallowInterceptTouchEvent(false);

                // only if it touched the top or the bottom. Thanks to @Sergey's answer.
                if (!canScrollVertically(-1) || !canScrollVertically(1)) {
                    // stop scroll to enable child view to get the touch event
                    stopScroll();
                    // do not consume the event
                    return false;
                }
            }
            break;
    }

    return consumed;
}

编辑

该问题已在支持库27.0.1中修复。

https://developer.android.com/topic/libraries/support-library/revisions.html#27-0-1

用户滚动后,他们无法单击RecyclerView中的项目。(AOSP问题66996774)

更新于2017年11月17日。

一些用户报告说这个问题在支持库27.0.1中没有解决。 您可以在此处找到问题跟踪器。 https://issuetracker.google.com/issues/66996774

因此,您可以选择使用此官方解决方法。 https://gist.github.com/chrisbanes/8391b5adb9ee42180893300850ed02f2

或者使用这里的一个。


1
问题已在支持库27.0.1中得到修复。但我实际上并没有解决:P,还没有时间进行解决方法(Workaround)。27.0.2。 - EpicPandaForce
@EpicPandaForce 我们都知道 Google 不擅长修复错误。我仍在等待他们修复导致我的应用程序每天崩溃200次的铬缺陷。而且已经过去了将近一年。 - Kimi Chiu
@nyconing 完全同意你的看法。这总是一场灾难。 - Kimi Chiu
我没有使用Coordinate layout和AppBarLayout,而是在Relativelayout中放置了一个水平的recylerview。我的要求是,由于recylerview需要自动滚动,需要找到屏幕中心的项目,并且在滚动时需要点击这些项目。我已经实现了自动滚动。但是,无法点击项目。我在这里提出了一个问题:https://stackoverflow.com/questions/51959211/item-click-is-not-working-while-auto-scroll-in-recyclerview - Manikandan
问题在androidX中仍然存在。 - Usman Rana
显示剩余3条评论

5
非常感谢您的提问和回答!这为我节省了很多时间。很抱歉将此发布为答案。我的声望不够,无法发表评论。
我也注意到了这个问题,但作为一个新的Android开发者,我没有意识到这是新支持库内部的错误。
我想建议的是添加这个检查:
if( requestCancelDisallowInterceptTouchEvent ){
    if (!canScrollVertically(-1) || !canScrollVertically(1)) {
        ...
    }
}

这将确保在RecyclerView实际滚动时,我们不会点击任何项目。

据我所知,这是一种预期行为。但是,您的答案帮助了我解决了这个问题


你说得对。我正在使用LayoutManagerfindFirstCompletelyVisibleItemPositionfindLastCompletelyVisibleItemPosition来检查我的RecyclerView是否已经滚动到顶部或底部。我不知道还有一个canScrollVertically方法。这比使用LayoutManager要更加优雅。 - Kimi Chiu
嗨@KimiChiu!我只能对我的答案进行评论:) 嘿!看起来他们已经在修复版本27.0.1中修复了这个错误。我敢打赌这是你的报告https://issuetracker.google.com/issues/66996774 :)) 至少电子邮件以“ki...@gmail.com”开头。 - Sergey
看起来问题在27.0.1版本仍然存在。我已根据您的建议编辑了我的答案,以防其他人遇到相同的问题。 - Kimi Chiu

0

我找到了一份源代码,解决了这个问题。这个问题是由于AppBarLayout的行为(AppBarLayout.Behavior)引起的。这个源代码提供了一个扩展或自定义AppBarLayout行为的方法,并将其介绍了在xml直接使用或在java中使用的方法。我只能简要地解释一下,因为这个源代码有许可证,你也必须阅读它。请查看此链接中的解决方案: https://gist.github.com/chrisbanes/8391b5adb9ee42180893300850ed02f2


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接