1. SurfaceView
View通過刷新來重繪視圖,Android系統(tǒng)通過發(fā)出VSYNC信號(hào)來進(jìn)行屏幕的重繪,刷新的時(shí)間間隔為16ms
在一些需要頻繁刷新,執(zhí)行很多邏輯操作的時(shí)候,超過了16ms,就會(huì)導(dǎo)致卡頓
SurfaceView
繼承之View
,但擁有獨(dú)立的繪制表面,即它不與其宿主窗口共享同一個(gè)繪圖表面,可以單獨(dú)在一個(gè)線程進(jìn)行繪制,并不會(huì)占用主線程的資源。這樣,繪制就會(huì)比較高效,游戲,視頻播放,還有最近熱門的直播,都可以用SurfaceView
SurfaceView
有兩個(gè)子類GLSurfaceView
和VideoView
SurfaceView
和View
的區(qū)別:
1.View
主要適用于主動(dòng)更新的情況下,而SurfaceView
主要適用于被動(dòng)更新,例如頻繁地刷新
2.View
在主線程中對(duì)畫面進(jìn)行刷新,而SurfaceView
通常會(huì)通過一個(gè)子線程來進(jìn)行頁面的刷新
3.View
在繪圖時(shí)沒有使用雙緩沖機(jī)制,而SufaceView
在底層實(shí)現(xiàn)機(jī)制中就已經(jīng)實(shí)現(xiàn)了雙緩沖機(jī)制
如果自定義
View
需要頻繁刷新,或者刷新時(shí)數(shù)據(jù)處理量比較大,就 可以考慮使用SurfaceView
來取代View
了
2. SurfaceView的使用模板
SurfaceView使用過程有一套模板代碼,大部分的SurfaceView都可以套用
3步走套路:
1.創(chuàng)建SurfaceView
2.初始化SurfaceView
3.使用SurfaceView
2.1 創(chuàng)建SurfaceView
創(chuàng)建一個(gè)自定義的SurfaceViewL,繼承之SurfaceView,并實(shí)現(xiàn)兩個(gè)接口SurfaceHolder.CallBack和Runnable
代碼:
public class SurfaceViewL extends SurfaceView implements SurfaceHolder.Callback,Runnable{
public SurfaceViewL(Context context, AttributeSet attrs) {
super(context, attrs);
}
@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() {
}
}
SurfaceHolder.CallBack有3個(gè)方法,分別在SurfaceView創(chuàng)建,改變,銷毀時(shí)進(jìn)行回調(diào)
SurfaceHolder.CallBack還有一個(gè)子Callback2接口,里面添加了一個(gè)surfaceRedrawNeeded (SurfaceHolder holder)方法
當(dāng)需要重繪SurfaceView中的內(nèi)容時(shí),可以使用這個(gè)接口。目前還不了解具體的使用場景
2.2 初始化SurfaceView
在自定義的SurfaceView中,通常需要3個(gè)成員變量
SurfaceHolder mSurfaceHolder 可以控制SurfaceView的大小,格式,可以監(jiān)控或者改變SurfaceView
Canvas mCanvas 畫布
boolean isDrawing 子線程標(biāo)志位,用來控制子線程
在構(gòu)造方法中,對(duì)SurfaceHolder mSurfaceHolder進(jìn)行初始化
public SurfaceViewL(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mSurfaceHolder = getHolder();//得到SurfaceHolder對(duì)象
mSurfaceHolder.addCallback(this);//注冊(cè)SurfaceHolder
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);//保持屏幕長亮
}
- setFocusable(true) 能否獲得焦點(diǎn)
- setFocusableInTouchMode(true) 能否通過觸摸獲得焦點(diǎn)
這兩個(gè)方法都是View類的方法,可以看看setFocusable與setFocusableInTouchMode差異以及clickable
2.3 使用SurfaceView
利用在2.2拿到的mSurfaceHolder對(duì)象,通過lockCanvas()方法獲得當(dāng)前的Canvas
注意:
lockCanvas()獲取到的Canvas對(duì)象還是上次的Canvas對(duì)象,并不是一個(gè)新的對(duì)象。之前的繪圖都將被保留,如果需要擦除,可以在繪制之前通過drawColor()方法來進(jìn)行清屏
繪制要充分利用SurfaceView的三個(gè)回調(diào)方法,在surfaceCreate()方法中開啟子線程進(jìn)行繪制。在子線程中,使用一個(gè)while(isDrawing)循環(huán)來不停地繪制。具體的繪制過程,由lockCanvas()方法進(jìn)行繪制,并通過unlockCanvasAndPost(mCanvas)進(jìn)行畫布內(nèi)容的提交
2.4 完整的模板代碼
public class SurfaceViewL extends SurfaceView implements SurfaceHolder.Callback, Runnable {
// SurfaceHolder
private SurfaceHolder mSurfaceHolder;
// 畫布
private Canvas mCanvas;
// 子線程標(biāo)志位
private boolean isDrawing;
public SurfaceViewL(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {//創(chuàng)建
isDrawing = true;
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//改變
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {//銷毀
isDrawing = false;
}
@Override
public void run() {
while (isDrawing){
drawing();
}
}
private void drawing() {
try {
mCanvas = mSurfaceHolder.lockCanvas();
//這里進(jìn)行內(nèi)容的繪制
...
}finally {
if (mCanvas != null){
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
mSurfaceHolder.unlockCanvasAndPost(mCanvas)將這行代碼放入finally代碼塊中,目的是為了確保內(nèi)容都能夠被提交
3. 簡單使用
簡易畫板的實(shí)現(xiàn):
代碼:
public class SurfaceViewL extends SurfaceView implements SurfaceHolder.Callback, Runnable {
// SurfaceHolder
private SurfaceHolder mSurfaceHolder;
// 畫布
private Canvas mCanvas;
// 子線程標(biāo)志位
private boolean isDrawing;
// 畫筆
Paint mPaint;
// 路徑
Path mPath;
private float mLastX, mLastY;//上次的坐標(biāo)
public SurfaceViewL(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
/**
* 初始化
*/
private void init() {
//初始化 SurfaceHolder mSurfaceHolder
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
setFocusable(true);
setFocusableInTouchMode(true);
this.setKeepScreenOn(true);
//畫筆
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mPaint.setStrokeWidth(10f);
mPaint.setColor(Color.parseColor("#FF4081"));
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
//路徑
mPath = new Path();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {//創(chuàng)建
isDrawing = true;
Log.e("surfaceCreated","--"+isDrawing);
//繪制線程
new Thread(this).start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {//改變
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {//銷毀
isDrawing = false;
Log.e("surfaceDestroyed","--"+isDrawing);
}
@Override
public void run() {
while (isDrawing){
drawing();
}
}
/**
* 繪制
*/
private void drawing() {
try {
mCanvas = mSurfaceHolder.lockCanvas();
mCanvas.drawColor(Color.WHITE);
mCanvas.drawPath(mPath,mPaint);
} finally {
if (mCanvas != null) {
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastX = x;
mLastY = y;
mPath.moveTo(mLastX, mLastY);
break;
case MotionEvent.ACTION_MOVE:
float dx = Math.abs(x - mLastX);
float dy = Math.abs(y - mLastY);
if (dx >= 3 || dy >= 3) {
mPath.quadTo(mLastX, mLastY, (mLastX + x) / 2, (mLastY + y) / 2);
}
mLastX = x;
mLastY = y;
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
/**
* 測量
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int wSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int hSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int hSpecSize = MeasureSpec.getSize(heightMeasureSpec);
if (wSpecMode == MeasureSpec.AT_MOST && hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, 300);
} else if (wSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(300, hSpecSize);
} else if (hSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(wSpecSize, 300);
}
}
}
本文章為轉(zhuǎn)載原貼