SurfaceView的使用


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的區別:

Thread.Join()
阻塞調用線程,直至某個線程終止。在此期間,被阻塞線程繼續執行標准的COM和SendMessage消息泵。該方法使線程狀態包含ThreadState.SleepWaitJoin.
 
該方法可以用來確認某個線程是否結束。如果線程已經結束,則該方法立即返回,否則阻塞直至線程結束。在某些需要等待其他線程執行結束后,繼續后續操作時,可以使用該方法。
該方法有2個帶參的重載方法,可以指定阻塞的時間。超時或者線程結束時,該方法將返回。
 
Thread.Sleep(int milliSec)
在指定的時間段內掛起當前線程。此期間不執行的COM和SendMessage消息泵,也不會被系統調度並執行。該方法使線程狀態包含ThreadState.SleepWaitJoin.

 實例二:

調用攝像頭進行拍照:

需要權限:

<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);             } } } } } }

 

效果

 

轉自:

Android之SurfaceView使用總結


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM