Android 多執行緒載入幀動畫
阿新 • • 發佈:2018-12-11
幀動畫在使用時,很容易造成卡頓。此程式碼使用多執行緒動態載入,防止卡頓。
使用方法:
int[] right_res_id = new int[]{R.drawable.r_00072, R.drawable.r_00073, R.drawable.r_00074, R.drawable.r_00075}; final AnimationsContainer.FramesSequenceAnimation rightAnim = AnimationsContainer.getInstance().createProgressDialogAnim(imgRight,right_res_id); rightAnim.setOnAnimStopListener(new AnimationsContainer.OnAnimationListener() { @Override public void onAnimationStart(AnimationsContainer.FramesSequenceAnimation animation) { //TODO } @Override public void onAnimationEnd(AnimationsContainer.FramesSequenceAnimation animation) { //TODO } @Override public void onAnimationStopOrCancel(AnimationsContainer.FramesSequenceAnimation animation) { //TODO } }); //rightAnim.setLoop(true); //rightAnim.setGoBack(true); //只有動畫停止時才會呼叫 //rightAnim.setDelayMillis(58); //設定每幀播放時間,與setDuration(long)二選一 rightAnim.setDuration(3000); //設定播放總時間 rightAnim.start(); //rightAnim.stop(); //rightAnim.goBackStart(); //返回第一幀 //rightAnim.cancel();
程式碼AnimationsContainer.java
import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import android.os.Handler; import android.widget.ImageView; import java.lang.ref.SoftReference; /** * 多執行緒幀動畫載入類 */ public class AnimationsContainer { private static final AnimationsContainer mInstance = new AnimationsContainer(); private AnimationsContainer() { } /** * 獲取單例 */ public static AnimationsContainer getInstance() { return mInstance; } /** * 建立一個 Imageview 的播放動畫類 * * @param imageView 要播放動畫的ImageView * @param resId 資源id陣列 * @return progress dialog animation */ public FramesSequenceAnimation createProgressDialogAnim(ImageView imageView, int[] resId) { return new FramesSequenceAnimation(imageView, resId); } /** * 迴圈讀取幀---迴圈播放幀 */ public class FramesSequenceAnimation { private int[] mFrames; // 幀陣列 private int mIndex; // 當前幀 private boolean mShouldRun; // 開始/停止播放用 private boolean mIsRunning; // 動畫是否正在播放,防止重複播放 private SoftReference<ImageView> mSoftReferenceImageView; // 軟引用ImageView,以便及時釋放掉 private Handler mHandler; private long mDelayMillis = 58; private boolean isLoop = false; private boolean isGoBack = true; private OnAnimationListener mOnAnimationListener; //播放停止監聽 private Bitmap mBitmap = null; private BitmapFactory.Options mBitmapOptions;//Bitmap管理類,可有效減少Bitmap的OOM問題 public FramesSequenceAnimation(ImageView imageView, int[] frames) { mHandler = new Handler(); mFrames = frames; mIndex = -1; mSoftReferenceImageView = new SoftReference<ImageView>(imageView); mShouldRun = false; mIsRunning = false; imageView.setImageResource(mFrames[0]); // 當圖片大小型別相同時進行復用,避免頻繁GC if (Build.VERSION.SDK_INT >= 11) { Bitmap bmp = ((BitmapDrawable) imageView.getDrawable()).getBitmap(); int width = bmp.getWidth(); int height = bmp.getHeight(); Bitmap.Config config = bmp.getConfig(); mBitmap = Bitmap.createBitmap(width, height, config); mBitmapOptions = new BitmapFactory.Options(); //設定Bitmap記憶體複用 mBitmapOptions.inBitmap = mBitmap;//Bitmap複用記憶體塊,類似物件池,避免不必要的記憶體分配和回收 mBitmapOptions.inMutable = true;//解碼時返回可變Bitmap mBitmapOptions.inSampleSize = 1;//縮放比例 } } /** * 迴圈讀取下一幀 */ private int getNext() { mIndex++; if (mIndex < mFrames.length) { return mFrames[mIndex]; } else if (mIndex >= mFrames.length && isLoop) { return mFrames[mIndex = 0]; } else if (isGoBack) { end(); return mFrames[mIndex = 0]; } else { end(); return mIndex = -1; } } /** * How long this animation should last. The duration cannot be negative. * 與<code>setDelayMillis(long)</code>方法二選一 * * @param duration Duration in milliseconds * @throws java.lang.IllegalArgumentException if the duration is < 0 * @attr ref android.R.styleable#Animation_duration * @see #setDelayMillis(long) */ public FramesSequenceAnimation setDuration(long duration) { if (mFrames.length == 0) { throw new IllegalArgumentException("Animation frame length == 0"); } if (duration < 0) { throw new IllegalArgumentException("Animators cannot have negative duration: " + duration); } mDelayMillis = duration / mFrames.length; return this; } /** * 直接設定每幀間隔時間,與 <code>setDuration(long)</code> 方法二選一 * * @param delayMillis 每幀間隔時間,數值推薦:58,單位:毫秒 * @throws java.lang.IllegalArgumentException if the duration is < 0 * @attr ref android.R.styleable#Animation_duration * @see #setDuration(long) */ public FramesSequenceAnimation setDelayMillis(long delayMillis) { if (delayMillis < 0) { throw new IllegalArgumentException("Animators cannot have negative duration: " + delayMillis); } mDelayMillis = delayMillis; return this; } /** * 是否迴圈播放 * * @param isLoop */ public synchronized FramesSequenceAnimation setLoop(boolean isLoop) { this.isLoop = isLoop; return this; } /** * 是否返回第一幀 * * @param isGoBack */ public synchronized FramesSequenceAnimation setGoBack(boolean isGoBack) { this.isGoBack = isGoBack; return this; } /** * 播放動畫,同步鎖防止多執行緒讀幀時,資料安全問題 */ public synchronized void start() { mShouldRun = true; if (mIsRunning) return; Runnable runnable = new Runnable() { @Override public void run() { ImageView imageView = mSoftReferenceImageView.get(); if (!mShouldRun || imageView == null) { mIsRunning = false; return; } mIsRunning = true; //新開執行緒去讀下一幀 mHandler.postDelayed(this, mDelayMillis); if (imageView.isShown()) { int imageRes = getNext(); if (imageRes == -1) { return; } if (mBitmap != null) { // so Build.VERSION.SDK_INT >= 11 Bitmap bitmap = null; try { bitmap = BitmapFactory.decodeResource(imageView.getResources(), imageRes, mBitmapOptions); } catch (Exception e) { e.printStackTrace(); } if (bitmap != null) { imageView.setImageBitmap(bitmap); } else { imageView.setImageResource(imageRes); mBitmap.recycle(); mBitmap = null; } } else { imageView.setImageResource(imageRes); } } } }; mHandler.post(runnable); if (mOnAnimationListener != null) { mOnAnimationListener.onAnimationStart(FramesSequenceAnimation.this); } } /** * 暫停播放,下次start會繼續播放 */ public synchronized void stop() { if (!mShouldRun) { return; } mShouldRun = false; if (mOnAnimationListener != null) { mOnAnimationListener.onAnimationStopOrCancel(FramesSequenceAnimation.this); } } /** * 取消播放,下次start會從頭播放 */ public synchronized void cancel() { if (!mShouldRun) { return; } mShouldRun = false; mIndex = 0; if (mOnAnimationListener != null) { mOnAnimationListener.onAnimationStopOrCancel(FramesSequenceAnimation.this); } } /** * 播放結束 */ private synchronized void end() { if (!mShouldRun) { return; } mShouldRun = false; if (mOnAnimationListener != null) { mOnAnimationListener.onAnimationEnd(FramesSequenceAnimation.this); } } /** * 直接返回第一幀 */ public synchronized void goBackStart() { if (mFrames.length == 0) { throw new IllegalArgumentException("Animation frame length == 0"); } mIndex = 0; ImageView imageView = mSoftReferenceImageView.get(); imageView.setImageResource(mFrames[mIndex]); } /** * 設定停止播放監聽 * * @param listener */ public FramesSequenceAnimation setOnAnimStopListener(OnAnimationListener listener) { this.mOnAnimationListener = listener; return this; } } /** * 停止播放監聽 */ public interface OnAnimationListener { void onAnimationStart(FramesSequenceAnimation animation); void onAnimationEnd(FramesSequenceAnimation animation); void onAnimationStopOrCancel(FramesSequenceAnimation animation); } }