2016-03-03 108 views
0

首先,我嘗試了很多方法在Android中製作流暢的動畫,可能我最好的選擇是使用AnimationDrawable。在舊設備上出現內存異常之前,一切都是完美的。原因很明顯是幀數,在我的情況下是75.這就是我如何使用AsyncTaskThread.sleep()來動畫幀。爲了避免動畫滯後,我使用了一個Stack,其中我預加載了前10幀,然後彈出使用過的一個,並推入新的一個,直到沒有更多的幀。一切都比我預期的更好,但唯一的問題是,在動畫結束時,最後一幀消失了,我整天都在摸索着理解爲什麼這樣的事情顯然沒有成功。以下是我在其中調用動畫的活動以及動畫代碼所在的文件中的代碼。Android異步任務框架動畫

SplashActivity.java

private void startAnimation() { 
     gifImageView = (LogoAnimImageView) findViewById(R.id.gifImageView); 
     gifImageView.setSplashActivityContext(this); 
     gifImageView.setBackgroundResource(R.drawable.logo_frame_0); 
     gifImageView.setAnimImageViewListener(new LogoAnimImageView.LogoAnimImageViewInterface() { 
      @Override 
      public void animationEnd() { 
       mAnimationFinished = true; 
       LoadNextActivity(); 

      } 
     }); 
     gifImageView.startLogoAnimation(); 

    } 

LogoAnimImageView.java

public class LogoAnimImageView extends ImageView { 

    public interface LogoAnimImageViewInterface { 
     void animationEnd(); 
    } 

    final Handler mHandler = new Handler(); 

    private Stack<Drawable> mImageStack; 

    private SplashActivity mSplashActivity; 

    private LogoAnimImageViewInterface mListener; 

    private int mFrameIndex; 

    private int[] mResources = {R.drawable.logo_frame_0,R.drawable.logo_frame_1,R.drawable.logo_frame_2,R.drawable.logo_frame_3, 
      R.drawable.logo_frame_4,R.drawable.logo_frame_5,R.drawable.logo_frame_6, 
      R.drawable.logo_frame_7,R.drawable.logo_frame_8,R.drawable.logo_frame_9,R.drawable.logo_frame_10, 
      R.drawable.logo_frame_11,R.drawable.logo_frame_12,R.drawable.logo_frame_13,R.drawable.logo_frame_14, 
      R.drawable.logo_frame_15,R.drawable.logo_frame_16,R.drawable.logo_frame_17,R.drawable.logo_frame_18, 
      R.drawable.logo_frame_19,R.drawable.logo_frame_20,R.drawable.logo_frame_21,R.drawable.logo_frame_22, 
      R.drawable.logo_frame_23,R.drawable.logo_frame_24,R.drawable.logo_frame_25,R.drawable.logo_frame_26, 
      R.drawable.logo_frame_27,R.drawable.logo_frame_28,R.drawable.logo_frame_29,R.drawable.logo_frame_30, 
      R.drawable.logo_frame_31,R.drawable.logo_frame_32,R.drawable.logo_frame_33,R.drawable.logo_frame_34, 
      R.drawable.logo_frame_35,R.drawable.logo_frame_36,R.drawable.logo_frame_37,R.drawable.logo_frame_38, 
      R.drawable.logo_frame_39,R.drawable.logo_frame_40,R.drawable.logo_frame_41,R.drawable.logo_frame_42, 
      R.drawable.logo_frame_43,R.drawable.logo_frame_44,R.drawable.logo_frame_45,R.drawable.logo_frame_46, 
      R.drawable.logo_frame_47,R.drawable.logo_frame_48,R.drawable.logo_frame_49,R.drawable.logo_frame_50, 
      R.drawable.logo_frame_51,R.drawable.logo_frame_52,R.drawable.logo_frame_53,R.drawable.logo_frame_54, 
      R.drawable.logo_frame_55,R.drawable.logo_frame_56,R.drawable.logo_frame_57,R.drawable.logo_frame_58, 
      R.drawable.logo_frame_59,R.drawable.logo_frame_60,R.drawable.logo_frame_61,R.drawable.logo_frame_62, 
      R.drawable.logo_frame_63,R.drawable.logo_frame_64,R.drawable.logo_frame_65,R.drawable.logo_frame_66, 
      R.drawable.logo_frame_67,R.drawable.logo_frame_68,R.drawable.logo_frame_69,R.drawable.logo_frame_70, 
      R.drawable.logo_frame_71,R.drawable.logo_frame_72,R.drawable.logo_frame_73,R.drawable.logo_frame_74 

    }; 

    public LogoAnimImageView(Context context) { 
     super(context); 
    } 

