onScale和Canvas - 如何缩放图像后调整原点? [英] onScale and Canvas - how to adjust origin point after zooming image?

查看:376
本文介绍了onScale和Canvas - 如何缩放图像后调整原点?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个自定义组件<一个很简单的测试应用程序href="https://github.com/afarber/android-newbie/blob/master/TestScroll/src/de/afarber/testscroll/MyView.java"相对=nofollow> MyView.java - 它会显示一个可滚动的,可扩展的图像(重presenting一板一拼字游戏般的文字游戏):

I have a very simple test app with a custom component MyView.java - which displays a scrollable and scalable image (representing a board in a Scrabble-like word game):

1)在我的听众规模我调整比例系数:

1) In my scale listener I adjust the scale factor:

public boolean onScale(ScaleGestureDetector detector) {
    mScale *= detector.getScaleFactor();

    // XXX how to adjust mOffsetX and mOffsetY ? XXX

    constrainZoom();
    constrainOffsets();
    invalidate();
    return true;
}

2),然后在我的自定义组件的绘制方法,我申请的平移和缩放系数:

2) And then in the drawing method of my custom component I apply the translation and scale factor:

protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);

    canvas.translate(mOffsetX, mOffsetY);
    canvas.scale(mScale, mScale);
    gameBoard.setBounds(
        0, 
        0, 
        gameBoard.getIntrinsicWidth(),
        gameBoard.getIntrinsicHeight()
    );
    gameBoard.draw(canvas);  
}

不幸的是,有捏的手势缩放的时候似乎是一个小错误 -

Unfortunately, there seems to be a small bug when scaling with pinch gesture -

我可以看到,规模和边界是正确的尺寸放大后,但图像的偏移不是。

I can see, that the scale and boundaries are of correct size after zooming, but the offset of the image is not.

这个问题变得更糟,当捏手势的焦点是远远0,0点的画面。

The problem get worse, when the focus point of the pinch gesture is far from 0, 0 point of the screen.

这是一个有点难以用言语来形容的问题,但是当你检查出的从GitHub上我的测试项目你会马上看到它(你可以随时双击复位偏移和标度)。

It is a bit difficult to describe the problem in words, but when you check out my test project from GitHub you will see it immediately (and you can always double tap to reset the offset and the scale).

这可能是一个常见的​​问题来解决它的标准方式,但我一直没能找到它。

This is probably a common problem with a standard way to solve it, but I haven't been able to find it yet.

董事会图像 CC BY-SA由Denelson83 和code基于href="http://www.tiemenschut.com/support-fling-gesture-bounce-effect/" rel="nofollow">如何-支持一扔手势反弹效应博客。

The board image is CC BY-SA by Denelson83 and the code is based on the How-to support the fling gesture with a bounce effect blog.

推荐答案

这是使用该框架的滚动功能,而不是由我们自己实施,有几个原因的最佳实践。

It is a best practice to use the framework's scrolling feature, rather than implementing by our own, for several reasons.

  • Android将翻译画布。因此,我们不需要做最少的工作。

  • Android will translate the canvas. So we need no to do least work.

如果我们把我们的查看的ViewGroup 它的工作原理基于我们的查看的滚动位置,它会正常工作。

If we place our View in a ViewGroup which works based on our View's scroll position it will work as expected.

显示滚动条是容易的。

作为第二点的一个例子,如果我们把<一href="https://github.com/afarber/android-newbie/blob/q1/TestScroll/src/de/afarber/testscroll/MyView.java"相对=nofollow>在 ViewPager ViewPager 截取水平一扔MyView.java 并水平滚动所以 MyView的将不会收到这些事件。但是,如果我们使用的框架滚动功能一切顺利,你可以将 MyView的 ViewPager 无任何伤害。

As an example for second point, if we place MyView.java in a ViewPager, the ViewPager intercepts the horizontal fling and horizontal scroll so MyView will not receive these events. But if we use frameworks scrolling feature everything goes fine and you can place MyView in ViewPager without any harm.

