简体   繁体   English

Android中基于一个手柄旋转和缩放视图

[英]Rotate and scale a view based on one handle in Android

I'm trying to rotate and scale a view based on one "drag" handle in Android.我正在尝试基于 Android 中的一个“拖动”手柄旋转和缩放视图。 With the layout rotating, and the view resizing, the end result should be that the drag handle follows the users finger where ever it moves.随着布局旋转和视图大小调整,最终结果应该是拖动手柄跟随用户手指移动。

This is based on combining these two questions:这是基于结合这两个问题:

  1. How to scale a view in android using a handle? 如何使用手柄在android中缩放视图?
  2. Rotating around two points in android not working 在android中围绕两点旋转不起作用

The only thing that seems wrong is the rotation code.唯一似乎错误的是旋转代码。

Here is my activity code:这是我的活动代码:

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() {

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

            float startAngle;
            float zeroAngle;
            int firstPointX;
            int firstPointY;

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

                    /*
                     * Rotate code
                     */

                    int[] locationOfLayout = new int[2];
                    int[] locationOfDrag = new int[2];

                    layout.getLocationOnScreen(locationOfLayout);
                    dragHandle.getLocationOnScreen(locationOfDrag);

                    firstPointX = locationOfLayout[0];
                    firstPointY = locationOfLayout[1];

                    float secondPointX = e.getRawX();
                    float secondPointY = e.getRawY();

                    zeroAngle = findRotation(firstPointX, firstPointY, secondPointX, secondPointY); // remember
                                                                                                    // "zero"
                                                                                                    // angle
                    startAngle = layout.getRotation(); // remember angle at
                                                        // which layout is
                                                        // rotated at the start

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

                    /*
                     * Rotate code
                     */

                    layout.setRotation(findRotation(firstPointX, firstPointY, e.getRawX(), e.getRawY()) - zeroAngle
                            + startAngle); // rotate relative to start and zero
                                            // angle

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

                }
                return true;
            }
        });
    }

    private float findRotation(float firstPointX, float firstPointY, float secondPointX, float secondPointY) {
        double delta_x = (secondPointX - firstPointX);
        double delta_y = (secondPointY - firstPointY);
        double radians = Math.atan2(delta_y, delta_x);
        return (float) Math.toDegrees(radians);
    }
}

xml: 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/meanicons" />

    </RelativeLayout>

</RelativeLayout>

In case you need to resize and rotate image simultaneously using one handler icon, some trigonometrical calculations should be performed.如果您需要使用一个处理程序图标同时调整大小和旋转图像,则应执行一些三角函数计算。

It is not so difficult to calculate angle by which image should be rotated, based on its initial angle and angle, by which the vector, that connects center of image and current finger position, is rotated.根据图像的初始角度和角度,计算图像应该旋转的角度并不困难,连接图像中心和当前手指位置的矢量旋转的角度。 A little bit more complicated task is to position the handler in a proper place of the screen to make it always connect the corner of main image.稍微复杂一点的任务是将处理程序放置在屏幕的适当位置,使其始终连接主图像的角落。

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

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

        public boolean onTouch(View v, MotionEvent e) {

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

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

                startX = e.getRawX() - dragHandle.getX() + centerX;
                startY = e.getRawY() - dragHandle.getY() + centerY; 

                startR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
                startA = (float) Math.toDegrees(Math.atan2(e.getRawY() - startY, e.getRawX() - startX));

                startScale = imageView.getScaleX();
                startRotation = imageView.getRotation();

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

                float newR = (float) Math.hypot(e.getRawX() - startX, e.getRawY() - startY);
                float newA = (float) Math.toDegrees(Math.atan2(e.getRawY() - startY, e.getRawX() - startX));
                float newScale = newR / startR * startScale;
                float newRotation = newA - startA + startRotation;

                imageView.setScaleX(newScale);
                imageView.setScaleY(newScale);
                imageView.setRotation(newRotation);


                // ----- this part takes some effort to understand... ------
                dragHandle.setX((float) (centerX + Math.hypot(imageView.getWidth(), imageView.getHeight())/2f * newScale 
                        * Math.cos(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))));

                dragHandle.setY((float) (centerY + Math.hypot(imageView.getWidth(), imageView.getHeight())/2f * newScale 
                        * Math.sin(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))));
                //-----------------------------------------------------------

                dragHandle.setPivotX(0);
                dragHandle.setPivotY(0);
                dragHandle.setRotation(newRotation);


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

            }
            return true;
        }
    });
}

So, what I'm doing?那么,我在做什么?

Math.hypot(imageView.getWidth(), imageView.getHeight()) / 2f * newScale

--this calculates half length of diagonal of the main image, ie distance between its center and corner point --这计算主图像对角线的一半长度,即它的中心和角点之间的距离

Math.atan2(imageView.getHeight(), imageView.getWidth())

--this is angle by which diagonal was rotated initially (since image must not be square, this angle is not always 45 deg.) --这是对角线最初旋转的角度(因为图像不能是正方形,所以这个角度并不总是 45 度。)

Math.cos(Math.toRadians(newRotation) + Math.atan2(imageView.getHeight(), imageView.getWidth()))

--this gives us projection onto X axis of unit vector, rotated by an angle composed with angle, at which image is rotated, and angle of initial rotation of its diagonal. -- 这给了我们在单位向量的 X 轴上的投影,旋转了一个由角度组成的角度,图像旋转的角度,以及其对角线的初始旋转角度。 After multiplying it to half length of diagonal, we get X of image corner.乘以对角线的一半长度后,我们得到图像角的X。

The same with Y, but using sin instead of cos .与 Y 相同,但使用sin而不是cos

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM