Android中SurfaceView學習

  SurfaceView和View的明顯不同在於Surface不需要通過線程來更新視圖,但在繪制之前必須使用lockCanvas方法鎖定畫佈,並得 到畫佈,然後繪制,完成後用unlockCanvasAndPost方法解鎖畫佈。SurfaceView類的事件處理和View一樣。

             首先來看一個簡單的框架。

繪制界面類:

[java]
package com.example.bonusball; 
 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.RectF; 
import android.util.AttributeSet; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
 
public class CanvasView extends SurfaceView implements SurfaceHolder.Callback 

    private SurfaceHolder myHolder; 
    private Paint ballPaint; // Paint used to draw the cannonball  
    private int screenWidth; // width of the screen  
    private int screenHeight; // height of the screen  
    private int ballRadius; 
    private CanvasThread myThread; 
    //控制循環  
    private boolean isLoop; 
 
    public CanvasView(Context context) { 
        super(context);  
        // TODO Auto-generated constructor stub  
        myHolder=this.getHolder(); 
        myHolder.addCallback(this); 
        ballPaint=new Paint(); 
        ballPaint.setColor(Color.BLUE); 
        isLoop = true; 
    } 
 
    public void fireBall(float startX,float startY) 
    { 
        System.out.println("Fire"); 
 
    } 
 
    @Override 
    public void surfaceChanged(SurfaceHolder holder, int format, int width, 
            int height) { 
        // TODO Auto-generated method stub  
 
 
    } 
    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
    { 
        super.onSizeChanged(w, h, oldw, oldh); 
        screenWidth = w; // store the width  
        screenHeight = h; // store the height  
        ballRadius=w/10; 
    } 
 
    @Override 
    public void surfaceCreated(SurfaceHolder holder) { 
        // TODO Auto-generated method stub  
        myThread = new CanvasThread(); 
        System.out.println("SurfaceCreated!"); 
        myThread.start();  
 
    } 
 
    @Override 
    public void surfaceDestroyed(SurfaceHolder holder) { 
        // TODO Auto-generated method stub  
        // 停止循環  
        isLoop = false; 
    } 
    public void drawGameElements(Canvas canvas) 
    { 
        canvas.drawCircle(100, 100,ballRadius,ballPaint); 
 
    } 
    private class CanvasThread extends Thread 
    { 
        @Override 
        public void run() 
        { 
            while(true) 
            { 
                synchronized( myHolder ) 
                { 
 
                    Canvas canvas = myHolder.lockCanvas(null);//獲取畫佈  
                    drawGameElements(canvas); 
                    myHolder.unlockCanvasAndPost(canvas);//解鎖畫佈,提交畫好的圖像  
                    //System.out.println("run");  
                } 
            } 
        } 
 
    } 

package com.example.bonusball;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CanvasView extends SurfaceView implements SurfaceHolder.Callback
{
 private SurfaceHolder myHolder;
 private Paint ballPaint; // Paint used to draw the cannonball
 private int screenWidth; // width of the screen
 private int screenHeight; // height of the screen
 private int ballRadius;
 private CanvasThread myThread;
 //控制循環
    private boolean isLoop;

 public CanvasView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  myHolder=this.getHolder();
  myHolder.addCallback(this);
  ballPaint=new Paint();
  ballPaint.setColor(Color.BLUE);
  isLoop = true;
 }

 public void fireBall(float startX,float startY)
 {
  System.out.println("Fire");

 }

 @Override
 public void surfaceChanged(SurfaceHolder holder, int format, int width,
   int height) {
  // TODO Auto-generated method stub

 }
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh)
 {
  super.onSizeChanged(w, h, oldw, oldh);
  screenWidth = w; // store the width
  screenHeight = h; // store the height
  ballRadius=w/10;
 }

 @Override
 public void surfaceCreated(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  myThread = new CanvasThread();
  System.out.println("SurfaceCreated!");
  myThread.start();

 }

 @Override
 public void surfaceDestroyed(SurfaceHolder holder) {
  // TODO Auto-generated method stub
  // 停止循環
        isLoop = false;
 }
 public void drawGameElements(Canvas canvas)
 {
  canvas.drawCircle(100, 100,ballRadius,ballPaint);

 }
 private class CanvasThread extends Thread
 {
  @Override
  public void run()
  {
   while(true)
   {
    synchronized( myHolder )
    {

     Canvas canvas = myHolder.lockCanvas(null);//獲取畫佈
     drawGameElements(canvas);
     myHolder.unlockCanvasAndPost(canvas);//解鎖畫佈,提交畫好的圖像
     //System.out.println("run");
    }
   }
  }

 }
}
事件處理 類:

[java]
package com.example.bonusball; 
 
import android.os.Bundle; 
import android.app.Activity; 
import android.view.Menu; 
import android.content.Context; 
import android.graphics.Canvas; 
import android.graphics.Color; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.view.GestureDetector; 
import android.view.GestureDetector.SimpleOnGestureListener; 
import android.view.MotionEvent; 
import android.view.SurfaceHolder; 
import android.view.SurfaceView; 
import android.widget.Toast; 
public class BallActivity extends Activity { 
 
    private GestureDetector myGestureDetector;//監聽手勢  
    private CanvasView myCanvas; 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        myCanvas=new CanvasView(this); 
        setContentView(myCanvas); 
        myGestureDetector = new GestureDetector(this, new MyGestureListener()); 
         
    } 
    @Override 
    public boolean onTouchEvent(MotionEvent event) 
    { 
        return myGestureDetector.onTouchEvent(event); 
 
    } 
 
     
    private class MyGestureListener extends SimpleOnGestureListener   
    {   
        public boolean onDown(MotionEvent e1) {     
            Toast.makeText(getApplicationContext(), "onDown", Toast.LENGTH_SHORT).show();  
            return true;     
        }  
         
         @Override     
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)   
         { 
             System.out.println("Fling"); 
            return true;   
         } 
    } 
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
        getMenuInflater().inflate(R.menu.activity_ball, menu); 
        return true; 
     
    } 

package com.example.bonusball;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.GestureDetector;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class BallActivity extends Activity {

 private GestureDetector myGestureDetector;//監聽手勢
 private CanvasView myCanvas;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myCanvas=new CanvasView(this);
        setContentView(myCanvas);
        myGestureDetector = new GestureDetector(this, new MyGestureListener());
       
    }
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
     return myGestureDetector.onTouchEvent(event);

    }

   
    private class MyGestureListener extends SimpleOnGestureListener 
    { 
     public boolean onDown(MotionEvent e1) {   
            Toast.makeText(getApplicationContext(), "onDown", Toast.LENGTH_SHORT).show();
            return true;   
        }
     
      @Override   
         public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) 
         {
       System.out.println("Fling");
   return true; 
         }
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_ball, menu);
        return true;
   
    }
}

解釋幾個 概念 :

 callback接口:

 隻要繼承SurfaceView類並實現SurfaceHolder.Callback接口就可以實現一個自定義的SurfaceView 瞭,SurfaceHolder.Callback在底層的Surface狀態發生變化的時候通知 View,SurfaceHolder.Callback具有如下的接口:

 surfaceCreated(SurfaceHolder holder):當Surface第一次創建後會立即調用該函數。程序可以在該函數中做些和繪制界面相關的初始化工作,一般情況下都是在另外的線程來繪制界面,所以不要在這個函數中繪制Surface。
 surfaceChanged(SurfaceHolder holder, int format, int width,int height):當Surface的狀態(大小和格式)發生變化的時候會調用該函數,在surfaceCreated調用後該函數至少會被調用一次。
SurfaceHolder 類:

它是一個用於控制surface的接口,它提供瞭控制surface 的大小,格式,上面的像素,即監視其改變的。

SurfaceView的getHolder()函數可以獲取SurfaceHolder對象,Surface 就在SurfaceHolder對象內。雖然Surface保存瞭當前窗口的像素數據,但是在使用過程中是不直接和Surface打交道的,由SurfaceHolder的Canvas lockCanvas()或 則Canvas lockCanvas()函數來獲取Canvas對象,通過在Canvas上繪制內容來修改Surface中的數據。如果Surface不可編輯或則尚未 創建調用該函數會返回null,在 unlockCanvas() 和 lockCanvas()中Surface的內容是不緩存的,所以需要完全重繪Surface的內容,為瞭提高效率隻重繪變化的部分則可以調用 lockCanvas(Rect rect)函數來指定一個rect區域,這樣該區域外的內容會緩存起來。在調用lockCanvas函數獲取Canvas後,SurfaceView會獲 取Surface的一個同步鎖直到調用unlockCanvasAndPost(Canvas canvas)函數才釋放該鎖,這裡的同步機制保證在Surface繪制過程中不會被改變(被摧毀、修改)。

 

最後來看一個復雜一些的例子 ,結合瞭之前的手勢操作。

 

 

You May Also Like