1

In trying to add animation to ImageViews, I don't seem to get the multithreading right which is giving me incorrect results.

This is the part where I am calling the animation >>

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

The problem here is that when I was not creating a separate thread, for the startAnimation() function, all 3 calls happened simultaneously and everything got messed up. But still now it is not working perfectly and the 1st & 3rd calls are jumbled up.

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

I tried to experiment with join() but it didn't work out, it is giving more radical outcomes now. I thought that using join() will ensure excution of the previous calls to startAnimation() before starting a new.

EDIT: I have tracked the issue and the issue is that my startAnimation() function is being called when the main Looper is running. So it is being executed after all three calls to the function has been made. I need to ensure that startAnimation() runs at the time when I make the call, and is executed before the next call to the same function happens. Any ideas how to ensure this?

4

2 回答 2

1

try to run this code it will help you understand a work around, once you run it it will be easier for you to understand,

make an layout name it animation_test_activity.xml code is below

<?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> 

now in make an activity named AnimationTest.java the code is as mentioned below.

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

}

This code is working you can test it as well that you may customize the way you want to use it.

Now we have a working example we will see how did we get there,

First if you try to animate same image over and over using AnimationSet or making different TranslateAnimation reference type to same image that doesn't work because a single image tries to be at two different places virtually at the same time. So what happens is only first animation is seen and others are ignored by android it is one of the result of repeat method doesn't work in android.

To solve this what we can do is have as many Views as we need to show different animation on each one a separate View this time. It works as each animation is related to single view and they can work concurrently.

I am in no position to tell not not to go for Multi-Threading but remember you are trying to do this on UI Thread so if you have many animations in your app it will bog your app with these threads that is quite possible.

if you want to listen to an event when a particular animation is over lets say animation then do this

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

                }
            });

by doing this you can start a new animation when previous is over :)

于 2015-03-05T05:15:01.277 回答
0

I tried and failed using Android tools. I went back and fixed it with multithreading.

Though there is a lot of other code that I changed, this is one of the important one w.r.t. my issue-

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

This is being called from the main controller as-

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

Basically, every time I need to run the animation, I make and tag the thread so I can run it in order.

Please add your answer if you have a better solution.

于 2015-03-05T19:30:41.663 回答