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

Posted

技术标签:

【中文标题】基于 Android 中的一个句柄旋转和缩放视图【英文标题】:Rotate and scale a view based on one handle in Android 【发布时间】:2014-05-31 16:21:55 【问题描述】:

我正在尝试基于 android 中的一个“拖动”句柄来旋转和缩放视图。随着布局旋转和视图大小调整,最终结果应该是拖动手柄跟随用户手指移动。

这是基于结合这两个问题:

    How to scale a view in android using a handle? Rotating around two points in android not working

唯一看起来错误的是轮换代码。

这是我的活动代码:

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:

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

    <RelativeLayout
        android:id="@+id/relativeLayout2"
        android:layout_
        android:layout_
        android:layout_centerInParent="true" >

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

        <ImageView
            android:id="@+id/imageView2"
            android:layout_
            android:layout_
            android:layout_below="@+id/imageView1"
            android:layout_toRightOf="@+id/imageView1"
            android:src="@drawable/meanicons" />

    </RelativeLayout>

</RelativeLayout>

【问题讨论】:

使用ze框架! developer.android.com/guide/topics/ui/drag-drop.html 在这里查看我的答案***.com/questions/21633545/… 【参考方案1】:

如果您需要使用一个处理程序图标同时调整图像大小和旋转图像,则应执行一些三角计算。

根据图像的初始角度和角度,计算图像应该旋转的角度并不难,连接图像中心和当前手指位置的向量旋转的角度。稍微复杂一点的任务是将处理程序放置在屏幕的适当位置,使其始终连接主图像的角落。

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

那么,我在做什么?

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

——计算主图像对角线的一半长度,即。 e.中心到角点的距离

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

--这是对角线最初旋转的角度(因为图像不能是正方形,所以这个角度并不总是 45 度。)

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

——这给了我们在单位向量的 X 轴上的投影,旋转了一个角度,该角度由旋转图像的角度和其对角线的初始旋转角度组成。将其乘以对角线的一半长度后,我们得到图像角的X。

与 Y 相同,但使用 sin 而不是 cos

【讨论】:

效果很好。试图通过用手指移动视图来理解 X 和 rawX。你知道为什么这段代码只在第一次移动视图时有效吗? pastebin.com/zNmaNhqw 除了记住它接触的起点之外,您还应该记住在上一次移动时应用于视图的平移,并在motion_move事件期间使用它来计算新的平移 我应该在action_up中记住它吗?我第一次看不到这是如何工作的。但不是第二个。 @user2676468 你应该考虑之前的移动进行的 x 和 y 平移。看看我如何使用startScalestartRotation 进行类比 @user2676468 您应该创建一种startMoveXstartMoveY 变量,并像使用startScalestartRotation 一样使用它们。

以上是关于基于 Android 中的一个句柄旋转和缩放视图的主要内容,如果未能解决你的问题,请参考以下文章

图片的缩放旋转和平移

Android围绕动态枢轴旋转视图

Android OpenGLES2.0——OpenGL中的平移旋转缩放

Android自定义控件之可平移、缩放、旋转图片控件

Android:当视图改变方向时切换高度和宽度

android控件随手势旋转