Android,canScrollVertically 从未调用过 ImageView

Android, canScrollVertically never called for ImageView

我正在使用 Mike Ortiz 的 TouchImageView

其中,canScrollHorizontally被覆盖:

@Override
public boolean canScrollHorizontally(int direction) {
    matrix.getValues(m);
    float x = m[Matrix.MTRANS_X];
    float y = m[Matrix.MTRANS_Y];

    if (getImageWidth() < viewWidth) {
        return false;

    } else if (x >= -1 && direction < 0) {
        return false;

    } else if (Math.abs(x) + viewWidth + 1 >= getImageWidth() && direction > 0) {
        return false;
    }

    return true;
}

这按预期工作。我可以将 TouchImageView 放在 ViewPager 中,然后在缩放的图像周围滚动,但当我到达图像的边缘时,也可以通过 ViewPager 滑动 right/left。

但我想要的是嵌套在常规 ViewPager 中的垂直 ViewPager。垂直 ViewPager 仿照 this SO post:

public class VerticalViewPager extends ViewPager {

    public VerticalViewPager(Context context) {
        super(context);
        init();
    }

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

    private void init() {
        // The majority of the magic happens here
        setPageTransformer(true, new VerticalPageTransformer());
        // The easiest way to get rid of the overscroll drawing that happens on the left and right
        setOverScrollMode(OVER_SCROLL_NEVER);
    }

    private class VerticalPageTransformer implements ViewPager.PageTransformer {

        @Override
        public void transformPage(View view, float position) {

            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);

            } else if (position <= 1) { // [-1,1]
                view.setAlpha(1);

                // Counteract the default slide transition
                view.setTranslationX(view.getWidth() * -position);

                //set Y position to swipe in from top
                float yPosition = position * view.getHeight();
                view.setTranslationY(yPosition);

            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }

    /**
     * Swaps the X and Y coordinates of your touch event.
     */
    private MotionEvent swapXY(MotionEvent ev) {
        float width = getWidth();
        float height = getHeight();

        float newX = (ev.getY() / height) * width;
        float newY = (ev.getX() / width) * height;

        ev.setLocation(newX, newY);

        return ev;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev){
        boolean intercepted = super.onInterceptTouchEvent(swapXY(ev));
        swapXY(ev); // return touch coordinates to original reference frame for any child views
        return intercepted;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        return super.onTouchEvent(swapXY(ev));
    }

}

对于常规的 ImageView,效果很好。我可以在水平 ViewPager 中滑动 left/right,同时还可以根据需要在嵌套的 ViewPager 中垂直滚动。对垂直 ViewPager 的一项修改(以允许从垂直 ViewPager 内的任何位置进行水平滚动)是:

@Override
public boolean canScrollHorizontally(int direction) {
    return false;
}

小改动,效果很好。

当垂直ViewPager 的内容是自定义的TouchImageView 时,问题就来了。 right/left 滑动仍然有效,但垂直滑动仅在图像右边缘与设备屏幕右侧对齐时有效。这是由于 TouchImageView class.

中的 canScrollHorizontally 覆盖

int direction 参数不考虑 x 或 y。正向 y 滑动与正向 x 滑动无法区分。

我想做的是覆盖 canScrollVertically 方法,以检测何时发生垂直滑动,但从未调用该方法。即使只是像这样的快速测试:

@Override
public boolean canScrollVertically(int direction) {
    matrix.getValues(m);

    Toast.makeText(context, "vertical", Toast.LENGTH_SHORT).show();


    return true;
}

从未被调用。在 canScrollHorizontally 方法中放置相同的 Toast 消息会在每次滑动(水平或垂直)时显示消息。

为什么 canScrollVertically 从未被调用过?

我想检测图像何时在视图中触底,以及用户何时向上滑动。当满足这两个条件时,我可以从 canScrollHorizontally return false 并允许垂直 ViewPager 正确滚动。

在此先感谢您的帮助。有人认识迈克·奥尔蒂斯吗!?

所以我想出了以下解决方案。

TouchImageView 内,Mike 有一个习惯 class

private class PrivateOnTouchListener implements OnTouchListener

其中包含以下块:

    case MotionEvent.ACTION_MOVE:
        if (state == State.DRAG) {
            float deltaX = curr.x - last.x;
            float deltaY = curr.y - last.y;

            ... etc ... 
        }
        break;

我所做的是设置全局 boolean verticalDrag;。使用上面的块,我检查以确保 deltaY 比 deltaX 足够大(即垂直 drag/swipe)

if(Math.abs(deltaY) > (Math.abs(deltaX) * sensitivity)){
    verticalDrag = true;
}else{
    verticalDrag = false;
}

敏感度由编码人员自行决定分配。真正的垂直滑动通常会有几个像素 left/right 偏差 - 很难只在一个方向或另一个方向滑动。因此,灵敏度可能非常高(即 5 到 10,或更高,具体取决于测试),并且它仍然会触发。

回到 canScrollHorizontally 方法中,我只是添加:

float y = m[Matrix.MTRANS_Y];

else if(Math.abs(y) + viewHeight + 1 >= getImageHeight() && verticalDrag){
    return false;
}

允许垂直 ViewPager 根据需要滑动。

在我的例子中,我正在检查我的 scrollView(其中包含文本)是否可以垂直滚动 当 activity 初始化时。在手机上,它会滚动,但在平板电脑上则不能。 canScrollVertically 返回给我 不正确的值 因为它还不能确定。我通过在 OnGlobalLayoutListener.

中调用它解决了这个问题

(科特林)

override fun onActivityCreated(savedInstanceState: Bundle?) {
    super.onActivityCreated(savedInstanceState)

        // Must use onGlobalLayout or else canScrollVertically will not return the correct value because the layout hasn't been made yet
        scrollView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {

            override fun onGlobalLayout() {
                // If the scrollView can scroll, disable the accept menu item button
                if ( scrollView.canScrollVertically(1) || scrollView.canScrollVertically(-1) )
                    acceptMenuItem?.isEnabled = false

                // Remove itself after onGlobalLayout is first called or else it would be called about a million times per second
                scrollView.viewTreeObserver.removeOnGlobalLayoutListener(this)
            }
        })
  }

我的用例是显示使用条款。在用户滚动到底部之前,我不希望启用接受按钮。我知道这已经晚了,但我希望这能解决一些关于 canScrollVertically

的困惑