【问题标题】:Delegate touch event from ScrollView to vertical ViewPager将 ScrollView 中的触摸事件委托给垂直 ViewPager
【发布时间】:2018-03-12 15:24:04
【问题描述】:

我有一个VerticalViewPager,其中每个子片段都包含类似

的内容
<MyScrollView>
    <LinearLayout orientation=vertical>
        ...
        ...
    </LinearLayout>
</MyScrollView>

MyScrollView 滚动到底部时,我希望VerticalViewPager 启动并发挥其魔力。同样,如果我将MyScrollView 滚动到最顶部。

我已经尝试了一堆拦截ScrollView 中的触摸事件,以某种方式将MotionEvent 发送到VerticalViewPager,但没有任何运气。

这显然行不通,但为了让对话继续进行,我目前在MyScrollView 中执行以下操作

override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
    return super.onInterceptTouchEvent(ev) && atBottom
}

override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
    atBottom = getChildAt(0).bottom <= (height + scrollY)
    super.onScrollChanged(l, t, oldl, oldt)
}

这工作不稳定且不值得信赖,而且根本不是。非常感谢任何输入!

【问题讨论】:

  • 根据您的逻辑,您是否尝试过在 ScrollView 中从 onTouch 侦听器返回 false?我认为返回 false 将向 SDK 发出您不希望滚动视图处理触摸事件的信号,并且它可能会被进一步传播。
  • 您在ScrollView 内有一个ScrollViewMyScrollViewVerticalScrollView 内)。确保MyScrollViewNestedScrollView 的一个实例。
  • @azizbekian 谢谢,但我也试过了。不会改变任何东西。
  • VerticalScrollView是自己写的,还是库?如果您使用的是库,能否添加指向它的链接,或者告诉我们它的名称?
  • @cjurjiu 感谢您的评论。但是不,没有图书馆。并且要明确一点——MyScrollView 只是一个自定义的NestedScrollView,让我更容易编写触摸拦截代码。但也许你想问一下VerticalViewPager?这就像任何谷歌点击会告诉你做的一样。例如。 android.googlesource.com/platform/packages/apps/DeskClock/+/…

标签: android android-viewpager kotlin android-scrollview


【解决方案1】:

解决方案可能与这里的这个非常相似: (原答案是针对webview,但逻辑是一样的)

Scroll webview horizontally inside a ViewPager

【讨论】:

  • 感谢您的回答。也许我做错了什么,但我没有使用这种方法。也许它与从水平到垂直的事情有关。
  • 是的 - 你需要捕捉指针移动事件,了解运动(垂直/水平)并将事件传递给负责的组件。这正是我之前的回答中所描述的。
【解决方案2】:

我认为让 ScrollView 指示 ViewPager 何时启动的想法非常好,它已经使用 requestDisallowInterceptTouchEvent 来防止 ViewPager 拦截滚动。您需要的是在您指定的情况下使用相同的方法允许拦截,即当 ScrollView 到达顶部或底部时。

因此,如果 ScrollView 处理了 ACTION_MOVE 触摸事件,但产生的滚动偏移量没有改变,我们可以假设我们已经滚动到顶部或底部(并且现在正在过度滚动)。可能有更好的方法来检测过度滚动,因此请以此为起点进行实验:

//MyScrollView.java

@Override
public boolean onTouchEvent(MotionEvent ev) {
    boolean handled = super.onTouchEvent(ev);
    if (handled && ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
        int y = getScrollY();
        boolean moved = y != prevY;
        prevY = y;
        // This is the important part to allow the parent to intercept
        if (!moved) {
            ViewParent parent = getParent();
            if (parent != null) {
                parent.requestDisallowInterceptTouchEvent(false);
            }
        }
        // Also important to re
        return moved;
    }
    return handled;
}

编辑 - 我快速玩了一下,觉得这很好用

public class MyScrollView extends ScrollView {

    private boolean clampedY;

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

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

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = super.onTouchEvent(ev);
        if (handled) {
            ViewParent parent = getParent();
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_MOVE:
                    if (clampedY && parent != null) {
                        parent.requestDisallowInterceptTouchEvent(false);
                    }
                    break;
                case MotionEvent.ACTION_DOWN:
                    clampedY = false;
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    break;
            }
        }
        return handled;
    }

    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        this.clampedY = clampedY;
    }
}

编辑 2 - 如果有任何滚动,这将保留触摸的所有权,因此需要单独的触摸来切换页面

public class MyScrollView extends ScrollView {

    private boolean scrolled;

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

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

    public MyScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = super.onTouchEvent(ev);
        if (handled) {
            ViewParent parent = getParent();
            switch (ev.getActionMasked()) {
                case MotionEvent.ACTION_MOVE:
                    if (!scrolled && parent != null) {
                        parent.requestDisallowInterceptTouchEvent(false);
                    }
                    break;
                case MotionEvent.ACTION_DOWN:
                    scrolled = false;
                    if (parent != null) {
                        parent.requestDisallowInterceptTouchEvent(true);
                    }
                    break;
            }
        }
        return handled;
    }

    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        if (t != oldt) {
            scrolled = true;
        }
    }
}

【讨论】:

  • 感谢您的宝贵时间!我没有让它与我的VerticalViewPager 完美配合,它本质上是android.googlesource.com/platform/packages/apps/DeskClock/+/…。你是否?如果MyScrollView 滚动到底部,那就太好了,并且您可以进行新的触摸以进一步滚动。但是当你继续直接进入过度滚动时,会有一个很大的跳跃。你看到同样的行为吗?
  • 就好像它认为我在到达终点之前滚动的距离应该是过度滚动距离的一部分。
  • 我使用了相同的垂直视图寻呼机并没有注意到这种行为,虽然我使用的是模拟器所以可能没有很好的感觉,它也可能取决于滚动的大小视图是,我刚刚将 50 个文本视图放入其中
  • 获得您想要的行为应该非常简单,需要新的触摸来滚动页面。在这种情况下,如果自上次 ACTION_DOWN 以来有任何滚动,您希望保留触摸事件,因此可以在 ACTION_MOVE 案例中添加额外条件
  • 再次感谢您的时间和精力。您的“编辑 2”存在误解(顺便说一句,效果很好,但不是我的意思)。我不想要求新的卷轴进入过度卷轴。只是使用“编辑 1”,过度滚动可能会搞砸。查看此示例视频youtu.be/JJIG13UwCjc。它显示了它是如何奇怪地跳跃的。而且,它的错误在于有时您必须多次拖动(几乎总是(如果不总是)当您向下拖动时,以便在视图层次结构中向上移动)。我很想给你赏金,但目前还不能在真正的应用程序中使用它。
猜你喜欢
  • 1970-01-01
  • 2023-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-05
  • 1970-01-01
相关资源
最近更新 更多