在 Android 音視訊開發學習思路 裡面,我們寫到了,想要逐步入門音視訊開發,就需要一步步的去學習整理,並積累。本文是音視訊開發指南的第一篇。 對應的要學習的內容是:在 Android 平臺繪製一張圖片,使用3 種不同的 API,ImageView、SurfaceView、自定義 View。
ImageView 繪製圖片
這個想必做過Android開發的都知道如何去繪製了:
// ImageView 載入幾種來源
//(1) drawable/mipmap 中通過 R.drawabe.xxx 載入圖片資源
//(2) assests或者sdcard的路徑的資源
// 這裡我以 assests 代表來載入資源
ImageView customImageView = findViewById(R.id.img_middle);
customImageView.setImageBitmap(Util.getImageFromAssetsFile(this, "prettygirl.png"));
// Util.getImageFromAssetsFile(this, "prettygirl.png")
public static Bitmap getImageFromAssetsFile(Context context, String fileName) {
Bitmap image = null;
AssetManager am = context.getResources().getAssets();
try {
InputStream is = am.open(fileName);
image = BitmapFactory.decodeStream(is);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
複製程式碼
很輕鬆,在介面上清晰的看到養眼的美女:
自定義 View 繪製圖片
有些時候我們需要載入圖片需要自定義View來載入圖片,通過上邊載入圖片的方式加上我自己自定義View 經驗,我們很快能寫出下邊通過自定義View的程式碼來載入圖片。
/**
* 自定義View 顯示圖片
*/
public class CustomImageView extends View {
private Bitmap mBitmap;
private Paint mPaint = new Paint();
···
private void init() {
initPaint();
}
private void initPaint() {
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
//設定抗鋸齒
mPaint.setAntiAlias(true);
}
public void setBitmap(Bitmap bitmap) {
mBitmap = bitmap;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mBitmap == null) {
return;
}
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
}
}
複製程式碼
SurfaceView 繪製圖片
這個比 ImageView 繪製圖片稍微複雜一點點,接下來呢,說說自己對它的理解
定義
SurfaceView是View的一個特殊子類,它的目的是另外提供一個執行緒進行繪製操作。
它的特性是:可以在主執行緒之外的執行緒中向螢幕繪圖上。這樣可以避免畫圖任務繁重的時候造成主執行緒阻塞,從而提高了程式的反應速度。
實現
首先繼承SurfaceView並實現SurfaceHolder.Callback介面,使用介面的原因:因為使用SurfaceView 有一個原則,所有的繪圖工作必須得在Surface 被建立之後才能開始(Surface—表面,這個概念在 圖形程式設計中常常被提到。基本上我們可以把它當作視訊記憶體的一個對映,寫入到Surface 的內容。
可以被直接複製到視訊記憶體從而顯示出來,這使得顯示速度會非常快),而在Surface 被銷燬之前必須結束。所以Callback 中的surfaceCreated 和surfaceDestroyed 就成了繪圖處理程式碼的邊界。
需要重寫的方法如下:
//在surface的大小發生改變時激發、
public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){}
//在建立時激發,一般在這裡呼叫畫圖的執行緒。
public void surfaceCreated(SurfaceHolder holder){}
//銷燬時激發,一般在這裡將畫圖的執行緒停止、釋放。
public void surfaceDestroyed(SurfaceHolder holder) {}
複製程式碼
使用SurfaceView大概流程:
- 繼承SurfaceView並實現SurfaceHolder.Callback介面
- SurfaceView.getHolder()獲得SurfaceHolder物件
- SurfaceHolder.addCallback(callback)新增回撥函式
- SurfaceHolder.lockCanvas()獲得Canvas物件並鎖定畫布
- Canvas繪畫
- SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定畫圖,並提交改變,將圖形顯示。
這裡用到了一個類SurfaceHolder,可以把它當成surface的控制器,就好比我們常用的MVC 設計模式,用來操縱Surface。處理它的Canvas上畫的效果和動畫,控制表面、大小、畫素等。下面我說幾個需要注意的方法:
// 給SurfaceView當前的持有者一個回撥物件。
abstract void addCallback(SurfaceHolder.Callback callback);
// 鎖定畫布,一般在鎖定後就可以通過其返回的畫布物件Canvas,在其上面畫圖等操作了。
abstract Canvas lockCanvas();
// 鎖定畫布的某個區域進行畫圖等..因為畫完圖後,會呼叫下面的unlockCanvasAndPost來改變顯示內容。
// 相對部分記憶體要求比較高的遊戲來說,可以不用重畫dirty外的其它區域的畫素,可以提高速度。
abstract Canvas lockCanvas(Rect dirty);
// 結束鎖定畫圖,並提交改變。
abstract void unlockCanvasAndPost(Canvas canvas);
複製程式碼
注意:每次利用SurfaceHolder獲得畫布時,前一次的內容將會保留。
我這裡貼一下重要部分的程式碼:
/**
* 自定義SurfaceView 繪製圖片
*/
public class CustomSurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {
····
private void init() {
mHolder = getHolder();
mHolder.addCallback(this);
mPaint = new Paint();
//獲取焦點
setFocusable(true);
setFocusableInTouchMode(true);
//設定常量
setKeepScreenOn(true);
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
isRunning = true;
mThread = new Thread(this);
mThread.start();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
isRunning = false;
}
@Override
public void run() {
//迴圈繪製
while (isRunning) {
draw();
}
}
private void draw() {
try {
mCanvas = mHolder.lockCanvas();
if (mCanvas != null) {
mCanvas.drawBitmap(Util.getImageFromAssetsFile(mContext, PICTURE_NAME), 0, 0, mPaint);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mCanvas != null) {
//釋放canvas
mHolder.unlockCanvasAndPost(mCanvas);
}
}
}
}
複製程式碼
原始碼地址(image包名下):https://github.com/StudyLifeTime/basicvideotutorial
總結
最後總結一下學習這三種載入方式,自定義View 和 ImageView 載入方式大同小異,就是自定義View注意畫筆的抗鋸齒操作,然後繪製圖片如果很浪費資源的情況下推薦使用 SurfaceView ,畢竟是在工作執行緒裡繪製不會影響到主執行緒的阻塞問題。
歡迎掃碼關注