Android:垂直 Recyclerview 内的水平 Recyclerview

标签 android android-layout android-recyclerview touch-event motionevent

这似乎是 android 中相当普遍的模式。我正在尝试创建类似于 Facebook 展示广告的方式:

Facebook ads

您可以看到它们有一个外部垂直回收器 View 和一个内部水平回收器 View 作为外部垂直回收器 View 适配器中的一项。

我遵循了 Google 的“在 ViewGroup 中管理触摸事件”指南 - http://developer.android.com/training/gestures/viewgroup.html因为 Recyclerview 扩展了 ViewGroup 并且那里的代码与我想要做的相似。

我对其进行了一些定制,以便它检测 Y 轴上的移动而不是 X 轴上的移动,并将其应用于外部垂直回收 View 。

/**
 * Created by Simon on 10/11/2015.
 *///This is the recyclerview that would allow all vertical scrolls
public class VerticallyScrollRecyclerView extends RecyclerView {


    public VerticallyScrollRecyclerView(Context context) {
        super(context);
    }

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }


    ViewConfiguration vc = ViewConfiguration.get(this.getContext());
    private int mTouchSlop = vc.getScaledTouchSlop();
    private boolean mIsScrolling;
    private float startY;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = MotionEventCompat.getActionMasked(ev);
        // Always handle the case of the touch gesture being complete.
        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
            // Release the scroll.
            mIsScrolling = false;
            startY = ev.getY();
            return false; // Do not intercept touch event, let the child handle it
        }
        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                if (mIsScrolling) {
                    // We're currently scrolling, so yes, intercept the
                    // touch event!
                    return true;
                }

                // If the user has dragged her finger horizontally more than
                // the touch slop, start the scroll

                // left as an exercise for the reader
                final float yDiff = calculateDistanceY(ev.getY());
                Log.e("yDiff ", ""+yDiff);
                // Touch slop should be calculated using ViewConfiguration
                // constants.
                if (yDiff > mTouchSlop) {
                    // Start scrolling!
                    Log.e("Scroll", "we are scrolling vertically");
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return false;
    }

    private float calculateDistanceY(float endY) {
        return startY - endY;
    }

}

我的 xml 布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_recyclerview_holder">

    <com.example.simon.customshapes.VerticallyScrollRecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
/>

</RelativeLayout>

当我尝试拖动内部水平 recyclerview(希望 touchevent 被外部垂直 recyclerview 拦截)时,外部 recyclerview 根本不会垂直滚动。

它也给我一个错误提示:

错误处理滚动;找不到 id -1 的指针索引。是否跳过了任何 MotionEvents?

有谁知道如何让它正常工作?

最佳答案

我遇到了类似的问题,网上没找到解决方法。在我的应用程序中,我有自定义交互元素列表,您可以通过水平滑动(在这种情况下滚动应该被锁定)与之交互,但是当您垂直滑动时它应该滚动。 根据您的评论,您已经解决了您的问题,但是对于那些与我的问题相似并且其研究导致他们来到这里的人,我将发布我的解决方案。 我仔细查看了 RecyclerView 的内部结构,下面是我的发现。 “处理滚动时出错;未找到 id -1 的指针索引。是否跳过了任何 MotionEvents?” - 问题是当 ACTION_DOWN 时,用于获取 index 的变量 mScrollPointerIdonTouchEvent 中设置发生。在我的特殊情况下,当 ACTION_DOWN 发生时,我从 onInterceptTouchEvent 返回 false,因为那时我不知道天气用户想要滚动或与列表元素交互。在这种情况下,onTouchEvent 不会被调用。后来,当我发现用户想要与元素交互时,我从 onInterceptTouchEvent 返回 false 并调用 onTouchEvent,但它无法处理滚动,因为 mScrollPointerId 未设置。这里的解决方法就是当ACTION_DOWN发生时,从onInterceptTouchEvent调用onTouchEvent

@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
    ...
    switch (action) {
        case MotionEvent.ACTION_DOWN: {
            onTouchEvent(e);
            ...
            break;
            }
        ...
        }
    ...
}

关于Android:垂直 Recyclerview 内的水平 Recyclerview,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33266233/

相关文章:

android - 使用 TextInputLayout 和 TextInputEditText 时删除多余的空间/填充/边距

android - 如何将触摸监听器添加到表面 View ?

java - RecyclerView 的动态部分标题使用当前日期时间

单击链接时,Android Webview 滚动到顶部

java - 将多个 RecyclerView 与一个 Adapter 类一起使用

android - 更新 VM 选项后无法在 mac 上启动 Android studio

java - Android 改造 Base64 @Body

Android SDK的layout_width和layout_height xml属性继承?

android.widget.Toolbar 无法转换为 android.support.v7.widget.Toolbar

android - 如何在布局的一角放置徽章图标