2017-11-16 2 views
0

나는 일반적으로 안드로이드와 스레딩을 처음 사용합니다. 현재 UI에서 별도의 스레드에서 렌더링 및 논리가 발생하는 게임을 만들고 있습니다. 제 문제는 작업자 스레드의 게시물을 사용하여 UI를 업데이트 할 수 없다는 것입니다.게시물 문제를 사용하여 게시물

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 스레드에서 실행해야합니다 이것은 실행 가능한 구현하는 클래스가

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(); 
} 

:

내 mainActivity입니다 권리? 하지만 텍스트를 설정하려고 할 때 null 개체 참조가 나타납니다. 그리고 예 id가 fps_counter 인 텍스트가 있습니다.

+0

는 UI 스레드에서 코드를 실행하는 runOnUiThread를 사용해보십시오. – Fustigador

+0

레이아웃 xml을 게시하십시오. – TpoM6oH

+0

어디에서 불러야합니까? @Fustigador – Tagor

답변

1

SurfaceView 내에서 을 호출하면 SurfaceView 내부에 지정된 ID가있는 뷰를 효과적으로 찾습니다.

id가 R.id.fps_counter 인 사용자의보기는 활동 안에 있으므로 활동에서 찾아야합니다.

GameScene 클래스를 생성하고 생성자에서 제공하기 전에 찾아 보시기 바랍니다.

https://developer.android.com/reference/android/view/View.html#findViewById(int)https://developer.android.com/reference/android/app/Activity.html#findViewById(int)

가 나중에 대답 피티하여 코드를 사용하십시오.

다른 솔루션은 귀하의 GameScene에 활동을 전달하는 것이며, 그러면 처리기없이 처리 할 수 ​​있습니다. Gamescene가 fps_counter의 텍스트 뷰를 포함하지 않기 때문에

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(); 
    } 

here

2

처럼, init 메소드의 프레임 워크와 설정 활동에 의해 예상 생성자를 사용하는 것 , 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(); 
    } 

그런 다음 텍스트 뷰의 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); 
+0

고맙습니다. 하지만 하나의 질문이, 그것은 또한 핸들러를 사용하지 않고 작동합니다. 내가 무엇을 읽었는지에서 당신은 UI와 통신하기 위해 핸들러를 사용해야 만합니다. 대신 관련된 변수에 대한 일반 참조를 사용하여 통신한다면 어떻게 될까요? – Tagor

+0

그렇다면 표면 뷰 콜백은 항상 UI 스레드에서 호출되어야하기 때문에 ui 처리기가 필요하지 않습니다. 어떤 이유로 백그라운드 스레드에서 호출하는 경우 황금 규칙 중 하나가 깨 졌으므로 앱이 중단됩니다 (항상 UI 스레드의보기를 터치). 당신이 지금 일하고있어 기쁘다. – petey

+0

고마워요하지만 난 여전히 이해가 안되네요, 현장에서 run 메소드를 사용하는 것은 거기에있는 모든 것이 UI 스레드와 다른 스레드에서 실행된다는 것입니다. uiHandler를 실행하지 않고 textView.setText()를 호출하면 여전히 UI 스레드에서 실행됩니까? 나는 혼란 스럽네요. @petey – Tagor

관련 문제