    public LogoAnimImageView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
    } 

    public LogoAnimImageView(Context context, AttributeSet attrs, int defStyleAttr) { 
     super(context, attrs, defStyleAttr); 
    } 

    public void startLogoAnimation() { 

     mFrameIndex = 10; 
     mImageStack = new Stack<Drawable>(); 
     for (int i=1;i<=mFrameIndex;i++) { 
      Drawable drawable = getDrawable(mResources[i]); 
      mImageStack.push(drawable); 
     } 
     mFrameIndex++; 
     mSplashActivity.runOnUiThread(new Runnable() { 
      @Override 
      public void run() { 
       new LogoAnimOperation().execute((Object)null); 
      } 
     }); 



    } 

    public void setSplashActivityContext(SplashActivity splashActivity) { 
     this.mSplashActivity = splashActivity; 
    } 

    public void setAnimImageViewListener(LogoAnimImageViewInterface listener) { 
     this.mListener = listener; 
    } 

    private Drawable getDrawable(int id) { 
     Drawable drawable; 
     if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){ 
      drawable = mSplashActivity.getDrawable(id); 
     } else { 
      drawable = mSplashActivity.getResources().getDrawable(id); 
     } 
     return drawable; 
    } 

    private class LogoAnimOperation extends AsyncTask<Object,Void,String> { 

     @Override 
     protected String doInBackground(Object... params) { 
      int number=1; 
      while (mImageStack.size() > 1) { 
       try { 
        Thread.sleep(40); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       final Drawable drawable = mImageStack.pop(); 
       mSplashActivity.runOnUiThread(new Runnable() { 
        @Override 
        public void run() { 

         if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
          LogoAnimImageView.this.setBackground(drawable); 
         } 
         else { 
          LogoAnimImageView.this.setBackgroundDrawable(drawable); 
         } 
         if (mFrameIndex < mResources.length) { 
          Drawable newDrawable = getDrawable(mResources[mFrameIndex]); 
          mImageStack.push(newDrawable); 
          mFrameIndex++; 
         } 

        } 
       }); 

      } 

      return ""; 
     } 

     @Override 
     protected void onPostExecute(String s) { 
      mSplashActivity.runOnUiThread(new Runnable() { 
       @Override 
       public void run() { 
        Drawable drawable = getDrawable(R.drawable.logo_frame_74); 
        if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
         LogoAnimImageView.this.setBackground(drawable); 
        } 
        else { 
         LogoAnimImageView.this.setBackgroundDrawable(drawable); 
        } 
       } 
      }); 
      mListener.animationEnd(); 
      super.onPostExecute(s); 
     } 
    } 

} 

回答

2

...但唯一的問題是,在動畫的最後 框消失結束而我整天都在摸索着理解爲什麼 就是這樣發生的顯然沒有成功。

問題可能出在你的AsyncTask'sonPostExecute(String)

@Override 
protected void onPostExecute(String s) { 
    mSplashActivity.runOnUiThread(new Runnable() { 
     @Override 
     public void run() { 
      Drawable drawable = getDrawable(R.drawable.logo_frame_74); 
      if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
       LogoAnimImageView.this.setBackground(drawable); 
      } else { 
       LogoAnimImageView.this.setBackgroundDrawable(drawable); 
      } 
     } 
    }); 
    mListener.animationEnd(); 
    super.onPostExecute(s); 
} 
  1. onPostExecute(String)總是在UI線程調用。所以,mSplashActivity.runOnUiThread(....)是多餘的。

  2. 通過使用runOnUiThread(Runnable),您發佈到UI線程的事件隊列。所以,runnable會在輪到它時執行。但是,mSplashActivity.runOnUiThread(....)調用之後的代碼可能會在可運行之前執行。因此,mListener.animationEnd()可能會在您的LogoAnimImageView有機會顯示R.drawable.logo_frame_74之前被呼叫。

但是,這不應該發生在你的情況。如果runOnUiThread(Runnable)從UI線程(它是)被調用,Runnable而不是被髮送到事件隊列,並立即執行。

我懷疑這裏真正的問題是,動畫(R.drawable.logo_frame_74)的最後一幀與下一個活動的啓動之間沒有任何延遲。也許你可以將電話註釋到mListener.animationEnd(),以檢查動畫是否在最後或倒數第二幀結束。

儘管這是一個有趣的方法,而我之前從未見過,但我不得不說,你正在干預比你需要的更多的線程。如果你試圖加載Drawables爲和在需要的時候,有一個簡單的方法:

public class LogoAnimImageView extends ImageView { 

    .... 
    .... 

    // flag to indicate whether `mNextFrameDrawable` should continue loading the next frame 
    private boolean mStopAnimating; 

    // loads the next frame, and calls back to activity when done 
    private Runnable mNextFrameRunnable = new Runnable() { 
     @Override 
     public void run() { 
      if (!mStopAnimating) { 
       if (isFinishedAnimating() && mListener != null) { 
        mListener.animationEnd(); 
       } else { // Load next frame 
        setViewBg(getNextFrameDrawable()); 
        // Will load the next frame in 40 ms 
        postDelayed(this, 40L); 
       } 
      } 
     } 
    };  

    // This method can be set `public static` and placed in a separate `Utils` class 
    private void setViewBg(Drawable d) { 
     if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 
      setBackground(drawable); 
     } else { 
      setBackgroundDrawable(drawable); 
     } 
    } 

    private Boolean isFinishedAnimating() { 
     return mFrameIndex >= mResources.length; 
    } 

    // returns the next frame's drawable and increments the `mFrameIndex` pointer 
    private Drawable getNextFrameDrawable() { 
     return getDrawable(mResources[mFrameIndex++]); 
    } 

    // start animating 
    public void startLogoAnimation() { 
     mFrameIndex = 0; 
     mStopAnimating = false; 
     post(mNextFrameRunnable); 
    } 

    // stop animating 
    public void stopLogoAnimation() { 
     mStopAnimating = true; 
     removeCallbacks(mNextFrameRunnable); 
    } 

    .... 
    .... 
} 

AsyncTask既不需要,也不是設計來處理這樣的情況。

+0

我試過你的解決方案,它的效果很好。唯一的問題是動畫不是很流暢。你認爲使用帶預裝drawable的堆棧可以幫助提高動畫的性能嗎? – axel

+0

@axel對不起,我一直忙於完成一個項目。自那以後我沒有登錄過。爲了順利進行,我們需要從一幀轉換到下一幀。而不是調用'setViewBg(getNextFrameDrawable()); 'mNextFrameRunnable'內,我們可以使用一個持續時間爲35毫秒的'TransitionDrawable'來實現這種平滑。我真的希望你能夠挖掘一些其他資源來獲得答案。如果沒有,我可以用添加的轉換位更新答案。 – Vikram