@linux1s1s
2017-01-22T16:31:13.000000Z
字数 3028
阅读 2488
AndroidView
2015-05
public class MainActivity extends ActionBarActivity
{
@Override
protected 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);
}
@Override
public void surfaceCreated(SurfaceHolder holder)
{
mRunnable = new Worker();
new Thread(mRunnable).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
{
}
@Override
public void surfaceDestroyed(SurfaceHolder holder)
{
mRunnable.stopRunning();
}
private class Worker implements Runnable
{
private boolean running = true;
@Override
public 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);
}
这样就可以了,效率提高了不少。