1

My question is very similar to this question, except that I need it to work with fragments instead of Views.

As explained in that thread, there seems to be a bug(?) in Android when an AnimationListener is attached to an Animation, which results in the onAnimationEnd() callback getting called before the animation has really completely ended.

I'm using fullscreen fragments which are animated in the FragmentTransaction (v4 support library) to have a horizontal "roll-in" effect. This works smoothly everywhere except in the fragment where I start a camera preview. Therefore I want to only start the camera preview once the animation is finished.

I tried using this method in my Fragment:

@Override
public Animation onCreateAnimation(final int transit,final boolean enter,final int nextAnim)
{
  Animation anim=super.onCreateAnimation(transit, enter, nextAnim);

  if (anim==null && nextAnim!=0)
    anim=AnimationUtils.loadAnimation(getActivity(), nextAnim);

  if(anim!=null)
  {
    anim.setAnimationListener(new AnimationListener() {

    @Override
    public void onAnimationStart(Animation animation)
    {
    }

    @Override
    public void onAnimationRepeat(Animation animation)
    {
    }

    @Override
    public void onAnimationEnd(Animation animation)
    {
      scannerView.startCameraInHandlerThread();
    }
  });
}

Unfortunately starting the camera is quite an expensive operation and so my animation lags every time right before the animation is over. It looks ugly and my client is complaining about it.

I also thought about a cheap solution like waiting/sleeping on the camera thread, but the main thread actually needs to wait for the camera starting thread for synchronization otherwise it can deadlock in some cases, so sleeping actually makes the issue worse (the lag becomes longer).

Does anyone know how to solve this?


edit: someone asked for more code...

public void startCameraInHandlerThread()
{
  if (mThread == null)
    mThread = new CameraHandlerThread();

  synchronized (mThread) {
    mThread.openCamera();
  }
}

private class CameraHandlerThread extends HandlerThread
{
  Handler mHandler = null;

  CameraHandlerThread()
  {
    super("CameraHandlerThread");
    start();
    mHandler = new Handler(getLooper());
  }

  synchronized void notifyCameraActionCompleted() {
    notify();
  }

  void openCamera()
  {
    mHandler.post(new Runnable() {
      @Override
      public void run() {
        startCamera();
        notifyCameraActionCompleted();
      }
    });

    try
    {
      wait();
    }
    catch (InterruptedException e)
    {
      Thread.currentThread().interrupt();
      LOG.warn("Waiting for camera opening was interrupted");
    }
  }
}
Community
  • 1
  • 1
peedee
  • 3,257
  • 3
  • 24
  • 42

1 Answers1

0

I was able to come up with a workaround: Just delay the start of my method for a very short time to give the animation time to run to completion. On the devices that my client is going to use this works fine but I don't feel that it's a general enough solution, therefore I'm not going to mark this as the accepted answer.

@Override
public void onAnimationEnd(Animation animation)
{
  new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
      scannerView.startCameraInHandlerThread();
    }
  }, 10); // will start after 10ms
}
peedee
  • 3,257
  • 3
  • 24
  • 42
  • in onCreateAnimation return a test Animation that does nothing (class A extends Animation) but overrides applyTransformation and Log.d interpolatedTime parameter, see if it is called after onAnimationEnd callback was called – pskink Nov 17 '14 at 09:47