Android 和多线程中的动画

Animation in Android and Multithreading

在尝试向 ImageView 添加动画时,我似乎没有正确使用多线程,这导致我得到不正确的结果。

这是我调用动画的部分>>

startAnimation(gameController.getSecondImage()); // 1
...
startAnimation(gameController.getFirstImage()); // 2
...
startAnimation(gameController.getSecondImage()); // 3
...

这里的问题是,当我没有为 startAnimation() 函数创建单独的线程时,所有 3 个调用同时发生,一切都变得一团糟。但是现在它仍然无法正常工作,并且第 1 次和第 3 次调用混乱不堪。

public void startAnimation(final ImageView image1) {

    final ImageView image2 = cardViewMap.get(cardSwapMap.get(image1.getId()));
    Runnable animate = new Runnable() {

        @Override
        public void run() {

            animationController.animate(image1, image2);
        }
    };
    Thread animationThread = new Thread(animate);
    animationThread.start();
    try {                                         //
        animationThread.join();                   //
    } catch (InterruptedException e) {            // Added Later
        Log.d("ERROR", e.toString());             //
    }                                             //
}

我尝试用 join() 进行试验,但没有成功,它现在给出了更激进的结果。我认为使用 join() 将确保在开始新的 startAnimation() 之前执行之前对 startAnimation() 的调用。

编辑: 我已经跟踪了这个问题,问题是我的 startAnimation() 函数在主 Looper 为 运行 时被调用。所以它是在对函数的所有三个调用都完成后执行的。我需要确保 startAnimation() 在我进行调用时运行,并在下一次调用同一函数之前执行。有什么想法可以确保这一点吗?

尝试 运行 这段代码将帮助您理解解决方法,一旦您 运行 它会让您更容易理解,

为其命名布局animation_test_activity.xml代码如下

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<ImageView
    android:id="@+id/imageView3"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true"
    android:layout_centerHorizontal="true"
    android:src="@drawable/ic_launcher"
    android:visibility="invisible" />

<ImageView
    android:id="@+id/imageView2"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/imageView3"
    android:layout_alignParentTop="true"
    android:src="@drawable/ic_launcher"
    android:visibility="invisible" />

<ImageView
    android:id="@+id/imageView1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/imageView3"
    android:layout_alignParentTop="true"
    android:src="@drawable/ic_launcher"
    android:visibility="invisible" />

<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignLeft="@+id/imageView3"
    android:layout_alignParentBottom="true"
    android:text="click me" />

</RelativeLayout> 

现在创建一个名为 AnimationTest.java 的 activity,代码如下所述。

package com.some.servewrtest;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.ImageView;

public class AnimationTest extends Activity implements OnClickListener {

private ImageView[] imageViews = new ImageView[3];
private Button button;


@Override
protected void onCreate(Bundle savedInstanceState) {
    // TODO Auto-generated method stub
    super.onCreate(savedInstanceState);
    setContentView(R.layout.animation_test_activity);

    initializeUI();
}

private void initializeUI() {
    // TODO Auto-generated method stub
    imageViews[0] = (ImageView) findViewById(R.id.imageView1);
    imageViews[1] = (ImageView) findViewById(R.id.imageView2);
    imageViews[2] = (ImageView) findViewById(R.id.imageView3);
    button = (Button)findViewById(R.id.button1);
    button.setOnClickListener(this);
}

@Override
public void onClick(View v) {
    // TODO Auto-generated method stub
    AnimationSet set = new AnimationSet(true);
    TranslateAnimation[] animations = new TranslateAnimation[3];
    int duration = 300;
    animations[0] = createCoins(0, duration*1, 0, 0, 0, 270); 
    animations[1] = createCoins(1, duration*2, 0, -100, 0, 270); 
    animations[2] = createCoins(2, duration*3, 0, 100, 0, 270);
    set.addAnimation(animations[0]);
    set.addAnimation(animations[1]);
    set.addAnimation(animations[2]);
    set.start();
}

private TranslateAnimation createCoins(int i, int duration, int fromXDelta, int toXDelta, int fromYDelta, int toYDelta) {
    // TODO Auto-generated method stub
    TranslateAnimation animation = new TranslateAnimation(fromXDelta, toXDelta, fromYDelta, toYDelta);
    animation.setDuration(duration+1900);
    animation.setFillAfter(false);
    imageViews[i].setVisibility(ImageView.VISIBLE);
    imageViews[i].setAnimation(animation);
    imageViews[i].setVisibility(ImageView.INVISIBLE);
    return animation;
}

}

此代码有效,您也可以对其进行测试,您可以自定义使用它的方式。

