所以我有一个带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,好像得分当然没有更新。
如果有人有任何煽动,我们非常感谢。我也从上面的片段中挑出了许多不必要的代码。如果你想看到全文,你可以在这里看到:
答案 0 :(得分: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);
正如托马斯提到Handler不应该以这种方式使用这里引用doc:
Handler有两个主要用途:(1)安排消息和runnables作为将来的某个点执行; (2)将要在不同于自己的线程上执行的动作排入队列。
完全符合(1)!
答案 1 :(得分:2)
有更好的方法来更新UI数据,使用事件/侦听器机制。
坐在循环线程中不是OOP风格的编程。如果您的应用中的分数发生变化,那么您肯定会有一个检测变化的句柄。检测到后,通过对textview的一个简单的setText调用来更新UI。
不要过度拥挤你的UI事件队列堆栈(因为你可能在不知不觉中)通过调用post(....),这会使你的UI对实际用户交互的响应性降低,特别是如果你在定时循环中这样做。请记住,UI事件队列堆栈对于整个系统来说是一个,它用于处理用户与设备的交互。
如果您需要从非UI线程使用中更改UI组件
activity.runOnUiThread(Runnable);
方法而不是post(Runnable)。这样,您可能会在Runnable中定义更长的执行代码(尽管不推荐)。否则,请确保post(Runnable)中的代码执行和完成非常快。
答案 2 :(得分:1)
您从哪里拨打mHandler.post(Runnable)
?您发布的代码会将一条消息(来自您的onCreate)添加到主线程的消息队列中,这没有多大意义。处理此消息,将打印0,然后就是它。因此,如果您不定期从某个地方调用它,它将不会在初始0之后打印任何内容。
如果您也从其他地方拨打Handler.post(Runnable)
,请务必从正确的位置拨打电话。每次要更新UI时,都必须从GameThread
调用它。因此,您需要在GameThread中引用mHandler
。例如。 GameThread
的构造函数是一个很好的地方(我不会将GameThread
实现为内部类,而是实现自己的内部类)。在GameThread
引用mHandler
后,您可以使用mHandler.post(Runnable)
更新您的用户界面。例如:
@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::我检查了您的完整代码,似乎您的游戏代码没有任何FPS规定,即您不使用例如Thread.sleep(33)
或类似的东西。因此,如果您的盗版发生冲突,我上面写的解决方案会向您的UI发送很多消息(并且您的分数会在很短的时间内达到很高的价值 - 我怀疑这是您想要的 )。实际上,除非碰撞非常快,否则您的score
值可能会很快溢出。因此,我建议您像所有游戏一样在代码中添加FPS平衡。