系列文章目錄
講講Android為自定義view提供的SurfaceView
文章目錄
- 系列文章目錄
- 前言
- 一、Android為什么會提供SurfaceView
- 二、先看看Android Demo的實作
- 1.實作介面以及介面定義的方法
- 2.與Activity生命周期進行系結
- 3.完成初始化操作
- 4.實作
- 5.運行
- 三、繼承SurfaceView實作
- 1.自定義類繼承自SurfaceView,并且實作兩個介面以及介面定義的方法,
- 2.初始化
- 3.步驟與Android Demo的實作-4.實作類似
- 四、放一個使用案例原始碼
- 五、拓展一下(以下內容來源于網路)
前言
前幾天發表了幾篇在自定義view中通過修改值實作動態效果的文章,起到主要作用的是呼叫重繪界面的方法,但是假設繪制的程序邏輯比較復雜,并且界面更新頻繁,這時候就會造成界面的卡頓,十分影響用戶體驗感,
靈感來源于,Android官方demo(效果圖如下)

一、Android為什么會提供SurfaceView
View是通過重繪來重繪視圖,并且有一個重繪的間隔,當繪制程序邏輯很復雜加上界面更新還非常頻繁時,就可能無法在間隔內完成繪制,就會造成界面效果的卡頓,影響用戶體驗,為此Android提供了SurfaceView來解決這一問題,
二、先看看Android Demo的實作
1.實作介面以及介面定義的方法
....implements SurfaceHolder.Callback2
public void surfaceCreated(SurfaceHolder holder) {
synchronized (mDrawingThread) {
mDrawingThread.mSurface = holder;
mDrawingThread.notify();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
//這里不需要做任何事情;繪制執行緒將從畫布中獲取
}
public void surfaceRedrawNeeded(SurfaceHolder holder) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
//我們需要告訴繪圖執行緒停止
synchronized (mDrawingThread) {
mDrawingThread.mSurface = holder;
mDrawingThread.notify();
while (mDrawingThread.mActive) {
try {
mDrawingThread.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2.與Activity生命周期進行系結
@Override
protected void onPause() {
super.onPause();
//當我們暫停時,確保繪制執行緒沒有運行,
synchronized (mDrawingThread) {
mDrawingThread.mRunning = false;
mDrawingThread.notify();
}
}
@Override
protected void onResume() {
super.onResume();
//讓繪圖執行緒繼續運行,
synchronized (mDrawingThread) {
mDrawingThread.mRunning = true;
mDrawingThread.notify();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//確保繪圖執行緒消失,
synchronized (mDrawingThread) {
mDrawingThread.mQuit = true;
mDrawingThread.notify();
}
}
3.完成初始化操作
4.實作
- 通過
lockCanvas()方法獲得Canvas物件
//鎖定畫布進行繪圖,
Canvas canvas = mSurface.lockCanvas();
if (canvas == null) {
Log.i("WindowSurface", "Failure locking canvas");
continue;
}
- 在子執行緒中使用Canvas物件進行繪制
// 更新圖形
if (!mInitialized) {
mInitialized = true;
mPoint1.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
mPoint2.init(canvas.getWidth(), canvas.getHeight(), mMinStep);
mColor.init(127, 127, 1);
} else {
mPoint1.step(canvas.getWidth(), canvas.getHeight(),
mMinStep, mMaxStep);
mPoint2.step(canvas.getWidth(), canvas.getHeight(),
mMinStep, mMaxStep);
mColor.step(127, 127, 1, 3);
}
//顏色的效果
mBrightLine+=2;
if (mBrightLine > (NUM_OLD*2)) {
mBrightLine = -2;
}
// 清理背景
canvas.drawColor(mBackground.getColor());
// 畫舊線
for (int i=mNumOld-1; i>=0; i--) {
mForeground.setColor(mOldColor[i] | makeGreen(i));
mForeground.setAlpha(((NUM_OLD-i) * 255) / NUM_OLD);
int p = i*4;
canvas.drawLine(mOld[p], mOld[p+1], mOld[p+2], mOld[p+3], mForeground);
}
// 畫新線
int red = (int)mColor.x + 128;
if (red > 255) red = 255;
int blue = (int)mColor.y + 128;
if (blue > 255) blue = 255;
int color = 0xff000000 | (red<<16) | blue;
mForeground.setColor(color | makeGreen(-2));
canvas.drawLine(mPoint1.x, mPoint1.y, mPoint2.x, mPoint2.y, mForeground);
// 添加新的線條
if (mNumOld > 1) {
System.arraycopy(mOld, 0, mOld, 4, (mNumOld-1)*4);
System.arraycopy(mOldColor, 0, mOldColor, 1, mNumOld-1);
}
if (mNumOld < NUM_OLD) mNumOld++;
mOld[0] = mPoint1.x;
mOld[1] = mPoint1.y;
mOld[2] = mPoint2.x;
mOld[3] = mPoint2.y;
mOldColor[0] = color;
- 使用
unlockCanvasAndPost()方法將畫布內容進行提交
//全部完成
mSurface.unlockCanvasAndPost(canvas);
5.運行
//告訴活動的視窗,我們想做我們自己的繪制
getWindow().takeSurface(this);
//這是將繪制到我們的表面的執行緒,
mDrawingThread = new DrawingThread();
mDrawingThread.start();
三、繼承SurfaceView實作
1.自定義類繼承自SurfaceView,并且實作兩個介面以及介面定義的方法,
public class MyView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//創建
@Override
public void surfaceCreated(SurfaceHolder holder) {
}
//改變
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
//銷毀
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
//子執行緒
@Override
public void run() {
//子執行緒中執行的繪圖邏輯
}
}
2.初始化
mSurfaceHolder = getHolder();
mSurfaceHolder.addCallback(this);
......
3.步驟與Android Demo的實作-4.實作類似
- 通過
lockCanvas()方法獲得Canvas物件 - 在子執行緒中使用Canvas物件進行繪制(run())
- 使用
unlockCanvasAndPost()方法將畫布內容進行提交
try {
mCanvas = mSurfaceHolder.lockCanvas();
mCanvas.drawColor(Color.WHITE);
//繪制的邏輯
.....
}catch (Exception e){
}finally {
if (mCanvas != null){
//釋放canvas物件并提交畫布
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
四、放一個使用案例原始碼
效果見:Android自定義view之線條等待影片(靈感來源:金鏟鏟之戰)
public class MyView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
private SurfaceHolder mSurfaceHolder;
private Canvas mCanvas;
private int mWidth;
private int mHeight;
private int useWidth, minwidth;
private boolean viewContinue=true,viewContinue1=true;
private float mSweep,mSweep1;
private boolean runDrawing;
private Paint mPaint;
public MyView(Context context) {
super(context);
initView();
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//創建
@Override
public void surfaceCreated(SurfaceHolder holder) {
runDrawing = true;
new Thread(this).start();
}
//改變
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
//銷毀
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
runDrawing=false;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = w;
mHeight = h;
useWidth = mWidth;
if (mWidth > mHeight) {
useWidth = mHeight;
}
}
//子執行緒
@Override
public void run() {
while (runDrawing){
draw();
}
}
//繪制
private void draw() {
try {
//獲得canvas物件
mCanvas = mSurfaceHolder.lockCanvas();
//繪制背景顏色
mCanvas.drawColor(Color.WHITE);
minwidth = useWidth / 10;
mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5+mSweep,minwidth*5+mSweep,mPaint);
mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5-mSweep,minwidth*5-mSweep,mPaint);
mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5+mSweep,minwidth*5-mSweep,mPaint);
mCanvas.drawLine(minwidth*5,minwidth*5,minwidth*5-mSweep,minwidth*5+mSweep,mPaint);
mCanvas.drawLine(minwidth*5+mSweep,minwidth*5+mSweep,minwidth*5+mSweep,minwidth*5+mSweep-mSweep1,mPaint);
mCanvas.drawLine(minwidth*5-mSweep,minwidth*5-mSweep,minwidth*5-mSweep,minwidth*5-mSweep+mSweep1,mPaint);
mCanvas.drawLine(minwidth*5+mSweep,minwidth*5-mSweep,minwidth*5+mSweep-mSweep1,minwidth*5-mSweep,mPaint);
mCanvas.drawLine(minwidth*5-mSweep,minwidth*5+mSweep,minwidth*5-mSweep+mSweep1,minwidth*5+mSweep,mPaint);
if (viewContinue&&viewContinue1){
mSweep += 2;
if (mSweep > minwidth*2) {
viewContinue=false;
}
}
if (!viewContinue&&viewContinue1){
mSweep1 += 4;
if (mSweep1 > 4*minwidth) {
viewContinue1=false;
viewContinue=true;
}
}
if (viewContinue&&!viewContinue1){
if (mSweep1 <=0) {
mSweep-=4;
if (mSweep<0){
viewContinue=true;
viewContinue1=true;
}
}else{
mSweep1 -= 2;
}
}
//重繪View
invalidate();
}catch (Exception e){
}finally {
if (mCanvas != null){
//釋放canvas物件并提交畫布
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}
private void initView(){
mSurfaceHolder = getHolder();
//注冊回呼方法
mSurfaceHolder.addCallback(this);
setFocusable(true);
setKeepScreenOn(true);
setFocusableInTouchMode(true);
//初始化畫筆
initPaint();
}
private void initPaint() {
mPaint = new Paint(); //創建畫筆物件
mPaint.setColor(Color.BLACK); //設定畫筆顏色
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(4f); //設定畫筆寬度為10px
mPaint.setAntiAlias(true); //設定抗鋸齒
mPaint.setAlpha(255); //設定畫筆透明度
}
}
五、拓展一下(以下內容來源于網路)
View和SurfaceView的區別:
- View適用于主動更新的情況,而SurfaceView則適用于被動更新的情況,比如頻繁重繪界面,
- View在主執行緒中對頁面進行重繪,而SurfaceView則開啟一個子執行緒來對頁面進行重繪,
- View在繪圖時沒有實作雙緩沖機制,SurfaceView在底層機制中就實作了雙緩沖機制,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/302542.html
標籤:其他
上一篇:??【Android精進之路-04】Android核心組件Activity,必須掌握的知識點(Activity是什么,生命周期是怎樣的)??
