在尝试向ImageViews添加动画时,我似乎没有正确处理多线程,这给了我不正确的结果。
这是我调用动画>>的部分
startAnimation(gameController.getSecondImage()); // 1
...
startAnimation(gameController.getFirstImage()); // 2
...
startAnimation(gameController.getSecondImage()); // 3
...
这里的问题是,当我没有为startAnimation()
函数创建单独的线程时,所有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()
的调用。
EDIT:我已经跟踪了这个问题,问题是当主Looper运行时,我的startAnimation()
函数被调用了。因此,它是在对函数进行了所有三次调用之后执行的。我需要确保startAnimation()
在我进行调用时运行,并且在对同一函数的下一次调用发生之前执行。有什么办法确保这一点吗?
试着运行这段代码,它将帮助你理解解决方案,一旦你运行它,你就会更容易理解,
将布局名称设置为animation_st_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>
现在,在make一个名为AnimationTest.java的活动中,代码如下所述。
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工具,但失败了。我回去用多线程修复了它。
尽管我更改了很多其他代码,但这是关于我的问题的重要代码之一——
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();
}
基本上,每次我需要运行动画时,我都会制作并标记线程,以便按顺序运行它。
如果您有更好的解决方案,请添加您的答案