下面是需要你的重要方法定义

Here are the important method definitions needed for you

public class MyView extends View {
    private ScaleGestureDetector scaleGestureDetector;
    private GestureDetector gestureDetector;
    private OverScroller scroller;
    private Drawable drawable;
    private float scale = 1;
    private float maxScale = 4;

    public MyView(Context context) {
      super(context);
      MyGestureListener listener = new MyGestureListener();
      scaleGestureDetector = new ScaleGestureDetector(context, listener);
      gestureDetector = new GestureDetector(context, listener);
      scroller = new OverScroller(context);
      drawable = new BitmapDrawable(getResources(),
            BitmapFactory.decodeResource(getResources(), R.drawable.flower));
    }

  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    drawable.setBounds(0, 0, w, h);
    super.onSizeChanged(w, h, oldw, oldh);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    canvas.save();
    canvas.scale(scale, scale);
    drawable.draw(canvas);
    canvas.restore();
  }

  @Override
  public boolean onTouchEvent(MotionEvent event) {
    return gestureDetector.onTouchEvent(event)
            || scaleGestureDetector.onTouchEvent(event);
  }

  public void computeScroll() {
    if (!scroller.isFinished()) {
        if (scroller.computeScrollOffset()) {
            int oldX = getScrollX();
            int oldY = getScrollY();
            int x = scroller.getCurrX();
            int y = scroller.getCurrY();
            if (oldX != x || oldY != y) {
                scrollTo(x, y);
            }
            // Keep on drawing until the animation has finished.
            invalidate();
        }
    }
  }

  private void scale(float scale, int fx, int fy) {
    this.scale *= scale;
    if (this.scale > maxScale) {
        scale *= maxScale / this.scale;
        this.scale = maxScale;
    } else if (this.scale < 1) {
        scale = 1;
        this.scale = 1;
    }
    int scX = (int) ((scale - 1) * fx);
    int scY = (int) ((scale - 1) * fy);
    scrollBy(scX, scY);
  }

  @Override
  public void scrollTo(int x, int y) {
    int maxX = getMaxOffsetX();
    int maxY = getMaxOffsetY();
    if (x < 0) {
        x = 0;
    } else if (x > maxX) {
        x = maxX;
    }

    if (y < 0) {
        y = 0;
    } else if (y > maxY) {
        y = maxY;
    }
    super.scrollTo(x, y);
  }

  private int getMaxOffsetX() {
    return (int) ((scale - 1) * getWidth());
  }

  private int getMaxOffsetY() {
    return (int) ((scale - 1) * getHeight());
  }
}

和所述gestureListener的实现如下。把这个作为MyView.java一个内部类。

And the gestureListener's implementation is given below. Place this as an inner class in MyView.java.

private class MyGestureListener extends SimpleOnGestureListener implements
        OnScaleGestureListener {
    boolean scaling = false;

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2,
            float distanceX, float distanceY) {
        if (!scaling) {
            scrollBy((int) distanceX, (int) distanceY);
            return true;
        }
        return false;
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) {
        if (!scaling) {
            scroller.fling(getScrollX(), getScrollY(), (int) -velocityX,
                    (int) -velocityY, 0, getMaxOffsetX(), 0,
                    getMaxOffsetY(), 50, 50);
            invalidate();
            return true;
        }
        return false;
    }

    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        scale(detector.getScaleFactor(), (int) detector.getFocusX()
                + getScrollX(), (int) detector.getFocusY() + getScrollY());
        return true;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        scaling = true;
        return true;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        scaling = false;
    }
}

我希望这将正常工作。

I hope this will work fine.

完整的项目是 Github上,这显示了如何添加MyView的在ViewPager以及如何启用滚动条

The complete project is in Github, which shows how to add MyView in ViewPager and how to enable scrollbars.

这篇关于onScale和Canvas - 如何缩放图像后调整原点?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