SurfaceView 介面最適合開發顯示速率較快的應用程式。
一張靜態的圖是看不出 SurfaceView 的能力的, 如果要讓圖 "動"起來, 則還需要配合執行緒的運用,
透過執行緒不斷去改變圖片的座標並反覆貼上圖片, 就可以達到動態的效果。
Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系:
1. Surface:
屏幕显示内容合成器(screen compositor)所管理的原生缓冲器。
用来画图形(graphics)或图像(image)的地方。
常画图是在一个Canvas对象上面进行。
2. SurfaceView:
通过SurfaceView就可以看到Surface的部分或者全部的内容
3. SurfaceHolder:
一个接口,其作用就像一个关于Surface的监听器。
提供访问和控制SurfaceView背后的Surface 相关的方法
通过三个回调方法,让我们可以感知到Surface的创建 surfaceCreated、销毁 surfaceDestroyed 或者改变 surfaceChanged。
參考資料:
- Surface、SurfaceView、SurfaceHolder及SurfaceHolder.Callback之间的关系:http://blog.csdn.net/pathuang68/article/details/7351317
在 canvas 畫布上貼圖的三個步驟:
1. 鎖住畫布 : canvas = holder.lockCanvas();
2. 在畫布上貼圖 : canvas.drawBitmap(bp,x,y,null);
3. 解鎖並po出畫布 : holder.unlockCanvasAndPost(canvas);
SurfaceHolder.Callback 相關聯的三個程序 :
public void surfaceCreated(...) -- surface 建立時
public void surfaceChanged(...) -- surface 改變時
public void surfaceDestroyed(...) -- surface 結束時
參考資料:
- 使用 SurfaceView 介面來處理貼圖:http://dazi2012.blogspot.tw/2012/03/android_17.html
用 SurfaceView 與 View 繪圖的差異性:
1. SurfaceView :是在一個新起的單獨執行緒中可以重新繪製畫面
- 主動更新。比如一個人在一直跑動。這就需要一個單獨的 Thread 不停的重繪人的狀態,避免阻塞主 UI Thread。 (如: 拉霸台 App, 太陽系 App)
2. View :必須在 UI 的主執行緒中更新畫面。
- 被動更新畫面的。比如棋類,這種用 View 就好了。
- 因為畫面的更新是賴於 onTouch 來更新,可以直接使用 invalidate。因為這種情況下,這一次 Touch 和下一次的 Touch 需要的時間比較長些,不會產生影響。 (如: 繪圖 App)
在 UI 的主執行緒中更新畫面,可能會引發問題,比如你更新畫面的時間過長,那麼你的主 UI 執行緒會被你正在畫的函數阻塞,那麼將無法回應按鍵,觸屏等消息。
SurfaceView 具有雙緩衝機制。因此,開發遊戲時儘量使用 SurfaceView 而不要使用 View,這樣的話效率較高,而且 SurfaceView 的功能也更加完善。
參考資料:
- Android View 與 SurfaceView 的應用上差異性:http://tomkuo139.blogspot.tw/2013/12/android-view-surfaceview.html
SurfaceView 刷新特定區域:
內部矩形:比如surfaceview上绘制旋转图片的时候,只刷新一个特定的矩形区域就可以了。
在 "鎖住畫布" 步驟中,加入要刷新的矩形區域:
canvas = holder.lockCanvas(new Rect(0, 0, 130, 130)); //获取画布 像这种带rect参数的就是脏矩形的刷新。
參考資料:
- Android开发:SurfaceView上新建线程绘制旋转图片 及 刷新特定区域(脏矩形) :http://blog.csdn.net/yanzi1225627/article/details/8581200
- SurfaceView 畫圖線程要停止時,需考慮 "安全中止线程" 的方法來處理:參考我的 Android/ Developer Notes/ - Program, Process, Thread:3. 安全中止一個Thread
- https://sites.google.com/site/peterslearningstation/android/developer-notes/program-process-thread
----------------------------------------------------------------
1. 新增 自訂義 SurfaceView Class 到 Layout :
----------------------------------------------------------------
- 在 Layout 中加入 LinearLayout
<LinearLayout android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/sfviewLLayout"/>
- 程式碼部分:
private LinearLayout sfviewLayout;private tw.com.manufacturer.MFView mfView;
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sfviewLayout = (LinearLayout)findViewById(R.id.sfviewLLayout); mfView = new MFView(this); sfviewLayout.addView(mfView);}
----------------------------------------------------------------
2. 自訂義 SurfaceView Class :
----------------------------------------------------------------
SurfaceView 需要一個 SurfaceHolder 來抓取與設定 surface States、一個 DrawThread 繪圖用的執行緒 來執行 DoDraw 方法。
public class MFView extends SurfaceView implements SurfaceHolder.Callback{
private SurfaceHolder holder = null; public DrawThread dt = null;
public MFView(Context context){ super(context); init(); } public MFView(Context context, AttributeSet attrs) { super(context, attrs); // 回來執行父類別 init(); }private void init(){ setFocusable(true); // make sure we get key events holder = this.getHolder(); holder.addCallback(this);
} // Draw Thread 呼叫此方法來繪圖 public void DoDraw(Canvas canvas) {canvas.save(); canvas.drawCircle canvas.drawRect ... canvas.restore();
}
//------------------ // 觸碰事件判斷和處理 //------------------ public boolean onTouchEvent(MotionEvent Event) {
switch (Event.getAction() & MotionEvent.ACTION_MASK){ case MotionEvent.ACTION_DOWN : break; case MotionEvent.ACTION_POINTER_DOWN: break; case MotionEvent.ACTION_MOVE : break; case MotionEvent.ACTION_POINTER_UP: break; case MotionEvent.ACTION_UP: break; default : break; } return true; }
//------------------------------------------------------ // SurfaceView 狀態方法(Created, Changed, Destroyed) //------------------------------------------------------ @Override public synchronized void surfaceCreated(SurfaceHolder holder) {
}
@Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { }
@Override public void surfaceDestroyed(SurfaceHolder holder) {
// thread 停止 runnable 執行內容 boolean retry = true; dt.setRunning(false); while (retry) { try { dt.join(); retry = false; } catch (InterruptedException e) {} } }
}
----------------------------------------------------------------
3. 自訂義 Thread Class :
----------------------------------------------------------------
public class DrawThread extends Thread {private MFView mfv; //物件物件變數 private SurfaceHolder surfaceHolder; //SurfaceHolder物件物件變數 private boolean flag=false; //線程執行旗標位 private int sleepSpan = 1; //休眠時間 private Canvas canvas = new Canvas(); //宣告一個Canvas物件
//建構式 public DrawThread(MFView mfv,SurfaceHolder surfaceHolder){ this.mfv = mfv; //為BallView物件應用設值 this.surfaceHolder = surfaceHolder; //為SurfaceHolder物件應用設值 }
public void setRunning(boolean runflag){ this.flag = runflag; //設置旗標位 }
public void run(){ while(flag){ try{ Thread.sleep(sleepSpan); //線程休眠一段時間 canvas = surfaceHolder.lockCanvas(); //取得BallView的畫布 synchronized(surfaceHolder){ mfv.DoDraw(canvas); //呼叫BallView的doDraw方法進行繪製 } } catch(Exception e){ e.printStackTrace(); //捕獲並打印異常 } finally{ if(canvas != null){ //如果canvas不為空 surfaceHolder.unlockCanvasAndPost(canvas); //surfaceHolder解鎖並將畫布物件傳回 } } } }
}
----------------------------------------------------------------
1. 範例練習1:
----------------------------------------------------------------
- 目標:
在一個有背景的SurfaceView操作一顆足球左右跑。
程式中共分為四個部分:
1. OlaTeach_game1 extends Activity:作為程式啟動的起點
2. OlaView extends SurfaceView:註冊繪圖物件,顯示繪圖的成果
3. OlaDrawThread extends Thread:負責繪圖的Thread
4. OlaBall extends Thread:負責當好一顆球(計算球的運動方式)
- 架構:
- 流程:
.1. 程式啟動,OlaTeach_game1觸發onCreate。
.2. 建立OlaView(new),觸發OlaView的建構式,一併由該建構式建立OlaDrawThread與OlaBall兩個Thread。.
3. SetContentView觸發 OlaView的surfaceCreated(),並由該函式啟動OlaDrawThread。
.4. 執行OlaView的startMove(),並由該函式啟動OlaBall。
.5. 由OlaDrawThread這個Thread執行OlaView的DoDraw來不斷重新繪製OlaView。.
6. 啟動OlaBall後,由該Thread不斷計算球最後的落點(X,Y)。
.7. 由DoDraw來觸發OlaBall的drawSelf來繪製球。
- 重要程式碼:
.1. 繪製SurfaceView(必須先取得View的getHolder,在繪圖之前先上鎖,畫完以後再解鎖).
OlaView內:
getHolder().addCallback(this);//新增Callback介面.OlaDrawThread dt = new OlaDrawThread(this,getHolder());//新建後臺更新螢幕執行緒
OlaDrawThread內:
SurfaceHolder surfaceHolder = surfaceHolder; //利用建構式接surfaceHolder.canvas = surfaceHolder.lockCanvas(null); //為畫布加鎖.OlaView.doDraw(canvas); //重繪畫布.surfaceHolder.unlockCanvasAndPost(canvas); //解鎖畫布
目標是要在畫面上繪製一個會動的球,所以準備了一個放畫布的架子(Activity),一個畫布(OlaView),一個畫畫的人(OlaDrawThread),一個球(OlaBall)。
參考資料:
- Android學習_高速繪圖SurfaceView基本架構 : http://wangshifuola.blogspot.tw/2011/06/androidsurfaceviewpart1.html
沒有留言:
張貼留言