1.概念
SurfaceView是View類的子類,可以直接從內存或者DMA等硬件接口取得圖像數據,是個非常重要的繪圖視圖。它的特性是:可以在主線程之外的 線程中向屏幕繪圖上。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,從而提高了程序的反應速度。在游戲開發中多用到SurfaceView,游戲中的背 景、人物、動畫等等盡量在畫布canvas中畫出。
SurfaceHolder是一個接口,其作用就像一個關於Surface的監聽器。提供訪問和控制SurfaceView背后的Surface 相關的方法 (providingaccess and control over this SurfaceView's underlying surface),它通過三個回調方法,讓我們可以感知到Surface的創建、銷毀或者改變。在SurfaceView中有一個方法getHolder,可以很方便地獲得SurfaceView所對應的Surface所對應的SurfaceHolder
所有SurfaceView和SurfaceHolder.Callback中聲明的方法,必須在運行SurfaceView窗口中的線程中調用(典型地,就是應用的主線程。譯注:即UI線程),因為它們需要正確地將同時被繪制線程訪問的各種狀態進行同步
SurfaceView可見就會被創建,不可見就會被銷毀
2.實現方法
1)實現步驟
a.繼承SurfaceView
b.實現SurfaceHolder.Callback接口
2)需要重寫的方法
(1)public void surfaceChanged(SurfaceHolder holder,int format,int width,int height){} //在surface的大小發生改變時激發
(2)public void surfaceCreated(SurfaceHolder holder){} //在創建時激發,一般在這里調用畫圖的線程。
(3)public void surfaceDestroyed(SurfaceHolder holder) {} //銷毀時激發,一般在這里將畫圖的線程停止、釋放。
3)SurfaceHolder
SurfaceHolder,surface的控制器,用來操縱surface。處理它的Canvas上畫的效果和動畫,控制表面,大小,像素等。
幾個需要注意的方法:
(1)、abstract void addCallback(SurfaceHolder.Callback callback);
// 給SurfaceView當前的持有者一個回調對象。
(2)、abstract Canvas lockCanvas();
// 鎖定畫布,一般在鎖定后就可以通過其返回的畫布對象Canvas,在其上面畫圖等操作了。
(3)、abstract Canvas lockCanvas(Rect dirty);
// 鎖定畫布的某個區域進行畫圖等..因為畫完圖后,會調用下面的unlockCanvasAndPost來改變顯示內容。
// 相對部分內存要求比較高的游戲來說,可以不用重畫dirty外的其它區域的像素,可以提高速度。
(4)、abstract void unlockCanvasAndPost(Canvas canvas);
// 結束鎖定畫圖,並提交改變。
4)總結整個過程
繼承SurfaceView並實現SurfaceHolder.Callback接口 ----> SurfaceView.getHolder()獲得SurfaceHolder對象 ---->SurfaceHolder.addCallback(callback)添加回調函數 ---->SurfaceHolder.lockCanvas()獲得Canvas對象並鎖定畫布----> Canvas繪畫 ---->SurfaceHolder.unlockCanvasAndPost(Canvas canvas)結束鎖定畫圖,並提交改變,將圖形顯示。
實例一:
畫圓:在xml文件中引用自定義SurfaceVIew就ok了
/** *按下home鍵,頁面不可見 * Created by Administrator on 2016/10/3. */ public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback{ private final SurfaceHolder holder; private Paint paint; private MyThread thread; private boolean isDraw = false; public MySurfaceView(Context context, AttributeSet attrs) { super(context, attrs); holder = this.getHolder(); holder.addCallback(this); //創建畫筆 createPaint(); } private void createPaint() { paint = new Paint(); paint.setColor(Color.BLUE); paint.setAntiAlias(true); //設置畫的樣式為畫邊框 paint.setStyle(Paint.Style.STROKE); } /** *頁面可見調用 * @param holder */ @Override public void surfaceCreated(SurfaceHolder holder) { //創建一個繪圖線程 thread = new MyThread(); isDraw = true; thread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /** * surfaceView頁面不可見調用 * @param holder */ @Override public void surfaceDestroyed(SurfaceHolder holder) { //該方法在主線程中運行 isDraw = false; Log.i("tag", "surfaceDestroyed: "); //join方法,阻塞線程,只有當當前線程執行完,才會執行其他線程的方法 try { thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } class MyThread extends Thread{ private int radius = 10; @Override public void run() { while(isDraw){ Log.i("tag", "run: "+Thread.currentThread().getName()); //同步,避免不同線程在同一個畫布上進行繪畫操作 synchronized (holder){ //鎖定畫布 Canvas canvas = holder.lockCanvas(); //第一次進入和退出程序時,canvas為空 if(canvas != null) { //畫圓 canvas.drawCircle(100, 100, radius, paint); radius += 10; if (radius > 70) { radius += 3; } // 睡眠,時間不能太長,否則和join方法會產生沖突 SystemClock.sleep(50); //解鎖畫布,並提交 holder.unlockCanvasAndPost(canvas); } } } } } }
join和sleep的區別:
實例二:
調用攝像頭進行拍照:
需要權限:
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback { private SurfaceView surfaceView; private ImageView image; private SurfaceHolder holder; private Camera camera; private boolean isDraw = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = (SurfaceView) findViewById(R.id.surfaceView); image = (ImageView) findViewById(R.id.image); holder = surfaceView.getHolder(); holder.addCallback(this); //打開照相機 camera = Camera.open(0); } public void onTakePhotos(View view){ /*參數1: 回調 *參數2: 原圖片回調 * 參數3: jpg格式圖片回調 * */ camera.takePicture(null, null, new Camera.PictureCallback() { /*data就是圖片的字節形式的數據*/ @Override public void onPictureTaken(byte[] data, Camera camera) { surfaceView.setVisibility(View.GONE); image.setVisibility(View.VISIBLE); image.setImageBitmap(BitmapFactory.decodeByteArray(data,0,data.length)); } }); } /*當surfaceView可見的時候調用*/ @Override public void surfaceCreated(SurfaceHolder holder) { try { //設置預覽參數,與 surfaceView綁定 camera.setPreviewDisplay(holder); //設置顯示的布局為垂直 camera.setDisplayOrientation(90); //開啟預覽 camera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } /*當surfaceView不可見的時候調用*/ @Override public void surfaceDestroyed(SurfaceHolder holder) { if(camera != null) { // camera.release(); camera.stopPreview(); } } }
效果圖:
播放gif圖片:在main--new dir--asserts文件夾,將圖片放入
public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback{ private SurfaceView surfaceView; private Movie movie; private SurfaceHolder surfaceHolder; private boolean flag; private MyThread myThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); surfaceView = (SurfaceView) findViewById(R.id.surfaceView); //獲得surfaceView的holder對象 surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(this); try { //將gif圖片拆分成一幀一幀的資源 movie = Movie.decodeStream(getResources().getAssets().open("new.gif")); } catch (IOException e) { e.printStackTrace(); } } @Override public void surfaceCreated(SurfaceHolder holder) { flag = true; // 開啟線程播放gif圖片 myThread = new MyThread(); myThread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public void surfaceDestroyed(SurfaceHolder holder) { //銷毀該線程 flag = false; try { myThread.join(); } catch (InterruptedException e) { e.printStackTrace(); } } class MyThread extends Thread{ @Override public void run() { super.run(); long startTime = System.currentTimeMillis(); while(flag){ synchronized (surfaceHolder){ //鎖定畫板 Canvas canvas = surfaceHolder.lockCanvas();
if(canvas != null){ //gif的播放的總時間 int duration = movie.duration();
Paint paint = new Paint(); //得到當前時間 long currentTime = System.currentTimeMillis(); // 計算當前應該播放到的位置 設置該時間點播放的幀 movie.setTime((int) ((currentTime-startTime)%duration)); //畫 movie.draw(canvas,200,200,null); //解鎖畫板 surfaceHolder.unlockCanvasAndPost(canvas); } } } } } }
效果
轉自: