我正在创建一个计算反应时间的游戏。我正在使用计时器来获取用户按下按钮所需的时间。出于某种原因,当我尝试运行应用程序时,我收到了Timer-0异常。
的logcat 的
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.abz.layouts, PID: 16015
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
at android.view.View.invalidateInternal(View.java:12713)
at android.view.View.invalidate(View.java:12649)
at android.view.View.invalidateDrawable(View.java:16788)
at android.widget.TextView.invalidateDrawable(TextView.java:5408)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:705)
at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:701)
at android.graphics.drawable.LayerDrawable.invalidateDrawable(LayerDrawable.java:896)
at android.graphics.drawable.DrawableWrapper.invalidateDrawable(DrawableWrapper.java:153)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
at android.graphics.drawable.GradientDrawable.setColorFilter(GradientDrawable.java:837)
at android.graphics.drawable.DrawableWrapper.setColorFilter(DrawableWrapper.java:243)
at android.graphics.drawable.LayerDrawable.setColorFilter(LayerDrawable.java:1285)
at android.graphics.drawable.Drawable.clearColorFilter(Drawable.java:600)
at com.example.abz.layouts.HighLight.run(HighLight.java:33)
at java.util.Timer$TimerImpl.run(Timer.java:284)
D/EGL_emulation: eglMakeCurrent: 0xae424780: ver 3 0 (tinfo 0xae412ba0)
E/Surface: getSlotFromBufferLocked: unknown buffer: 0xaaac9820
Application terminated.
E/AndroidRuntime: FATAL EXCEPTION: Timer-0
Process: com.example.abz.layouts, PID: 16015
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
at android.view.View.invalidateInternal(View.java:12713)
at android.view.View.invalidate(View.java:12649)
at android.view.View.invalidateDrawable(View.java:16788)
at android.widget.TextView.invalidateDrawable(TextView.java:5408)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:705)
at android.graphics.drawable.RippleDrawable.invalidateSelf(RippleDrawable.java:701)
at android.graphics.drawable.LayerDrawable.invalidateDrawable(LayerDrawable.java:896)
at android.graphics.drawable.DrawableWrapper.invalidateDrawable(DrawableWrapper.java:153)
at android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:385)
at android.graphics.drawable.GradientDrawable.setColorFilter(GradientDrawable.java:837)
at android.graphics.drawable.DrawableWrapper.setColorFilter(DrawableWrapper.java:243)
at android.graphics.drawable.LayerDrawable.setColorFilter(LayerDrawable.java:1285)
at android.graphics.drawable.Drawable.clearColorFilter(Drawable.java:600)
at com.example.abz.layouts.HighLight.run(HighLight.java:33)
at java.util.Timer$TimerImpl.run(Timer.java:284)
D/EGL_emulation: eglMakeCurrent: 0xae424780: ver 3 0 (tinfo 0xae412ba0)
E/Surface: getSlotFromBufferLocked: unknown buffer: 0xaaac9820
Application terminated.
这是我的java类
MainActivty.java 的
package com.example.abz.layouts;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
private long start_time;
private long end_time;
private Button finalButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
// Create list of buttons
ArrayList<Button> buttons = createButtonsArrayList();
// Generates sequence of 5 random buttons
RandomSequence randomSequence = new RandomSequence(buttons, 5, 9);
// Add OnClickListener for last button
finalButton = randomSequence.sequence.get(randomSequence.sequence.size() - 1);
finalButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalButtonClicked();
}
});
// Start Game
start_time = System.nanoTime();
randomSequence.startSequence();
}
private void finalButtonClicked() {
end_time = System.nanoTime();
double diffInMillis = (end_time - start_time) / 1e6;
finalButton.getBackground().clearColorFilter();
Toast toast = Toast.makeText(this, "Reaction time: " + diffInMillis + " milliseconds.", Toast.LENGTH_LONG);
toast.show();
}
private ArrayList<Button> createButtonsArrayList() {
ArrayList<Button> buttons = new ArrayList<>();
final Button button1 = (Button) findViewById(R.id.button1);
final Button button2 = (Button)findViewById(R.id.button2);
final Button button3 = (Button) findViewById(R.id.button3);
final Button button4 = (Button) findViewById(R.id.button4);
final Button button5 = (Button) findViewById(R.id.button5);
final Button button6 = (Button) findViewById(R.id.button6);
final Button button7 = (Button) findViewById(R.id.button7);
final Button button8 = (Button) findViewById(R.id.button8);
final Button button9 = (Button) findViewById(R.id.button9);
buttons.add(button1);
buttons.add(button2);
buttons.add(button3);
buttons.add(button4);
buttons.add(button5);
buttons.add(button6);
buttons.add(button7);
buttons.add(button8);
buttons.add(button9);
return buttons;
}
}
HighLight.java
package com.example.abz.layouts;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends AppCompatActivity {
private long start_time;
private long end_time;
private Button finalButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
// Create list of buttons
ArrayList<Button> buttons = createButtonsArrayList();
// Generates sequence of 5 random buttons
RandomSequence randomSequence = new RandomSequence(buttons, 5, 9);
// Add OnClickListener for last button
finalButton = randomSequence.sequence.get(randomSequence.sequence.size() - 1);
finalButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finalButtonClicked();
}
});
// Start Game
start_time = System.nanoTime();
randomSequence.startSequence();
}
private void finalButtonClicked() {
end_time = System.nanoTime();
double diffInMillis = (end_time - start_time) / 1e6;
finalButton.getBackground().clearColorFilter();
Toast toast = Toast.makeText(this, "Reaction time: " + diffInMillis + " milliseconds.", Toast.LENGTH_LONG);
toast.show();
}
private ArrayList<Button> createButtonsArrayList() {
ArrayList<Button> buttons = new ArrayList<>();
final Button button1 = (Button) findViewById(R.id.button1);
final Button button2 = (Button)findViewById(R.id.button2);
final Button button3 = (Button) findViewById(R.id.button3);
final Button button4 = (Button) findViewById(R.id.button4);
final Button button5 = (Button) findViewById(R.id.button5);
final Button button6 = (Button) findViewById(R.id.button6);
final Button button7 = (Button) findViewById(R.id.button7);
final Button button8 = (Button) findViewById(R.id.button8);
final Button button9 = (Button) findViewById(R.id.button9);
buttons.add(button1);
buttons.add(button2);
buttons.add(button3);
buttons.add(button4);
buttons.add(button5);
buttons.add(button6);
buttons.add(button7);
buttons.add(button8);
buttons.add(button9);
return buttons;
}
}
RandomSequence.java
package com.example.abz.layouts;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.widget.Button;
import java.util.ArrayList;
import java.util.TimerTask;
public class HighLight extends TimerTask {
private ArrayList<Button> sequence;
private int sequencePosition;
public HighLight(ArrayList<Button> sequence) {
this.sequence = sequence;
sequencePosition = 0;
}
public void run() {
Button activeButton = sequence.get(sequencePosition);
if (sequencePosition == 0) {
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
} else if (sequencePosition == sequence.size() - 1) {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
// TODO: PLAY SOUND FOR FINAL BUTTON
this.cancel();
} else {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
}
sequencePosition++;
}
}
package com.example.abz.layouts;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.widget.Button;
import java.util.ArrayList;
import java.util.TimerTask;
public class HighLight extends TimerTask {
private ArrayList<Button> sequence;
private int sequencePosition;
public HighLight(ArrayList<Button> sequence) {
this.sequence = sequence;
sequencePosition = 0;
}
public void run() {
Button activeButton = sequence.get(sequencePosition);
if (sequencePosition == 0) {
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
} else if (sequencePosition == sequence.size() - 1) {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
// TODO: PLAY SOUND FOR FINAL BUTTON
this.cancel();
} else {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
}
sequencePosition++;
}
}
我一直在寻找解决方案,但没有成功。
答案 0 :(得分:1)
logcat中的关键行是:
只有创建视图层次结构的原始线程才能触及其视图
您正在尝试更改主线程之外的UI元素,在这种情况下,调用previousActiveButton.getBackground().clearColorFilter()
中的HighLight.run()
。
您可以在主线程上创建的run()
上发布Handler
方法,也可以在runOnUiThread()
中包含与UI相关的调用。因为您正在使用扩展TimerTask
的自包含类,所以我推荐前者:
public void run() {
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
Button activeButton = sequence.get(sequencePosition);
if (sequencePosition == 0) {
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
} else if (sequencePosition == sequence.size() - 1) {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(Color.RED, PorterDuff.Mode.MULTIPLY);
// TODO: PLAY SOUND FOR FINAL BUTTON
HighLight.this.cancel();
} else {
Button previousActiveButton = sequence.get(sequencePosition - 1);
previousActiveButton.getBackground().clearColorFilter();
activeButton.getBackground().setColorFilter(0xFF00FF00, PorterDuff.Mode.MULTIPLY);
}
}
});
sequencePosition++;
}
虽然我个人考虑使用递归Handler::postDelayed()
而不是Timer