0

所以我有一个带有 TextView 的活动来保存分数,如下所示:

public class PlayGameActivity extends Activity {
    private GameThread mGameThread;
    private GameView mGameView;

    private Handler mHandler = new Handler();

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

        final TextView scoreView = (TextView) findViewById(R.id.textView2);

        mGameView = (GameView) findViewById(R.id.gameView1);
        mGameThread = mGameView.getThread();

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                if (mGameView != null) {
                    scoreView.setText(mGameThread.getScore());
                }
            }
        });
    }
}

然后在我的 GameView 我有这样声明的函数:

class GameView extends SurfaceView implements SurfaceHolder.Callback {
    class GameThread extends Thread {
        int score = 0;

        public String getScore(){
            return Integer.toString(score);
        }

        @Override
        public void run() {
            synchronized (mSurfaceHolder) {
                if (something){
                    score++
                }
            }
        }
    }
}

问题是 mGameThread.getScore() 总是返回 0,就好像分数没有更新一样。

如果有人有任何煽动,将不胜感激。此外,我从上面的片段中删除了许多不必要的代码。如果您想查看全文,您可以在这里查看:

4

3 回答 3

2

你的处理程序只运行一次getScore(),所以你总是会得到初始值。你应该尝试使用postDelayed().

mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
        if (mGameView != null) {
            scoreView.setText(mGameThread.getScore());
        }
        mHandler.postDelayed(this, 1000); // run again in 1sec
    }
}, 1000);

正如 Thomas 提到的,不应以这种方式使用 Handler,这是来自doc的引用:

处理程序有两个主要用途:(1)安排消息和可运行文件在未来某个时间点执行;(2) 将要在与您自己的线程不同的线程上执行的操作排入队列。

它完全适合(1)!

于 2012-10-07T23:58:02.480 回答
2

有更好的方法来更新 UI 数据,使用事件/监听器机制。

坐在循环线程中不是 OOP 风格的编程。如果分数的变化发生在你的应用程序中,那么你肯定有一个检测变化的句柄。一旦检测到,用一个简单的 setText 调用你的 textview 来更新 UI。

不要像调用 post(....) 那样(可能在不知不觉中)过度拥挤 UI 事件队列堆栈,这将使您的 UI 对实际用户交互的响应性降低,特别是如果您在定时循环中执行此操作。请记住,UI 事件队列堆栈对于整个系统是 ONE,它旨在处理用户与设备的交互。

如果您需要从非 UI 线程使用更改 UI 组件

     activity.runOnUiThread(Runnable); 

方法而不是 post(Runnable)。这样,您可能会在 Runnable 中定义更长的执行代码(尽管不推荐)。否则,请确保 post(Runnable) 中的代码可以非常快速地执行和完成。

于 2012-10-08T00:45:28.137 回答
1

你从哪里打电话mHandler.post(Runnable)?您发布的代码将一条消息(来自您的 onCreate)添加到主线程的消息队列中,这没有多大意义。这条消息被处理了,会打印0,然后就是这样。因此,如果您不定期从某个地方调用它,它不会在初始 0 之后打印任何内容。

如果您也Handler.post(Runnable)从其他地方拨打电话,请确保您从正确的位置拨打电话。GameThread每次要更新 UI 时都必须调用它。因此,您将需要mHandler在 GameThread 中引用。例如,构造函数GameThread是执行此操作的好地方(我不会将其实现GameThread为内部类,而是针对每个人自己的)。一旦GameThread有了对 的引用mHandler,您就可以使用它mHandler.post(Runnable)来更新您的 UI。例如:

    @Override
    public void run() {
        synchronized (mSurfaceHolder) {
            if (something){
                // IMPORTANT: see "important note" at the end of this Answer                          

                mHandler.post(mRunnable); // mRunnable is preinstantiated somewhere
                score++
            }
        }
    }

重要提示:您GameThread可能使用了一种测量实时的方法(即它不会在没有任何 Thread.sleep(...) 的情况下无限循环调用所有内容。因此,Handler不会过度发送“更新分数”消息。如果你的游戏是回合制的(而不是实时的),那么这个问题甚至根本不会出现。

注2::我检查了你的完整代码,你的游戏tick似乎没有任何FPS规定,即你不使用egThread.sleep(33)或类似的东西。因此,如果您的海盗发生碰撞,我上面写的解决方案将向您的 UI 发送很多消息(并且您的分数将在很短的时间内达到非常高的值——我怀疑这就是您想要的)。事实上,你的score值可能很快就会溢出,除非碰撞很快结束。所以我建议你像所有游戏一样在你的代码中添加 FPS 平衡。

于 2012-10-07T23:59:37.077 回答