动画师只能在Looper线程上运行,但在UI上运行它们会导致程序冻结

时间:2018-10-11 19:17:32

标签: java android android-animation ui-thread

我正在尝试制作一个项目,如果计步器在过去3秒钟内检测到任何脚步,就会播放一个简单的动画。但是,这导致动画只能在Looper线程上运行。我尝试过制作方法来在“ WalkActivity”类中运行动画,因为动画在那里可以正常运行,但是发现它们仅在“ onCreate”下运行,否则抛出Looper错误。我还尝试使用Handlers和AsyncTask进行变体,但是它们会引起其他问题(冻结View中的所有gif)。

import android.animation.ObjectAnimator;
import pl.droidsonroids.gif.GifImageView;

public class WalkScreenAnimThread extends Thread {
    private int numSteps;
    private GifImageView closeCharacterView;
    private GifImageView farCharacterView;
    ObjectAnimator closeOffScreen;
    ObjectAnimator closeOnScreen;
    ObjectAnimator farStepLeft;
    ObjectAnimator farStepRight;
    int prevSteps;
    int currSteps;
    int remainingTime = 3000;
    int stage = 0;
    int stepsCaught = 0; // keeps track of steps taken within a certain timeframe
    int direction = 1;

    public WalkScreenAnimThread(GifImageView y, GifImageView z, ObjectAnimator a, ObjectAnimator b, ObjectAnimator c, ObjectAnimator d)
    {
        closeCharacterView = y;
        farCharacterView = z;
        closeOffScreen = a;
        closeOnScreen = b;
        farStepLeft = c;
        farStepRight = d;
    }

    public void setSteps(int steps)
    {
        numSteps = steps;
    }

    public void run() 
    {
        prevSteps = numSteps;
        currSteps = numSteps;
        try 
        {
            while(true) {
                currSteps = prevSteps+1; // currently this is set to constantly increase since android emulator doesn't include a pedometer, normally this would be "currSteps = numSteps;"
                if (prevSteps != currSteps)
                    stepsCaught += 1;

                if (stage == 0) {
                    if (stepsCaught > 2) { // require a few steps to start so the animation isn't triggered by one accidental step
                        stage = 1;
                        direction = 1;
                        stepsCaught = 0;
                        closeOffScreen.start(); //foreground sprite moves off screen
                        Thread.sleep(4000);
                    }
                    else if (remainingTime <= 0)
                        stepsCaught = 0;
                } else if (remainingTime % 1500 == 0) { // every second and a half, update the animation
                    if (remainingTime <= 0 && stepsCaught == 0) { // if three seconds have passed without any steps, take steps back to start
                        direction = -1;
                        farCharacterView.setRotationY(180);
                    } else if (stepsCaught > 0)
                        direction = 1;
                    if (direction > 0) { // do character fowards cycle https://cdn.discordapp.com/attachments/448275746147008534/499788306381406209/unknown.png
                        if (stage != 10) {
                            stage++;
                            if (stage >= 1 && stage <= 5) // background character has not reached the leftmost limit of the background, step left
                                farCharacterView.setRotationY(0);
                                farStepLeft.start();
                            else // background character has reached the leftmost limit of the background, step right
                                farCharacterView.setRotationY(180);
                                farStepRight.start();
                        } else { // background character has completed a full loop, rather than going off screen, turn character around
                            stage = 3;
                        }
                    } else // do character return to start cycle https://cdn.discordapp.com/attachments/448275746147008534/499788804807458817/unknown.png
                    {
                        if (stage >= 6 && stage <= 9) {
                            stage++;
                            farStepRight.start();
                        } else if (stage == 1) { // background character is off screen, return foreground character to screen
                            stage = 0;
                            closeOnScreen.start();
                            Thread.sleep(4000);
                        } else if (stage == 2)
                            stage = 10;
                        else if (stage == 10) {
                            stage = 1;
                            farStepRight.start();
                        } else if (stage >= 3) {
                            stage += 13 - stage * 2;
                            farStepRight.start();
                        }
                    }
                }

                if (remainingTime <= 0) // if 3 seconds have passed, set a new timer for 3 seconds
                    remainingTime = 3000;
                prevSteps = currSteps;
                Thread.sleep(100);
                remainingTime -= 100;
            }
        }
        catch (Exception e)
        {
            System.out.println(e);
        }
    }
}

此线程在此活动中运行:

import android.animation.ObjectAnimator;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import android.os.Handler;
import pl.droidsonroids.gif.GifImageView;

public class WalkActivity extends AppCompatActivity implements SensorEventListener, StepListener {
    private TextView textView;
    private StepDetector simpleStepDetector;
    private SensorManager sensorManager;
    private Sensor accel;
    private int numSteps;
    private TextView stepCountView;
    private WalkScreenAnimThread anim;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_walk);

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accel = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
        simpleStepDetector = new StepDetector();
        simpleStepDetector.registerListener(this);

        numSteps = 0;
        stepCountView = findViewById(R.id.stepCountView);
        stepCountView.setText("Steps taken: " + numSteps);
        sensorManager.registerListener(WalkActivity.this, accel, SensorManager.SENSOR_DELAY_FASTEST);
        GifImageView closeCharacterView = findViewById(R.id.closeCharacterView);
        GifImageView farCharacterView = findViewById(R.id.farCharacterView);

        ObjectAnimator a = ObjectAnimator.ofFloat(closeCharacterView, "translationX", 600f);
        a.setDuration(3500);

        ObjectAnimator b = ObjectAnimator.ofFloat(closeCharacterView, "translationX", -600f);
        b.setDuration(3500);

        ObjectAnimator c = ObjectAnimator.ofFloat(farCharacterView, "translationX", -124f);
        c.setDuration(5);

        ObjectAnimator d = ObjectAnimator.ofFloat(farCharacterView, "translationX", 124f);
        d.setDuration(5);

        anim = new WalkScreenAnimThread(closeCharacterView, farCharacterView, a, b, c, d);
        anim.start();
        // I have also tried variations of:
        //
        // new Handler().postDelayed(new Runnable() {
        //     @Override
        //     public void run() {
        //         runOnUiThread(anim);
        //     }
        // }, 100);
        //
        // but it just freezes the gif after the delay and nothing else happens.
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) {
            simpleStepDetector.updateAccel(
                event.timestamp, event.values[0], event.values[1], event.values[2]);
        }
    }

    @Override
    public void step(long timeNs) {
        numSteps++;
        stepCountView.setText("Steps taken: " + numSteps);
        anim.setSteps(numSteps);
    }
}

我该怎么做才能使这项工作成功?

编辑: 这是错误日志(在不使用Handler时发生。请注意,在使用Handler时不会发生任何错误,但在屏幕上显示所有gif图像时,不会出现动画):

Capturing and displaying logcat messages from application. This behavior can be disabled in the "Logcat output" section of the "Debugger" settings page.
D/skia: ---- fAsset->read(4096) returned 0
D/skia: --- SkAndroidCodec::NewFromStream returned null
D/MediaPlayer: setSubtitleAnchor in MediaPlayer
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)
I/System.out: android.util.AndroidRuntimeException: Animators may only be run on Looper threads
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)
D/OpenGLRenderer: endAllActiveAnimators on 0xa0274d80 (RippleDrawable) with handle 0x8eda0c00
D/EGL_emulation: eglMakeCurrent: 0x9bab3880: ver 2 0 (tinfo 0x9bab58d0)

0 个答案:

没有答案