如何使用手柄在Android中缩放视图?

4
我正在尝试在Android中实现一个处理程序来缩放视图。我想用一个手指就能调整图片大小,而不是使用多点触控。
以下是我的活动代码。我感觉我已经非常接近了,但有五件事情无法正常工作。
  1. 缩放比例不对。它增长的速度比应该快得多。已解决 感谢@Salauyou
  2. 视图只会增大,而不会缩小。 已解决 感谢@Salauyou
  3. 处理程序视图不随图像移动。 已解决 感谢@Salauyou
  4. 缩放开始非常小 已解决 感谢@Salauyou
  5. 处理程序不能完全跟随您的手指移动。
我正在寻找任何能够实现这种功能的帮助。无论是库还是有人可以帮助我改进我已经拥有的代码。我已经找到了一个可帮助缩放图像的多点触控库(https://github.com/brk3/android-multitouch-controller),但我能够学到的唯一指针是如何实现缩放增加。这必须通过使用两个点并找到它们之间的距离来完成。
我的Java活动:
public class MainActivity extends Activity {
    ImageView imageView;
    ImageView dragHandle;
    RelativeLayout layout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        imageView = (ImageView) findViewById(R.id.imageView1);
        imageView.setBackgroundColor(Color.MAGENTA);
        dragHandle = (ImageView) findViewById(R.id.imageView2);
        dragHandle.setBackgroundColor(Color.CYAN);
        layout = (RelativeLayout) findViewById(R.id.relativeLayout2);
        layout.setBackgroundColor(Color.YELLOW);
        setUpResize();

    }

    public void setUpResize() {
        dragHandle.setOnTouchListener(new View.OnTouchListener() {

            int[] touchPoint = new int[2];
            int[] centerOfImage = new int[2];

            double originalDistance = 0;
            double modifiedDistance = 0;

            float originalScale = 0;
            float modifiedScale = 0;

            public boolean onTouch(View v, MotionEvent motionEvent) {

                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {

                    centerOfImage[0] = (int) (imageView.getX() + imageView.getWidth() / 2);
                    centerOfImage[1] = (int) (imageView.getY() + imageView.getHeight() / 2);

                    touchPoint[0] = (int) motionEvent.getRawX();
                    touchPoint[1] = (int) motionEvent.getRawY();

                    int[] p = new int[2];
                    p[0] = touchPoint[0] - centerOfImage[0];
                    p[1] = touchPoint[1] - centerOfImage[1];

                    originalDistance = (float) Math.sqrt(p[0] * p[0] + p[1] * p[1]);
                    originalScale = imageView.getScaleX();

                } else if (motionEvent.getAction() == MotionEvent.ACTION_MOVE) {
                    touchPoint[0] = (int) motionEvent.getRawX();
                    touchPoint[1] = (int) motionEvent.getRawY();

                    int[] p = new int[2];
                    p[0] = (touchPoint[0] + p[0] - centerOfImage[0]);
                    p[1] = (touchPoint[1] + p[1] - centerOfImage[1]);
                    modifiedDistance = Math.hypot(touchPoint[0] - centerOfImage[0], touchPoint[1] - centerOfImage[1]);

                    Log.e("resize", "original " + imageView.getWidth() + " modified: " + imageView.getHeight());
                    modifiedScale = (float) (modifiedDistance / originalDistance * originalScale);

                    imageView.setScaleX(modifiedScale);
                    imageView.setScaleY(modifiedScale);

                    dragHandle.setX(centerOfImage[0] + imageView.getWidth()/2 * modifiedScale);
                    dragHandle.setY(centerOfImage[1] + imageView.getHeight()/2 * modifiedScale);

                } else if (motionEvent.getAction() == MotionEvent.ACTION_UP) {

                }
                return true;
            }
        });
    }
}

我的xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <RelativeLayout
        android:id="@+id/relativeLayout2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_centerInParent="true" >

        <ImageView
            android:id="@+id/imageView1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_centerVertical="true"
            android:src="@drawable/ic_launcher" />

        <ImageView
            android:id="@+id/imageView2"
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_below="@+id/imageView1"
            android:layout_toRightOf="@+id/imageView1"
            android:src="@drawable/dragArrow" />

    </RelativeLayout>

</RelativeLayout>

enter image description here


为什么不使用http://developer.android.com/reference/android/widget/ZoomButtonsController.html? - pskink
“处理视图不随图像移动”,因为它不应该移动。您的大图像被视为容器的原始大小,因为 setScaleX() 和其他变换方法仅影响视图的绘制,而不是其在布局中的位置。 - Alex Salauyou
所以规模不会改变大小?@Salauyou? - user2676468
@user2676468,缩放、旋转、倾斜、平移和其他矩阵变换是以绘图为单位而非布局为单位改变大小的。这就是为什么你的小图像不会跟随大图像的角落移动。 - Alex Salauyou
那么如果我想改变它的大小,我需要使用imageView.setWidth()和imageView.setHeight()吗? - user2676468
显示剩余13条评论
1个回答

2
所以......主要问题在于MotionEventgetRawX()getRawY()方法提供绝对屏幕坐标,而getX()getY()提供布局坐标。因此,当获取触摸坐标时,我们应该相对于布局重新计算它们,因为进度条和状态栏的高度会导致坐标不同。请注意保留HTML标签。
dragHandle.setOnTouchListener(new View.OnTouchListener() {

    float centerX, centerY, startR, startScale, startX, startY;

    public boolean onTouch(View v, MotionEvent e) {

        if (e.getAction() == MotionEvent.ACTION_DOWN) {

            // calculate center of image
            centerX = (imageView.getLeft() + imageView.getRight()) / 2f;
            centerY = (imageView.getTop() + imageView.getBottom()) / 2f;

            // recalculate coordinates of starting point
            startX = e.getRawX() - dragHandle.getX() + centerX;
            startY = e.getRawY() - dragHandle.getY() + centerY; 

            // get starting distance and scale
            startR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
            startScale = imageView.getScaleX();

        } else if (e.getAction() == MotionEvent.ACTION_MOVE) {

            // calculate new distance
            float newR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);

            // set new scale
            float newScale = newR / startR * startScale;
            imageView.setScaleX(newScale);
            imageView.setScaleY(newScale);

            // move handler image
            dragHandle.setX(centerX + imageView.getWidth()/2f * newScale);
            dragHandle.setY(centerY + imageView.getHeight()/2f * newScale);

        } else if (e.getAction() == MotionEvent.ACTION_UP) {

        }
        return true;
    }
});

另外,我用库方法替换了直角三角形的斜边计算,并将所有坐标声明为浮点型,以避免不必要的强制转换。


这个很好用。现在我需要旋转和缩放,但是当我将两者结合起来时出现了问题。http://stackoverflow.com/questions/23115472/rotate-and-scale-a-view-based-on-one-handle-in-android - user2676468
你想通过拖动一个处理程序同时执行两个操作吗? - Alex Salauyou
是的。我曾经在iOS上看到过一个可以这样做的应用程序。它运行得非常好,但我不记得它的名字了。我希望它能像捏合缩放一样工作。如果您捏合缩放,同时也可以旋转。所以是的,可以同时进行两个操作。 - user2676468
所以你想要实现双指捏合缩放/旋转或单指拖动缩放/旋转(使用处理器图标)?方法和解决方案将不同。 - Alex Salauyou

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接