Android 用surfaceview模擬幀動畫的效果,解決幀動畫的OOM問題
阿新 • • 發佈:2019-02-06
最近做的專案,客戶臨時要求改版,我真的是最煩這個,要求跟換主頁面的背景,換上新的背景圖,要求是動態的。
效果(我隨便拿的五個圖片做的gif):
方案:
幀動畫方案:
缺點:1.好像只能imageview才能播放幀動畫
2.容易OOM(播三四張還行,播九十張以上,且,每張都在300k左右就有OOM問題)
3.看到網上方案解決幀動畫OOM,是一次播放十張,再往裡面添十張,再播放。問題就有了,動畫有一個 新增十個圖片的時間卡頓。
surfaceview方案:
因為imageview要是以一張一張換,效能必定不如surfaceview。核心應該是
canvas = surfaceHolder.lockCanvas(rect);
canvas.drawBitmap(lruCache.get(folderName + "/" + assets[position]), null, rect, null);
我把上百張的圖片放在assets資料夾下,然後讀取圖片(這邊可以做Lrucache優化),下邊貼出我的程式碼:surfaceHolder = sv_main.getHolder(); lruCache = new StringBitmapLruCache(); surfaceHolder.addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(SurfaceHolder holder) { } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { rect = new Rect(0, 0, width, height); //獲取主題 SharedPreferences sp = getSharedPreferences("setting", Context.MODE_PRIVATE); String theme = sp.getString("theme", "bgone"); switch (theme) { case "bgone": folderName = "bgone"; break; case "bgtwo": folderName = "bgtwo"; break; case "bgthree": folderName = "bgthree"; break; } try { assetManager = getAssets(); assets = assetManager.list(folderName); totalCount = assets.length; if (lruCache.get(folderName + "/" + assets[0]) == null) { Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[0])); lruCache.put(folderName + "/" + assets[0], bitmap); } canvas = surfaceHolder.lockCanvas(rect); canvas.drawBitmap(lruCache.get(folderName + "/" + assets[0]), null, rect, null); holder.unlockCanvasAndPost(canvas); if (!isFlag) { startDecodeThread(); isFlag = true; } } catch (IOException e) { e.printStackTrace(); } isFlag = true; } @Override public void surfaceDestroyed(SurfaceHolder holder) { RxUtils.unsubscribe(animationSub); } });
private void startDecodeThread() { RxUtils.unsubscribe(animationSub); animationSub = Observable.interval(200, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io()).subscribe(new Subscriber<Long>() { @Override public void onCompleted() { } @Override public void onError(Throwable e) { } @Override public void onNext(Long aLong) { Logger.i("DDSSK::" + position); if (position >= totalCount) { position = 0; } canvas = surfaceHolder.lockCanvas(rect); // if (canvas != null) { canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR); if (lruCache.get(folderName + "/" + assets[position]) == null) { try { Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[position])); lruCache.put(folderName + "/" + assets[position], bitmap); } catch (IOException e) { e.printStackTrace(); } } canvas.drawBitmap(lruCache.get(folderName + "/" + assets[position]), null, rect, null); surfaceHolder.unlockCanvasAndPost(canvas); /* try { Bitmap bitmap = BitmapFactory.decodeStream(assetManager.open(folderName + "/" + assets[0])); canvas.drawBitmap(bitmap, null, rect, null); surfaceHolder.unlockCanvasAndPost(canvas); } catch (IOException e) { e.printStackTrace(); }*/ position++; } }); }
好,下面講一下我遇到的坑
1.surfaceview準備的時候,介面是一片黑。所以在surfacechanged的時候我要展示動畫的第一張圖,這樣就不黑了
2.canvas為空?因為canvas是從surfaceview獲得的,如果surfaceview沒有準備好,它絕對為空
3.如果介面跳轉再返回,surfaceview還會建立,但是我點了黑屏再亮屏,surfaceview就不會在建立,動畫就播不了,所以要寫一個開關,在onresume裡寫程式碼觸發動畫
4.surfaceview的優化,因為播放太多的圖片,佔用太多資源,導致其他的元件超卡。因為surfaceview可以在子執行緒跟新UI,所以我們可以起執行緒輪播圖
附件:
package utils;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
/**
* Created by Administrator on 2017/6/23 0023.
*/
public class StringBitmapLruCache extends LruCache<String, Bitmap> {
public StringBitmapLruCache() {
// 構造方法傳入當前應用可用最大記憶體的八分之一
super((int) (Runtime.getRuntime().maxMemory() / 1024 / 8));
}
@Override
// 重寫sizeOf方法,並計算返回每個Bitmap物件佔用的記憶體
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount() / 1024;
}
@Override
// 當快取被移除時呼叫,第一個引數是表明快取移除的原因,true表示被LruCache移除,false表示被主動remove移除,可不重寫
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap
newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
}
@Override
// 當get方法獲取不到快取的時候呼叫,如果需要建立自定義預設快取,可以在這裡新增邏輯,可不重寫
protected Bitmap create(String key) {
return super.create(key);
}
}
結束語:雖然這個需求是實現了,但造成了另外一個問題,apk過大,因為播放一個主題,就是至少90圖,每張300k。我要實現三個主題,那就是五六十M。於是我就在想,當初要是弄成視訊檔案,我播放視屏的話,既解決了OOM也解決了APK過大的問題。