0

我正在使用 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 放在 a 中ViewPager,并在缩放的图像周围滚动,但当我到达图像的边缘时,也可以在 ViewPager 中向右/向左滑动。

但我想要的是嵌套在常规 ViewPager 中的垂直 ViewPager。垂直 ViewPager 是根据这个SO 帖子建模的:

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));
    }

}

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

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

小改动,效果很好。

当垂直 ViewPager 的内容是自定义的 TouchImageViews 时,问题就来了。向右/向左滑动仍然很有效,但垂直滑动仅在图像的右边缘与设备屏幕的右侧对齐时才有效。这是由于canScrollHorizontallyTouchImageView 类中的覆盖。

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;
}

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

为什么canScrollVertically从来不叫?

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

提前感谢您的帮助。有谁认识迈克·奥尔蒂斯!?

4

2 回答 2

0

所以我想出的解决方案如下。

在 中TouchImageView,Mike 有一个自定义类

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 足够大(即垂直拖动/滑动)

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

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

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

float y = m[Matrix.MTRANS_Y];

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

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

于 2015-11-18T21:06:34.070 回答
0

就我而言,我正在检查我的 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

于 2019-09-10T15:46:30.050 回答