简体   繁体   中英

Android - How to rotate multiple 2D graphic objects

I am trying to rotate 2 circles on the screen. On the press of a button, one circles rotates clockwise, and the other circles rotates counterclockwise. Both will rotate by 90 degrees and then stop until the next button click.
Its working but it looks very bad. Instead of rotating at the same time, 1st one circle rotates, and then the 2nd.
I read about animation but all the examples I found showed how to rotate the entire canvas. Possibly I am not looking in the right places and there is a way to assign animation to an object somehow.
I've added my code below. I apologies for it not being a true SSCCE but I got errors when my custom SurfaceView was an internal class under the main activity.

Any advice or lead on how to do this properly is very appreciated. Activity

package sscce.android.rotation;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;

public class SscceRotationActivity extends Activity implements OnClickListener {

    private MySurfaceView mySurfaceView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        findViewById(R.id.btnClockwise).setOnClickListener(this);
        findViewById(R.id.btnCounterClockwise).setOnClickListener(this);
        mySurfaceView = (MySurfaceView) (findViewById(R.id.surfaceView1));
    }

    public void onClick(View arg0) {

        switch (arg0.getId()) {
        case R.id.btnClockwise:
            mySurfaceView.rotate(true);
            break;
        case R.id.btnCounterClockwise:
            mySurfaceView.rotate(false);
            break;
        }
    }
}

Custom SurfaceView

package sscce.android.rotation;

import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class MySurfaceView extends SurfaceView implements
        SurfaceHolder.Callback {

    private Circle circle1;
    private Circle circle2;
    private DrawThread drawThread;

    public MySurfaceView(Context context) {
        super(context);
        initialize();
    }

    public MySurfaceView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public MySurfaceView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initialize();
    }

    private void initialize() {
        getHolder().addCallback(this);
        drawThread = new DrawThread(getHolder(), this);
        setFocusable(true);
    }

    public void surfaceCreated(SurfaceHolder holder) {
        circle1 = new Circle(getWidth() / 2, getHeight() / 2, 50);
        circle2 = new Circle(getWidth() / 2, getHeight() / 2, 80);
        drawThread.setRunning(true);
        drawThread.start();
    }

    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
        // TODO Auto-generated method stub
    }

    public void surfaceDestroyed(SurfaceHolder arg0) {
        boolean retry = true;
        drawThread.setRunning(false);
        while (retry) {
            try {
                drawThread.join();
                retry = false;
            } catch (InterruptedException e) {
                // we will try it again and again...
            }
        }
    }

    public void onDraw(Canvas canvas) {
        circle2.onDraw(canvas);
        circle1.onDraw(canvas);
    }

    public void rotate(boolean clockWise) {
        Rotator rotator1 = new Rotator(circle1, clockWise);
        Rotator rotator2 = new Rotator(circle2, !clockWise);
        rotator1.run();
        rotator2.run();
    }

    private class Circle {
        private RectF rectF;
        private int rotationAngle = 0;

        MyPaint bluePaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
                Color.BLUE);
        MyPaint redPaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
                Color.RED);
        MyPaint yellowPaint = new MyPaint(1, Paint.Cap.SQUARE,
                Paint.Style.FILL, Color.YELLOW);
        MyPaint greenPaint = new MyPaint(1, Paint.Cap.SQUARE, Paint.Style.FILL,
                Color.GREEN);
        MyPaint borderPaint = new MyPaint(3, Paint.Cap.SQUARE,
                Paint.Style.STROKE, Color.WHITE);

        public Circle(int centerX, int centerY, int radius) {
            rectF = new RectF(new Rect(centerX - radius, centerY - radius,
                    centerX + radius, centerY + radius));
        }

        public void rotateClockwise() {
            for (int i = 0; i < 90; i++) {
                rotationAngle++;
                if (rotationAngle == 360) {
                    rotationAngle = 0;
                    return;
                }
                try {
                    Thread.sleep(20, 0);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        public void rotateCounterClockwise() {
            for (int i = 0; i < 90; i++) {
                rotationAngle--;
                if (rotationAngle == 0) {
                    rotationAngle = 360;
                    return;
                }
                try {
                    Thread.sleep(20, 0);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }

        public void onDraw(Canvas canvas) {
            canvas.drawArc(rectF, (0 + rotationAngle) % 360, 90, true,
                    bluePaint);
            canvas.drawArc(rectF, (90 + rotationAngle) % 360, 90, true,
                    redPaint);
            canvas.drawArc(rectF, (180 + rotationAngle) % 360, 90, true,
                    yellowPaint);
            canvas.drawArc(rectF, (270 + rotationAngle) % 360, 90, true,
                    greenPaint);
            canvas.drawArc(rectF, 0, 360, true, borderPaint);
        }

        private class MyPaint extends Paint {
            public MyPaint(int strokeWidth, Paint.Cap cap, Paint.Style style,
                    int color) {
                setStrokeWidth(strokeWidth);
                setAntiAlias(true);
                setStrokeCap(cap);
                setStyle(style);
                setColor(color);
            }
        }
    }

    private class Rotator extends Thread {
        private Circle circle;
        private boolean clockwise;

        public Rotator(Circle circle, boolean clockwise) {
            this.circle = circle;
            this.clockwise = clockwise;
        }

        @Override
        public void run() {
            if (clockwise) {
                circle.rotateClockwise();
            } else {
                circle.rotateCounterClockwise();
            }
        }
    }

    private class DrawThread extends Thread {
        private SurfaceHolder surfaceHolder;
        private MySurfaceView surfaceView;
        private boolean run = false;

        public DrawThread(SurfaceHolder surfaceHolder, MySurfaceView surfaceView) {
            this.surfaceHolder = surfaceHolder;
            this.surfaceView = surfaceView;
            run = false;
        }

        public void setRunning(boolean run) {
            Log.d("setRunning@DrawThread", "Run status is " + run);
            this.run = run;
        }

        @Override
        public void run() {
            Canvas canvas = null;
            while (run) {
                try {
                    canvas = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder) {
                        surfaceView.onDraw(canvas);
                    }
                } finally {
                    if (canvas != null) {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }
            }
        }
    }
}

Layout

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <sscce.android.rotation.MySurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <LinearLayout
        android:id="@+id/linearLayout1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center" >

        <Button
            android:id="@+id/btnClockwise"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Clockwise" />

        <Button
            android:id="@+id/btnCounterClockwise"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Counter Clockwise" />
    </LinearLayout>

</LinearLayout>

I would like to advise a different approach to rotation using matrices.The code would look like

canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(cwRotation);
//draw first circle here
canvas.restore();
canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.rotate(ccwRotation);
//draw second circle here
canvas.restore();

This approach has the advantage of being very straightforward and requiring no additional classes and APIs and it is similar to what you would do with OpenGL.

你将有更多的成功,如果您更换rotator1 / 2.run()rotator1 / 2.启动线()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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