【问题标题】:ImageButton not working when multi-threading. How to solve this?多线程时 ImageButton 不起作用。如何解决这个问题?
【发布时间】:2018-02-26 11:21:46
【问题描述】:

在 Android Studio 中开发游戏时遇到了一个重大问题。我创建了一个创建单独线程的类。它的目的是成为一个云移动的动画:

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

当我在主活动中创建一个云时,按钮仍然可以工作,但是当我创建多个时,按钮大部分时间都不起作用。

此外,如果按钮有效并且已经发送了转到下一个活动的意图,则会发生错误:

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)

我从来没有遇到过这样的错误,我也不完全理解它的确切含义。

主要活动:

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


}

我已经尝试搜索类似的问题,但仍然没有结果。

【问题讨论】:

标签: java android multithreading


【解决方案1】:

你可以简单地使用:

runOnUiThread(new Runnable() {
 @Override
 public void run() {
      // do your work
}});

【讨论】:

  • 感谢您的回复,我已经尝试过了,但没有成功。这段代码的目的是什么?以及如何将其集成到我的代码中?这段代码只有在我把它放在 OnCreate() 方法中而不在其他任何地方时才有效。当我使用此代码时,我的云也不会再移动了。
  • 使用上下文从任何地方调用这个方法,比如.. ((Activity)context).runOnUiThread();
  • 在您的情况下,您必须定义一个全局上下文并在云类的构造函数中对其进行初始化
  • 当我使用你的方法时,我的应用程序将保持空白(只有一个白屏)。那是因为云方法处于持续循环中。这就是为什么我需要将线程分开的原因。当意图执行时,我需要找到一种方法来执行云线程。
  • 我试图完全按原样运行您的代码,删除了除 Cloud1 之外的所有其他元素,并且其运行良好的云正在移动。您能显示您的“R.layout.activity_main”文件吗?跨度>
猜你喜欢
  • 1970-01-01
  • 2014-01-28
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-01
  • 2021-09-28
相关资源
最近更新 更多