5

Androidでキャンバス画の勉強を始めたので、簡単なアプリを作りたいと思っています。

アプリの起動時に、いわゆる「ヘビ」が画面上で動き始めます。ユーザーが画面をタップすると、「ヘビ」が方向を変えます。

私はこれを簡単に達成しましたが、少し問題があります:

ユーザーが画面をタップすると、ヘビはその瞬間、数ミリ秒後に方向を変えることがあります。そのため、ユーザーはインタラクションが期待どおりに反応しないことを明確に感じることができます。たとえあなたが非常に集中していても、ヘビが向きを変える正確な瞬間はかなり予測できません. これを行うには、私が行った方法よりも優れた方法が他にあるはずです。

私のコードを確認してください。Runnable で Handler を使用してヘビを移動します。(キャンバスに描画し、それをビューの背景として設定するたびに、つまり setContentView をアクティビティに設定するたびに.

コード:

public class MainActivity extends Activity implements View.OnClickListener {

    Paint paint = new Paint();
    Canvas canvas;


    View contentView; ///<The view to set by setContentView

    int moveSize; ///<Sze in px to move drawer to draw another square


    int leftIndex; ///<Indexes for square drawing
    int topIndex;
    int rightIndex;
    int bottomIndex;

    int maxBoundsX; ///<The max number of squares in X axis
    int maxBoundsY; ///<The max number of squares in Y axis

    int moveSpeedInMillis = 25; ///<One movement square per milliseconds

    Bitmap bitmapToDrawOn; ///<We draw the squares to this bitmap

    Direction currentDirection = Direction.RIGHT; ///< RIGHT,LEFT,UP,DOWN directions. default is RIGHT

    Handler  handler  = new Handler(); ///<Handler for running a runnable that actually 'moves' the snake by drawing squares to shifted positions
    Runnable runnable = new Runnable() {

        @Override
        public void run() {

            Log.i("runnable", "ran");

            //Drawing a square to the current destination
            drawRectPls(currentDirection);
            //After some delay we call again and again and again
            handler.postDelayed(runnable, moveSpeedInMillis);
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        /*getting area properties like moveSize, and bounds*/

        moveSize = searchForOptimalMoveSize();

        maxBoundsX = ScreenSizer.getScreenWidth(this) / moveSize;
        maxBoundsY = ScreenSizer.getScreenHeight(this) / moveSize;

        Log.i("moveSize", "moveSize: " + moveSize);
        Log.i("maxBounds: ", "x: " + maxBoundsX + " ; " + "y: " + maxBoundsY);


        /*setting start pos*/

        //We start on the lower left part of the screen

        leftIndex = moveSize * (-1);
        rightIndex = 0;
        bottomIndex = moveSize * maxBoundsY;
        topIndex = moveSize * (maxBoundsY - 1);


        //Setting contentView, bitmap, and canvas

        contentView = new View(this);
        contentView.setOnClickListener(this);

        bitmapToDrawOn = Bitmap.createBitmap(ScreenSizer.getScreenWidth(this), ScreenSizer.getScreenHeight(this), Bitmap.Config.ARGB_8888);
        canvas = new Canvas(bitmapToDrawOn);

        contentView.setBackground(new BitmapDrawable(getResources(), bitmapToDrawOn));
        setContentView(contentView);

        /*starts drawing*/
        handler.post(runnable);


    }

    /**
     * Draws a square to the next direction
     *
     * @param direction the direction to draw the next square
     */
    private void drawRectPls(Direction direction) {

        if (direction.equals(Direction.RIGHT)) {
            leftIndex += moveSize;
            rightIndex += moveSize;
        } else if (direction.equals(Direction.UP)) {
            topIndex -= moveSize;
            bottomIndex -= moveSize;
        } else if (direction.equals(Direction.LEFT)) {
            leftIndex -= moveSize;
            rightIndex -= moveSize;
        } else if (direction.equals(Direction.DOWN)) {
            topIndex += moveSize;
            bottomIndex += moveSize;
        }


        addRectToCanvas();
        contentView.setBackground(new BitmapDrawable(getResources(), bitmapToDrawOn));
        Log.i("drawRect", "direction: " + currentDirection);

    }


    private void addRectToCanvas() {
        paint.setColor(Color.argb(255, 100, 100, 255));
        canvas.drawRect(leftIndex, topIndex, rightIndex, bottomIndex, paint);
    }

    /**
     * Upon tapping the screen the the snake is changing direction, one way simple interaction
     */
    @Override
    public void onClick(View v) {

        if (v.equals(contentView)) {

            if (currentDirection.equals(Direction.RIGHT)) {
                currentDirection = Direction.UP;
            } else if (currentDirection.equals(Direction.UP)) {
                currentDirection = Direction.LEFT;
            } else if (currentDirection.equals(Direction.LEFT)) {
                currentDirection = Direction.DOWN;
            } else if (currentDirection.equals(Direction.DOWN)) {
                currentDirection = Direction.RIGHT;
            }
        }
    }


    /**
     * Just getting the size of a square. Searching for an integer that divides both screen's width and height
     * @return
     */
    private int searchForOptimalMoveSize() {
        int i;
        for (i = 16; i <= 64; i++) {
            Log.i("iter", "i= " + i);
            if (ScreenSizer.getScreenWidth(this) % i == 0) {
                Log.i("iter", ScreenSizer.getScreenWidth(this) + "%" + i + " =0 !");
                if (ScreenSizer.getScreenHeight(this) % i == 0) {
                    Log.i("iter", ScreenSizer.getScreenHeight(this) + "%" + i + " =0 !");
                    return i;
                }
            }
        }
        return -1;
    }


    /**
     * Stops the handler
     */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable);
    }
}

