Android中SurfaceView學習

來源:互聯網
上載者:User

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

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

繪製介面類:

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 cannonballprivate int screenWidth; // width of the screenprivate int screenHeight; // height of the screenprivate int ballRadius;private CanvasThread myThread;//控制迴圈    private boolean isLoop;public CanvasView(Context context) {super(context); // TODO Auto-generated constructor stubmyHolder=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");}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {// TODO Auto-generated method stub}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh){super.onSizeChanged(w, h, oldw, oldh);screenWidth = w; // store the widthscreenHeight = h; // store the heightballRadius=w/10;}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubmyThread = new CanvasThread();System.out.println("SurfaceCreated!");myThread.start(); }@Overridepublic 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{@Overridepublic 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.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繪製過程中不會被改變(被摧毀、修改)。

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

實現的效果是:在螢幕上進行Fling操作,在手滑動的方向上會產生一個運動的小球 ,速度 就是手勢滑動的速度。

球的大小隨機 ,顏色隨機 ,碰到邊界會反彈。

運行結果:



代碼清單:

小球類,主要是設定幾個屬性,還有幾個set,get方法。

package com.example.bonusball;public class Ball {private float posX;private float posY;private float velocityX;private float velocityY;private float radius;private int color;public Ball(int rgb,float r,float pX,float pY,float vX,float vY){this.color=rgb;this.radius=r;this.posX=pX;this.posY=pY;this.velocityX=vX;this.velocityY=vY;}public float getRadius(){return radius;}public int getColor(){return color;}public float getX(){return posX;}public float getY(){return posY;}public float getVX(){return velocityX;}public float getVY(){return velocityY;}public void setPosX(float newX){this.posX=newX;}public void setPosY(float newY){this.posY=newY;}public void setVX(float newVX){this.velocityX=newVX;}public void setVY(float newVY){this.velocityY=newVY;}}

主控類:

主要負責觸控時間的監聽。

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();             //myCanvas.fireBall(e1.getRawX(),e1.getRawY(),0,0);            return true;            }          @Override             public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)           {     System.out.println("Fling");     final int FLING_MIN_DISTANCE = 100, FLING_MIN_VELOCITY = 200;       if (Math.abs(e1.getX() - e2.getX()) > FLING_MIN_DISTANCE && Math.abs(velocityX) > FLING_MIN_VELOCITY)     myCanvas.fireBall(e1.getRawX(),e1.getRawY(),velocityX/4,velocityY/4); return true;           }    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.activity_ball, menu);        return true;        }}

畫板類:

負責圖形的繪製。

package com.example.bonusball;import java.util.ArrayList;import java.util.List;import java.util.Random;import java.util.concurrent.CopyOnWriteArrayList;import android.content.Context;import android.graphics.Bitmap;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 cannonballprivate int screenWidth; // width of the screenprivate int screenHeight; // height of the screenprivate int maxBallRadius;private CanvasThread myThread;private List<Ball> ballList;private Paint backgroundPaint;private Random mRandom;//控制迴圈private boolean isLoop;public CanvasView(Context context) {super(context); // TODO Auto-generated constructor stubmyHolder=this.getHolder();myHolder.addCallback(this);ballPaint=new Paint();backgroundPaint = new Paint();backgroundPaint.setColor(Color.BLACK);isLoop = true;ballList=new CopyOnWriteArrayList<Ball>();mRandom=new Random();}public void fireBall(float startX,float startY,float velocityX,float velocityY){int ranColor = 0xff000000 | mRandom.nextInt(0x00ffffff);float randomRadius=mRandom.nextInt(maxBallRadius);float tmpRadius=maxBallRadius/5.0>randomRadius?maxBallRadius:randomRadius;ballList.add(new Ball(ranColor,tmpRadius,startX,startY,velocityX,velocityY));System.out.println("Fire");}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {// TODO Auto-generated method stub}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh){super.onSizeChanged(w, h, oldw, oldh);screenWidth = w; // store the widthscreenHeight = h; // store the heightmaxBallRadius=w/10;}@Overridepublic void surfaceCreated(SurfaceHolder holder) {// TODO Auto-generated method stubmyThread = new CanvasThread();System.out.println("SurfaceCreated!");myThread.start(); }@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {// TODO Auto-generated method stub// 停止迴圈isLoop = false;}public void drawGameElements(Canvas canvas){canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);for(Ball b:ballList){ballPaint.setColor(b.getColor());canvas.drawCircle(b.getX(),b.getY(),b.getRadius(),ballPaint);}}private void updatePositions(double elapsedTimeMS) {// TODO Auto-generated method stubfloat interval = (float) (elapsedTimeMS / 1000.0); for(Ball b:ballList){b.setPosX(b.getX()+b.getVX()*interval);b.setPosY(b.getY()+b.getVY()*interval);if (b.getX() + b.getRadius()> screenWidth ){b.setVX(-1*b.getVX());//邊界修複b.setPosX(screenWidth-b.getRadius());}if(b.getX() - b.getRadius() < 0){b.setVX(-1*b.getVX());b.setPosX(b.getRadius());}if (b.getY() + b.getRadius()> screenHeight){b.setVY(-1*b.getVY());b.setPosY(screenHeight-b.getRadius());}if(b.getY() - b.getRadius() < 0){b.setVY(-1*b.getVY());b.setPosY(b.getRadius());}}}private class CanvasThread extends Thread{@Overridepublic void run(){Canvas canvas=null;long previousFrameTime = System.currentTimeMillis(); while(isLoop){try{canvas = myHolder.lockCanvas(null);//擷取畫布synchronized( myHolder ){canvas.drawColor(Color.BLACK);long currentTime = System.currentTimeMillis();double elapsedTimeMS = currentTime - previousFrameTime;updatePositions(elapsedTimeMS); // update game statedrawGameElements(canvas);previousFrameTime = currentTime; // update previous time//System.out.println("run");}}finally{if (canvas != null) myHolder.unlockCanvasAndPost(canvas);//解鎖畫布,提交畫好的映像} // end finally}}}}


未解決問題一枚

理論上surfaceview是 帶雙緩衝的,但實際運行起來,映像還是有閃爍。

嘗試寫一個新的進程來處理映像的繪製,未果。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.