我是Android和线程的新手。我目前正在制作一个游戏,其中渲染和逻辑发生在与UI不同的线程上。 我的问题是我似乎无法使用工作线程中的帖子更新ui。
这是我的主要活动:
private GameScene gameScene;
private Handler uiHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.game);
uiHandler = new Handler(Looper.getMainLooper());
gameScene = new GameScene(getApplicationContext(), uiHandler);
LinearLayout gameHolder = (LinearLayout)findViewById(R.id.game_view);
gameHolder.addView(gameScene);
gameScene.resume();
}
这是实现runnable的类:
public abstract class Scene extends SurfaceView implements Runnable {
Handler uiHandler;
Thread thread = null;
public Scene(Context context, Handler uiHandler) {
super(context);
this.uiHandler = uiHandler;
}
@Override
public void run() {
uiHandler.post(() -> {
TextView textView = findViewById(R.id.fps_counter);
textView.setText("hello");
});
}
public void resume() {
thread = new Thread(this);
thread.start();
}
的xml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/game_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:visibility="visible">
<TextView
android:id="@+id/fps_counter"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="0"
android:textColor="@color/colorAccent"
android:textSize="24sp" />
</LinearLayout>
</FrameLayout>
帖子里面的代码应该在ui线程上运行吗?但是在尝试设置文本时,我得到一个空对象引用。是的,id为fps_counter的文本存在。
答案 0 :(得分:2)
由于Gamescene不包含fps_counter文本视图,因此请修改您的抽象场景和类扩展,以便您可以将引用传递给textview。
public abstract class Scene extends SurfaceView implements Runnable {
Handler uiHandler;
Thread thread = null;
TextView textView;
public Scene(Context context, Handler uiHandler, TextView textView) {
super(context);
this.uiHandler = uiHandler;
this.textView = textView;
}
@Override
public void run() {
uiHandler.post(() -> {
textView.setText("hello");
});
}
public void resume() {
thread = new Thread(this);
thread.start();
}
然后使用textview的第3个参数创建GameScene:
TextView textView = findViewById(R.id.fps_counter);
// no need to use getApplicationContext(), you may use this (a reference to "mainActivity")
gameScene = new GameScene(this, uiHandler, textView);
答案 1 :(得分:1)
您正在findViewById
内调用SurfaceView
,有效地在SurfaceView中寻找具有给定ID的视图。
但是您的R.id.fps_counter
ID视图位于活动内部,因此您应该在活动中查找它。
我建议您在创建GameScene
类之前找到它并在构造函数中提供它。
请参阅https://developer.android.com/reference/android/view/View.html#findViewById(int)和https://developer.android.com/reference/android/app/Activity.html#findViewById(int)
在后面的答案中使用petey的代码。
另一个解决方案是将活动传递给您的GameScene
,然后您就可以在没有处理程序的情况下执行此操作。
private GameScene gameScene;
private TextView fpsCounter;
@Override
protected void onCreate(Bundle savedInstanceState) {
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.game);
fpsCounter = findViewById(R.id.fps_counter);
gameScene = new GameScene(this);
LinearLayout gameHolder = (LinearLayout)findViewById(R.id.game_view);
gameHolder.addView(gameScene);
gameScene.resume();
}
public void setFps(String fps) {
fpsCounter.setText(fps);
}
和
public abstract class Scene extends SurfaceView implements Runnable {
Thread thread;
Activity activity;
public Scene(Activity activity) {
super(activity);
this.activity = activity;
}
@Override
public void run() {
activity.runOnUiThread(() -> {
activity.setFps("Hello");
});
}
public void resume() {
thread = new Thread(this);
thread.start();
}
更好的方法是使用框架所期望的构造函数并在init方法中设置活动,如here