0

有多个类似我的问题,但这些问题对我没有帮助。

我在做游戏。游戏线程、SurfaceView 和 Activity 已经完成并运行至今。问题是画布没有重绘。启动时,它会在背景上绘制图标,但在每一次滴答声中,图标都不会移动(它应该每秒移动一次)。我想提一下,我从来不需要调用postInvalidate。我有一个工作示例,我从未调用它,但它在我当前的示例中不起作用(我不想更深入地研究它,因为我实际上不需要调用它)。我从我的工作示例中复制了当前代码,概念和实现方式完全相同,但我当前的代码没有刷新画布。当我在onDraw中记录绘图位置时方法,我看到它的坐标按预期每秒更新,所以我可以确定这是一个画布绘图问题。我已经搜索了几个小时,但没有发现与我的工作示例有什么不同(除了我使用的是另一个 Android 版本并且我没有扩展线程而是实现 Runnable,因为扩展线程是一种不好的风格。不过,我还扩展了线程以查看是否有任何区别,但这没有帮助)。我已经尝试使用canvas.drawColor(Color.BLACK)清理画布,但这也无济于事。我已经尝试使用背景颜色而不是每个刻度随机更改的背景图像,但它没有改变但始终保持不变。我发现第一次调用时画布的密度为(例如)240。第二次打勾后,画布密度始终为 0。我知道密度在这里对我没有帮助,但也许这是一个重要信息给某人。

这是重要的课程......

游戏布局,R.layout.game

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

    <com.mydomain.mygame.base.game.GameSurface
        android:id="@+id/gameSurface"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1.0"
        android:background="@drawable/background_game" >
    </com.mydomain.mygame.base.game.GameSurface>

<!-- ...more definitions -->

</LinearLayout>

GameActivity(包含布局)

public class GameActivity extends Activity
{
    @SuppressWarnings("unused")
    private GameSurface gameSurface;

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

        gameSurface = (GameSurface)findViewById(R.id.gameSurface);
        //TODO on button click -> execute methods
    }
}

GameSurface(登录 onDraw 显示每个刻度更新的坐标)

public class GameSurface extends SurfaceView implements SurfaceHolder.Callback
{
    private GameThread thread;
    protected final static int TICK_FREQUENCY = 100;// ms, stays always the same. It's a technical constant which doesn't change

    private static final String TAG = GameSurface.class.getSimpleName();

    public GameSurface(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        ShapeManager.INSTANCE.init(context);

        SurfaceHolder holder = getHolder();
        holder.addCallback(this);

        setFocusable(true); // make sure we get key events

        thread = new GameThread(holder, this);
    }

    public void updateStatus()
    {
        GameProcessor.INSTANCE.updateShapes();
    }

    @Override
    protected void onDraw(Canvas canvas)
    {
        for (Shape shape : GameProcessor.INSTANCE.getShapes())
        {
            Log.i(TAG, "getX()=" + shape.getX() + ", getY()=" + shape.getY());
            canvas.drawBitmap(shape.getBitmap(), shape.getX(), shape.getY(), null);
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        //will never invoked since we only operate in landscape
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        // start the thread here so we don't busy-wait in run
        thread.setRunning(true);
        new Thread(thread).start();
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        Log.i(TAG, "executing surfaceDestroyed()...");  
        thread.setRunning(false);
    }
}

游戏线程

public class GameThread implements Runnable
{
    private SurfaceHolder surfaceHolder;
    private boolean running = false;
    private GameSurface gameSurface;
    private long lastTick;

    public GameThread(SurfaceHolder surfaceHolder, GameSurface gameSurface)
    {
        this.surfaceHolder = surfaceHolder;
        this.gameSurface = gameSurface;

        lastTick = System.currentTimeMillis();
    }

    @Override
    public void run()
    {
        Canvas canvas;
        while (running)
        {
            canvas = null;
            if (System.currentTimeMillis() > lastTick + GameSurface.TICK_FREQUENCY)
            {
                long timeDifference = System.currentTimeMillis() - (lastTick + GameSurface.TICK_FREQUENCY);

                try
                {
                    canvas = surfaceHolder.lockCanvas(null);
                    synchronized (surfaceHolder)
                    {
                        gameSurface.updateStatus();
                        gameSurface.draw(canvas);
                    }
                }
                finally
                {
                    if (canvas != null)
                    {
                        surfaceHolder.unlockCanvasAndPost(canvas);
                    }
                }
                lastTick = System.currentTimeMillis() - timeDifference;
            }
        }
    }

    public void setRunning(boolean running)
    {
        this.running = running;
    }
}

任何想法为什么此代码不更新我的画布?我无法解释。我没有发布ShapeManagerGameProcessor ,因为它们与问题没有任何关系(它们只加载和控制游戏的当前状态和速度)。

[更新]

我发现在游戏线程开始之前调用了 onDraw()。这意味着画布在线程使用它之前被传递给这个方法。有趣的是,线程启动后,它总是使用相同的画布,但不是第一次传递的画布引用。虽然canvas = surfaceHolder.lockCanvas(null); 每个刻度都分配,它始终是相同的参考,但它不是原始参考。在我的一个工作示例中,引用始终相同,因为我在构造函数初始化时创建位图。在我当前的实现中我不能这样做,因为我必须使用从 onMeasure() 获得的值进行计算,该值比构造函数调用晚得多。

我试图以某种方式将原始画布传递给线程,但引用仍然发生变化。同时我认为这是问题所在,但我还不知道如何解决它。

4

1 回答 1

2

正如经常发生的那样,我自己找到了解决方案。显然,它绘制到不同的画布实例确实是一个问题。我仍然不确定为什么会这样。之前没有这个问题。不过,我可以通过设置setWillNotDraw(true);来避免绘制到画布上;在我的 GameSurface 构造函数中,我不能在我的线程中调用 gameSurface.draw(canvas) ,而是 gameSurface.postInvalidate() 。

于 2013-09-06T19:11:13.650 回答