-3

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.

Prafulla Malviya
  • 375
  • 3
  • 17
  • 1
    The error message already tells you what the issue is `Only the original thread that created a view hierarchy can touch its views.`. So you are trying to change a view from another thread which is not allowed in the threading model of android. – Ben Feb 26 '18 at 11:23
  • 1
    Duplicate [android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi](https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi) – ADM Feb 26 '18 at 11:25
  • 2
    Possible duplicate of [Android "Only the original thread that created a view hierarchy can touch its views."](https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi) – Jyoti JK Feb 26 '18 at 11:26
  • This isn't a duplicate of this question: https://stackoverflow.com/questions/5161951/android-only-the-original-thread-that-created-a-view-hierarchy-can-touch-its-vi I've already tried this one with no positive results – Jeffrey Saydam Feb 26 '18 at 11:32

1 Answers1

0

you can simply use:

runOnUiThread(new Runnable() {
 @Override
 public void run() {
      // do your work
}});
  • Thank you for your response, I've already tried this, but with no luck. What is the purpose of this piece of code? And how to integrate this is in my code? This piece of code only works if I put this in the OnCreate() Method and nowhere else. When I use this code my clouds won't move anymore neither. – Jeffrey Saydam Feb 26 '18 at 11:49
  • use context to call this method from anywhere like.. ((Activity)context).runOnUiThread(); – Abhinay Sharma Feb 26 '18 at 11:56
  • in your case you have to define a global context and initialize it in constructor of cloud class – Abhinay Sharma Feb 26 '18 at 12:00
  • When I use your method, my app will stay empty (only a white screen). That is because the cloud method is in an ongoing loop. That's why I need the threads to be separate. I need to find a way to execute the cloud threads when the intent executes. – Jeffrey Saydam Feb 26 '18 at 12:25
  • I tried to run your code exactly as it is, removing all other element except Cloud1 and its working absolutely fine clouds are moving.. Can you show your "R.layout.activity_main" file? – Abhinay Sharma Feb 27 '18 at 04:57