一. 概述
Android中提供了View進行繪圖處理,View可以滿足大部分的繪圖需求撮执,但是有時候鞭达,View卻顯得力不從心,所以Android提供了SurfaceView給Android開發(fā)者北秽,以滿足更多的繪圖需求。下面就讓我們一起來了解一下SurfaceView最筒。
我們知道View是通過刷新來重繪視圖贺氓,系統(tǒng)通過發(fā)出VSSYNC信號來進行屏幕的重繪,刷新的時間間隔是16ms,如果我們可以在16ms以內將繪制工作完成床蜘,則沒有任何問題辙培,如果我們繪制過程邏輯很復雜,并且我們的界面更新還非常頻繁悄泥,這時候就會造成界面的卡頓虏冻,影響用戶體驗,為此Android提供了SurfaceView來解決這一問題弹囚。
View和SurfaceView的區(qū)別:
1 . View適用于主動更新的情況厨相,而SurfaceView則適用于被動更新的情況,比如頻繁刷新界面鸥鹉。
2 . View在主線程中對頁面進行刷新蛮穿,而SurfaceView則開啟一個子線程來對頁面進行刷新。
3 . View在繪圖時沒有實現雙緩沖機制毁渗,SurfaceView在底層機制中就實現了雙緩沖機制践磅。
雙緩沖技術是游戲開發(fā)中的一個重要的技術。當一個動畫爭先顯示時灸异,程序又在改變它府适,前面還沒有顯示完,程序又請求重新繪制肺樟,這樣屏幕就會不停地閃爍檐春。而雙緩沖技術是把要處理的圖片在內存中處理好之后,再將其顯示在屏幕上么伯。
雙緩沖主要是為了解決 反復局部刷屏帶來的閃爍疟暖。把要畫的東西先畫到一個內存區(qū)域里,然后整體的一次性畫出來田柔。
二. 如何使用SurfaceView
1 . 創(chuàng)建SurfaceView
我們需要自定義一個類繼承自SurfaceView俐巴,并且實現兩個接口以及接口定義的方法,當然硬爆,與自定義View類似欣舵,還要重寫三個構造函數。下面是代碼:
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
public SurfaceViewTemplate(Context context) {
this(context, null);
}
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
//創(chuàng)建
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//改變
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
//銷毀
}
@Override
public void run() {
//子線程
}
}
前面三個構造函數的寫法和自定義View是相同的摆屯,接下來的三個方法分別在SurfaceView創(chuàng)建邻遏、改變糠亩、銷毀的時候進行調用,最后的run()方法中寫我們子線程中執(zhí)行的繪圖邏輯即可准验。
2 . 初始化SurfaceView
這一步我們主要是定義三個成員變量以備后面繪圖時使用赎线,然后初始化這三個成員變量并且注冊對應的回調方法。代碼如下:
private SurfaceHolder mSurfaceHolder;
//繪圖的Canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
/**
* 初始化View
*/
private void initView(){
mSurfaceHolder = getHolder();
//注冊回調方法
mSurfaceHolder.addCallback(this);
//設置一些參數方便后面繪圖
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
}
public SurfaceViewSinFun(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//在三個參數的構造方法中完成初始化操作
initView();
}
3 . 使用SurfaceView
(1) 通過lockCanvas()方法獲得Canvas對象
(2) 在子線程中使用Canvas對象進行繪制
(3) 使用unlockCanvasAndPost()方法將畫布內容進行提交
注意: lockCanvas() 方法獲得的Canvas對象仍然是上次繪制的對象糊饱,由于我們是不斷進行繪制垂寥,但是每次得到的Canvas對象都是第一次創(chuàng)建的Canvas對象。
SurfaceView的繪制可以使用下面的模板代碼來實現另锋,唯一的不同就是繪制邏輯的不同滞项,代碼如下:
public class SurfaceViewTemplate extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder mSurfaceHolder;
//繪圖的Canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
public SurfaceViewTemplate(Context context) {
this(context, null);
}
public SurfaceViewTemplate(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SurfaceViewTemplate(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
//開啟子線程
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing){
drawSomething();
}
}
//繪圖邏輯
private void drawSomething() {
try {
//獲得canvas對象
mCanvas = mSurfaceHolder.lockCanvas();
//繪制背景
mCanvas.drawColor(Color.WHITE);
//繪圖
}catch (Exception e){
}finally {
if (mCanvas != null){
//釋放canvas對象并提交畫布
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
/**
* 初始化View
*/
private void initView(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
}
}
三. 案例
繪制正弦曲線
public class SurfaceViewSinFun extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder mSurfaceHolder;
//繪圖的Canvas
private Canvas mCanvas;
//子線程標志位
private boolean mIsDrawing;
private int x = 0, y = 0;
private Paint mPaint;
private Path mPath;
public SurfaceViewSinFun(Context context) {
this(context, null);
}
public SurfaceViewSinFun(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SurfaceViewSinFun(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(5);
mPath = new Path();
//路徑起始點(0, 100)
mPath.moveTo(0, 100);
initView();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mIsDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
mIsDrawing = false;
}
@Override
public void run() {
while (mIsDrawing){
drawSomething();
x += 1;
y = (int)(100 * Math.sin(2 * x * Math.PI / 180) + 400);
//加入新的坐標點
mPath.lineTo(x, y);
}
}
private void drawSomething() {
try {
//獲得canvas對象
mCanvas = mSurfaceHolder.lockCanvas();
//繪制背景
mCanvas.drawColor(Color.WHITE);
//繪制路徑
mCanvas.drawPath(mPath, mPaint);
} catch (Exception e){
} finally {
if ( mCanvas != null ) {
//釋放canvas對象并提交畫布
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
/**
* 初始化View
*/
private void initView(){
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
}
}