最近做的项目,客户临时要求改版,我真的是最烦这个,要求跟换主页面的背景,换上新的背景图,要求是动态的。
效果(我随便拿的五个图片做的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() {
@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 {
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);
}
}