編集:

コードを変更しました。ビューにすべての詳細が含まれるようになり、フィリップが提案したようにonDrawメソッドと無効化メソッドを使用します。

結果は少し良くなりましたが、ユーザーの操作によって方向転換が遅れていることをはっきりと感じることができます。

おそらく私はスレッドで何かをすべきですか?

public class SpiralView extends View implements View.OnClickListener {

    int leftIndex; ///<Indexes for square drawing
    int topIndex;
    int rightIndex;
    int bottomIndex;

    int speedInMillis = 50;

    int moveSize; ///<Sze in px to move drawer to draw another square

    int maxBoundsX; ///<The max number of squares in X axis
    int maxBoundsY; ///<The max number of squares in Y axis


    Paint paint = new Paint();
    Direction currentDirection = Direction.RIGHT; ///< RIGHT,LEFT,UP,DOWN directions. default is RIGHT


    public void setUp(int moveSize, int maxBoundsX, int maxBoundsY) {
        this.moveSize = moveSize;
        this.maxBoundsX = maxBoundsX;
        this.maxBoundsY = maxBoundsY;
        this.leftIndex = moveSize * (-1);
        this.rightIndex = 0;
        this.bottomIndex = moveSize * (maxBoundsY);
        this.topIndex = moveSize * (maxBoundsY - 1);

    }

    public SpiralView(Context context, AttributeSet attrs) {
        super(context, attrs);
        setOnClickListener(this);


    }

    /**
     * Draws a square to the next direction
     *
     * @param direction the direction to draw the next square
     */
    private void drawOnPlease(Direction direction, Canvas canvas) {

        if (direction.equals(Direction.RIGHT)) {
            leftIndex += moveSize;
            rightIndex += moveSize;
        } else if (direction.equals(Direction.UP)) {
            topIndex -= moveSize;
            bottomIndex -= moveSize;
        } else if (direction.equals(Direction.LEFT)) {
            leftIndex -= moveSize;
            rightIndex -= moveSize;
        } else if (direction.equals(Direction.DOWN)) {
            topIndex += moveSize;
            bottomIndex += moveSize;
        }


        Log.i("drawRect", "direction: " + currentDirection);
        Log.i("drawRect", "indexes: "+topIndex+" , "+rightIndex+" ," +bottomIndex+" , "+leftIndex);

        addRectToCanvas(canvas);

    }

    private void addRectToCanvas(Canvas canvas) {
        paint.setColor(Color.argb(255, 100, 100, 255));

       canvas.drawRect(leftIndex, topIndex, rightIndex, bottomIndex, paint);
    }


    @Override
    protected void onDraw(Canvas canvas) {

        try {

            drawOnPlease(currentDirection, canvas);

            synchronized (this) {
                wait(speedInMillis);
            }


            invalidate();

        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void onClick(View v) {

        if (currentDirection.equals(Direction.RIGHT)) {
            currentDirection = Direction.UP;
        } else if (currentDirection.equals(Direction.UP)) {
            currentDirection = Direction.LEFT;
        } else if (currentDirection.equals(Direction.LEFT)) {
            currentDirection = Direction.DOWN;
        } else if (currentDirection.equals(Direction.DOWN)) {
            currentDirection = Direction.RIGHT;
        }

        //..?
        invalidate();

    }

}
4

3 に答える 3

1

Canvas での描画に Handler を使用しないでください。代わりに、カスタム ビューを作成し、onDraw(Canvas canvas) メソッドを使用する必要があります。このメソッドでは、既に行ったように Canvas オブジェクトに描画できます。invalidate() メソッドを呼び出すと、新しい onDraw() 呼び出しがトリガーされます。onTouch() または onClick() 関数では、invalidate() を呼び出して新しい onDraw 呼び出しもトリガーします

class SnakView extends View {
  @Override
  protected void onDraw(Canvas canvas) {
    // draw on canvas
    invalidate();
  }

  @Override
  public void onClick(View v) {
    // handle the event
    invalidate();
  }
}
于 2015-07-22T07:53:26.980 に答える
0

android:hardwareAccelerated="true"マニフェスト、 または に追加してみてください。

これは、3.0 以降のデバイスで機能します。

また、ターゲット API レベルは 11 である必要があります。

その後、よりスムーズに動作します。

于 2015-07-29T11:21:13.360 に答える