came across a major problem while game developing in Android Studio. I've made a Class which creates a separate thread. The class it's purphose is to be an animation of a cloud moving:
package com.example.j9.triqshot;
import android.content.Context;
import android.content.res.Resources;
import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
import android.view.View;
public class Cloud extends AppCompatImageView {
private int width;
private int height;
private int ScreenWidth;
private int ScreenHeight;
public long X;
public int Y;
public int speed = 1;
private long beginTime;
private long endTime;
private boolean MayMove = true;
private static int framerate = 12;
private Thread threadCloud;
public Cloud(Context context){
super(context);
}
public Cloud(Context context, AttributeSet attrs){
super(context, attrs);
}
public Cloud(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
}
public void setWidth(int width){
this.setMaxWidth(width);
this.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
this.width = this.getMeasuredWidth();
}
public void setHeight(int height){
this.setMaxWidth(height);
this.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
this.height = this.getMeasuredHeight();
}
public void Move(){
ScreenHeight = Resources.getSystem().getDisplayMetrics().heightPixels; //Define screen height
ScreenWidth = Resources.getSystem().getDisplayMetrics().widthPixels; //Define screen width
beginTime = System.currentTimeMillis();
threadCloud = new Thread(new Runnable(){
@Override
public void run() {
while(MayMove) {
endTime = System.currentTimeMillis();
long TimeDifference = endTime - beginTime;
if(TimeDifference >= (1000/framerate)) {
X += (speed * (TimeDifference/(1000/framerate)));
beginTime = endTime;
setXY(X, Y);
}
if(X > ScreenWidth){
X = 0 - width;
}
}
}});
threadCloud.start();
}
private void setXY(float X,float Y) {
this.setX(X);
this.setY(Y);
}
public void stopThread(){
threadCloud.interrupt();
}
}
When I create one Cloud in the main activity the button will still work, but when I create more than one the button will most of the time not work.
Besides, if the button works and the intent to go to the next activity has been send, an error occours:
02-26 12:13:59.209 4370-4394/com.example.j9.triqshot E/AndroidRuntime: FATAL EXCEPTION: Thread-4
Process: com.example.j9.triqshot, PID: 4370
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6891) at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1083) at android.view.ViewGroup.invalidateChild(ViewGroup.java:5205) at android.view.View.invalidateInternal(View.java:13656) at android.view.View.invalidate(View.java:13620) at android.view.View.invalidateViewProperty(View.java:13740) at android.view.View.setTranslationX(View.java:12848) at android.view.View.setX(View.java:12748) at com.example.j9.triqshot.Cloud.setXY(Cloud.java:87) at com.example.j9.triqshot.Cloud.access$400(Cloud.java:14) at com.example.j9.triqshot.Cloud$1.run(Cloud.java:75) at java.lang.Thread.run(Thread.java:761)
I've never came across an error like this, and I don't fully understand what it exactly means.
The Main activity:
package com.example.j9.triqshot;
import android.content.Intent;
import android.content.res.Resources;
import android.os.CountDownTimer;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
public class MainActivity extends AppCompatActivity {
ImageView BG; //Initialize background Image
ImageView GameName; //Initialize GameName Image
ImageView MenuBox; //Initialize MenuBox Image
ImageView MadeBy; //Initialize MadeBy Image
ImageButton playButton; //Initialize play button
ImageButton optionsButton; //Initialize options button
ImageButton shareButton; //Initialize share button
int width; //Initialize screen width
int height; //Initialize screen height
Cloud Cloud1;
Cloud Cloud2;
Cloud Cloud3;
boolean Moving = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
height = Resources.getSystem().getDisplayMetrics().heightPixels; //Define screen height
width = Resources.getSystem().getDisplayMetrics().widthPixels; //Define screen width
BG = (ImageView)findViewById(R.id.BG_MENU); //Assign Background
GameName = (ImageView)findViewById(R.id.GameName); //Assign GameName
MenuBox = (ImageView)findViewById(R.id.MenuBox); //Assign MenuBox
MadeBy = (ImageView)findViewById(R.id.MadeBy); //Assign MadeBy
playButton = (ImageButton)findViewById(R.id.playButton); //Assign playButton
optionsButton = (ImageButton)findViewById(R.id.optionsButton); //Assign OptionsButton
shareButton = (ImageButton)findViewById(R.id.shareButton); //Assign shareButton
Cloud1 = (Cloud)findViewById(R.id.Cloud1);
Cloud2 = (Cloud)findViewById(R.id.Cloud2);
Cloud3 = (Cloud)findViewById(R.id.Cloud3);
correctMenuSizes();
CorrectPositions();
playButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playButtonClicked();
}
}); //Listen to playButton
optionsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
optionsButtonClicked();
}
}); //Listen to optionsButton
shareButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
shareButtonClicked();
}
});
Cloud1.Move();
Cloud2.Move();
Cloud3.Move();
}
//Set image sizes
private void correctMenuSizes(){
GameName.setMaxHeight(height/12);
MenuBox.setMaxHeight(height/2);
MadeBy.setMaxWidth(width/3);
playButton.setMaxWidth(width/8);
optionsButton.setMaxWidth(width/8);
shareButton.setMaxWidth(width/8);
Cloud1.setWidth(width/3);
Cloud2.setWidth(width/4);
Cloud3.setWidth(width/3);
//Measurements
Cloud1.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Cloud2.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
Cloud3.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
}
private void CorrectPositions(){
//Cloud startPositions and speed
Cloud1.Y = (height / 10);
Cloud1.X = 0;
Cloud1.speed = 3;
Cloud2.Y = (height / 2);
Cloud2.X = (width / 2);
Cloud2.speed = 2;
Cloud3.Y = (height - (height / 3));
Cloud3.X = (width - (width / 3));
Cloud3.speed = 3;
}
//When clicked on playButton
public void playButtonClicked(){
playButton.setImageResource(R.drawable.play_pressed);
new CountDownTimer(500, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
playButtonTimer();
}
}.start();
}
//Reset the button with a delay of 0,5 seconds
//Go to LevelMenu
public void playButtonTimer(){
playButton.setImageResource(R.drawable.play_normal);
Cloud1.stopThread();
Cloud2.stopThread();
Cloud3.stopThread();
Intent intent = new Intent(this, LevelMenu.class);
startActivity(intent);
}
//When clicked on optionsButton
public void optionsButtonClicked(){
optionsButton.setImageResource(R.drawable.options_pressed);
new CountDownTimer(500, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
optionsButtonTimer();
}
}.start();
}
//Reset the button with a delay of 0,5 seconds
public void optionsButtonTimer(){
optionsButton.setImageResource(R.drawable.options_normal);
//Intent intent = new Intent(this, LevelMenu.class);
//startActivity(intent);
}
//When clicked on shareButton
public void shareButtonClicked(){
shareButton.setImageResource(R.drawable.share_pressed);
new CountDownTimer(500, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
shareButtonTimer();
}
}.start();
}
//Reset the button with a delay of 0,5 seconds
public void shareButtonTimer(){
shareButton.setImageResource(R.drawable.share_normal);
//Intent intent = new Intent(this, LevelMenu.class);
//startActivity(intent);
}
}
I've already tried to search similar problems, but with still no results.