如何让Android画布每5秒重新绘制

4

我正在制作一个需要动态动画的应用程序(玩家移动)。我使用Canvas对象来实现这个功能。我的第一个问题是,“Canvas是否真的是处理这些动画的最佳方式?”, 第二个问题是,“如何将玩家重新绘制到Canvas上?”以下是我的代码:

theGame.java:

package birdprograms.freezetag;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;

public class theGame extends Activity {
    players[] arr = {
            new player(),
            new player(),
            new player(),
            new player()
    };
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(new myView(this));
    }
    public class myView extends View {
        Paint paint = new Paint();
        public myView(Context context) {
            super(context);
            paint.setColor(Color.YELLOW);
        }
        @Override
        public void onDraw(final Canvas canvas) {
            arr[0].update(true, true);
            arr[0].draw(canvas, paint);
        }
    }
}

player.java

package birdprograms.freezetag;

import android.graphics.*;

public class player {
    int y = 0;
    int x = 0;
    int vy = 5;
    int vx = 5;
    int height = y + 15;
    int width = x + 15;
    public void draw(Canvas canvas, Paint paint){
        canvas.drawRect(x,y,width,height,paint);
    }
    public void update(boolean left, boolean top){
        if(left){x += vx; width = x + 15;}
        else{x -= vx; width = x + 15;}
        if(top){y += vy; height = y + 15;}
        else{y -= vy; height = y + 15;}
    }
}
3个回答

5

你无法控制何时调用onDraw:当视图失效时,onDraw将在未来某个时间点被调用。

你的代码存在一个基本设计缺陷:在执行onDraw期间修改了玩家的位置:这样你就无法控制它。

要使你的玩家每5秒移动一次:你可以使用Handler.postDelayed,每5秒重新发布相同的Runnable。该Runnable将更新玩家位置,然后使视图失效。

以下是一些代码以说明这个想法

(免责声明:这是伪代码,仅关注索引为0的玩家,还需要其他操作来移动所有玩家,...)

public class myView extends View {
    Paint paint = new Paint();
    public myView(Context context) {
        super(context);
        paint.setColor(Color.YELLOW);
        movePlayer0Runnable.run(); //this is the initial call to draw player at index 0
    }

    @Override
    public void onDraw(final Canvas canvas) {
        super.onDraw(canvas);  //IMPORTANT to draw the background
        arr[0].draw(canvas, paint);
    }

    Handler handler = new Handler(Looper.getMainLooper());
    Runnable movePlayer0Runnable = new Runnable(){
        public void run(){
            arr[0].update(true, true);
            invalidate(); //will trigger the onDraw
            handler.postDelayed(this,5000); //in 5 sec player0 will move again
        }
    }   
    ... 
}

2
你能贴一个例子吗? - Nathan
非常感谢!现在我理解得更好了。 - Nathan
你如何移除这些回调函数? - vin shaba
@vinshaba handler.removeCallbacksAndMessages(null)@vinshaba 处理程序.removeCallbacksAndMessages(null) - ben75
@ben75 是的,但你在哪里调用它?在 View 类的哪个方法中? - vin shaba
@vinshaba 这取决于您的业务逻辑。在您希望停止移动玩家时立即调用它(可能在 onDetachFromWindow 中),以确保在视图消失后它们不会继续移动。 - ben75

0

onDraw方法中调用postInvalidateDelayed(5000),如How to animate a path on canvas - android所示,比现有的生成计时器或可运行项的答案要简单一些。像它们一样,它不是高分辨率计时器。

这是一个最小完整示例:

布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:keepScreenOn="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
>
    <view
        class="com.example.foo.FooActivity$Drawer"
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
    />
</LinearLayout>

活动:

package com.example.foo;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.View;

import java.util.Random;

public class FooActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_foo);
    }

    public static class Drawer extends View {
        private static final int delayMS = 5000;
        private static Random rnd;
        private static Paint paint;
        private static int viewWidth;
        private static int viewHeight;

        public Drawer(Context con, AttributeSet attr) {
            super(con, attr);
            rnd = new Random();
            paint = new Paint(Paint.ANTI_ALIAS_FLAG);
            paint.setStyle(Paint.Style.FILL);
        }

        // https://dev59.com/P2855IYBdhLWcg3w7o-g#9718512
        @Override
        protected void onSizeChanged(int xNew, int yNew, int xOld, int yOld) {
            super.onSizeChanged(xNew, yNew, xOld, yOld);
            viewWidth = xNew;
            viewHeight = yNew;
        }

        @Override
        protected void onDraw(final Canvas c) {
            super.onDraw(c);

            // https://dev59.com/ImMl5IYBdhLWcg3wMUeP#18617993
            postInvalidateDelayed(delayMS);

            int color = rnd.nextInt(256);
            paint.setARGB(255, color, color, color);
            c.drawCircle(
                viewWidth / 2,
                viewHeight / 2,
                viewWidth / 4,
                paint
            );
        }
    }
}

0

你可以通过创建一个计时器并设置固定的时间表来实现这一点。在视图构造函数中调用init。

private void init(){
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask(){
        public void run() {
             postInvalidate();
        }
    }, 0, 5 * 1000L);//5 seconds
}

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