@linux1s1s
2017-01-22T08:31:13.000000Z
字数 3028
阅读 2807
AndroidView 2015-05
public class MainActivity extends ActionBarActivity{@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(new SurfaceViewExtend(this));}}
import android.content.Context;import android.content.res.Resources;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.graphics.Canvas;import android.graphics.Matrix;import android.graphics.Paint;import android.view.SurfaceHolder;import android.view.SurfaceView;public class SurfaceViewExtend extends SurfaceView implements SurfaceHolder.Callback{private Bitmap mBackground;private Bitmap mDirector;private final SurfaceHolder mHolder;private Worker mRunnable;public SurfaceViewExtend(Context context){super(context);final Resources resources = getResources();mBackground = BitmapFactory.decodeResource(resources, R.mipmap.ic_bg);mDirector = BitmapFactory.decodeResource(resources, R.mipmap.ic_directer);mHolder = this.getHolder();mHolder.addCallback(this);}@Overridepublic void surfaceCreated(SurfaceHolder holder){mRunnable = new Worker();new Thread(mRunnable).start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height){}@Overridepublic void surfaceDestroyed(SurfaceHolder holder){mRunnable.stopRunning();}private class Worker implements Runnable{private boolean running = true;@Overridepublic void run(){Canvas canvas = null;int rotate = 0;while (running){try{canvas = mHolder.lockCanvas();Paint paint = new Paint();if (canvas == null) return;canvas.drawBitmap(mBackground, 0, 0, paint);Matrix matrix = new Matrix();matrix.postRotate((rotate += 48) % 360,mDirector.getWidth() / 2, mDirector.getHeight() / 2);matrix.postTranslate(200, 200);canvas.drawBitmap(mDirector, matrix, paint);Thread.sleep(33);}catch (InterruptedException e){e.printStackTrace();}finally{if (canvas != null)mHolder.unlockCanvasAndPost(canvas);}}}public void stopRunning(){running = false;}}}
上面的代码我们基本上搞清楚了几件事情:
我们在代码中设置的帧频最大值是每秒30帧,而实际运行时的帧频根据目测就能看出是到不了30帧的,这是因为程序在每一帧都要对整个画面进行重绘,过多的时间都被用作绘图处理,所以难以达到最大帧频。
接下来我们将采取脏矩形刷新的方法来优化性能,所谓脏矩形刷新,意为仅刷新有新变化的部分所在的矩形区域,而其他没用的部分就不去刷新,以此来减少资源浪费。
我们可以通过在获取Canvas画布时,为其指派一个参数来声明我们需要画布哪个局部,这样就可以只获得这个部分的控制权:
上面的代码几乎不要改动,只需要更改一部分代码:
L61行,我们在获取画布的时候指定一个矩形,然后每次都是刷新这么一点区域:
canvas = holder.get().lockCanvas(new Rect(200, 200, 300, 300));
比较之前的代码,效果改善许多,但是当我们再一次扩大刷新区域的话,效果几乎微乎其微了,如何进一步优化?
试想一下,我们每次刷新时最大的消耗在哪?
没错,在背景图绘制上,这个绘制区域非常大,会消耗我们很多资源,但实际上背景图在此例中是从不变化的,也就是说我们浪费了很多资源在无用的地方。
那么可不可以只绘制一次背景,以后每次都只绘制会动的问号图形呢?
完全可以,尝试修改一下代码,再前面加一个帧计数器,然后我们仅在第一帧的时候绘制背景:
...int framCount = 0;while (running){try{canvas = holder.get().lockCanvas(new Rect(200, 200, 300, 300));Paint paint = new Paint();if (canvas == null) return;if (framCount ++ < 1){canvas.drawBitmap(background.get(), 0, 0, paint);}...}}
当你运行以后,会发生会奇怪的现象,背景图片没有了?
背景会在背景图和黑色背景之间来回闪。
这个问题其实是源于SurfaceView的双缓冲机制,我理解就是它会缓冲前两帧的图像交替传递给后面的帧用作覆盖,这样由于我们仅在第一帧绘制了背景,第二帧就是无背景状态了,且通过双缓冲机制一直保持下来,解决办法就是改为在前两帧都进行背景绘制:
所以我们这样修改:
if (framCount ++ < 2){canvas.drawBitmap(background.get(), 0, 0, paint);}
这样就可以了,效率提高了不少。