现在我们有了一个工作示例,我们将看看我们是如何做到这一点的,

首先,如果您尝试使用 AnimationSet 或为同一图像制作不同的 TranslateAnimation 引用类型来一遍又一遍地为同一图像制作动画,这将不起作用,因为单个图像试图几乎同时出现在两个不同的位置。所以发生的事情是只看到第一个动画而其他动画被 android 忽略,这是重复方法在 android 中不起作用的结果之一。

为了解决这个问题,我们可以做的是拥有尽可能多的视图,这次我们需要在每个单独的视图上显示不同的动画。它的工作原理是每个动画都与单个视图相关,并且它们可以同时工作。

我无法告诉你不要选择多线程,但请记住你正在尝试在 UI 线程上执行此操作,所以如果你的应用程序中有很多动画,它会使你的应用程序陷入困境这些线程很有可能。

如果你想在特定动画结束时收听事件让我们说动画然后这样做

animation..setAnimationListener(new AnimationListener() {

                @Override
                public void onAnimationStart(Animation animation) {
                    // TODO Auto-generated method stub
                    Log.d(TAG, "Animation has started");
                }

                @Override
                public void onAnimationRepeat(Animation animation) {
                    // TODO Auto-generated method stub
                    Log.d(TAG, "Animation is repeat");
                }

                @Override
                public void onAnimationEnd(Animation animation) {
                    // TODO Auto-generated method stub
                    Log.d(TAG, "Animation is over");

                }
            });

通过这样做,您可以在上一个动画结束时开始一个新动画:)

我尝试使用 Android 工具但失败了。我回去用多线程修复它。

虽然我更改了很多其他代码,但这是重要的代码之一 w.r.t。我的问题-

package thakur.rahul.colourmemory.controller;

import thakur.rahul.colourmemory.view.Animation.DisplayNextView;
import thakur.rahul.colourmemory.view.Animation.Flip3dAnimation;
import android.app.Activity;
import android.view.animation.AccelerateInterpolator;
import android.widget.ImageView;

public class AnimationController implements Runnable {

    private Activity activity;
    private ImageView image1;
    private ImageView image2;
    private boolean isFirstImage = true;
    private volatile static int numThread = 1;
    private volatile static int threadAllowedToRun = 1;
    private int myThreadID;
    private volatile static Object myLock = new Object();
    private volatile static boolean hasNotSlept = true;
    public volatile static boolean fixForMatch = false;

    public AnimationController(Activity activity, ImageView image1, ImageView image2) {

        this.activity = activity;
        this.image1 = image1;
        this.image2 = image2;
        this.myThreadID = numThread++;
    }

    @Override
    public void run() {

        synchronized (myLock) {
            while (myThreadID != threadAllowedToRun)
                try {
                    myLock.wait();
                } catch (InterruptedException e) {
                } catch (Exception e) {
                }
            animate();
            if (myThreadID % 2 == 0)
                if (hasNotSlept || fixForMatch)
                    try {
                        Thread.sleep(1000);
                        hasNotSlept = !hasNotSlept;
                    } catch (InterruptedException e) {
                    }
                else
                    hasNotSlept = !hasNotSlept;
            myLock.notifyAll();
            threadAllowedToRun++;
        }
    }

    public void animate() {

        if (isFirstImage) {
            applyRotation(0, 90);
            isFirstImage = !isFirstImage;
        } else {
            applyRotation(0, -90);
            isFirstImage = !isFirstImage;
        }
    }

    private void applyRotation(float start, float end) {

        final float centerX = image1.getWidth() / 2.0f;
        final float centerY = image1.getHeight() / 2.0f;
        final Flip3dAnimation rotation = new Flip3dAnimation(start, end, centerX, centerY);
        rotation.setDuration(300);
        rotation.setFillAfter(true);
        rotation.setInterpolator(new AccelerateInterpolator());
        rotation.setAnimationListener(new DisplayNextView(isFirstImage, image1, image2));
        if (isFirstImage)
            activity.runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    image1.startAnimation(rotation);
                }
            });
        else
            activity.runOnUiThread(new Runnable() {

                @Override
                public void run() {

                    image2.startAnimation(rotation);
                }
            });
    }
}

这是从主控制器调用的-

public void startAnimation(ImageView image1) {

    ImageView image2 = cardViewMap.get(cardSwapMap.get(image1.getId()));
    animationController = new AnimationController(activity, image1, image2);
    animationThread = new Thread(animationController);
    animationThread.start();
}

基本上,每次我需要 运行 动画时,我都会制作并标记线程,这样我就可以按顺序 运行 它。

如果您有更好的解决方案,请添加您的答案。