設計一個全螢幕且背景為黑色的畫布程式 , 支援手指單點觸控 , 於手指按下螢幕時捕捉該觸控點的座標 , 以該觸控點座標為基準點繪製一個10*10的白色實心矩形 , 且每一次繪製時皆會清除前一次遺留的矩形 , 這題會使用自訂的SurfaceView , 並且實作處理手勢變化的 OnGestureListener 及實作一個Runnable 讓 non-UI Thread 幫我們處理Canvas的更新。
package COM.TQC.GDD03; import android.app.Activity; import android.os.Bundle; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.SurfaceHolder; public class GDD03 extends Activity { public static SView mSurfaceView01; public static SurfaceHolder mSurfaceHolder01; public GestureDetector detector; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mSurfaceView01 = (SView) this.findViewById(R.id.surfaceView1); //從xml檔中取用我們的 CustomView detector = new GestureDetector(this,mSurfaceView01); //GestureDetector 能幫我們監聽觸控螢幕上的手勢變化 //它需要一個實作好的 GestureDetector.OnGestureListener interface //而這個例子中我們已經傳入 mSurfaceView01 , 因為它實作了 GestureDetector.OnGestureListener } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub //return super.onTouchEvent(event); //這是原本method 預設的回傳值 , 在這個例子使用 detector.onTouchEvent(event) 取代 return detector.onTouchEvent(event); //GestureDetector 會根據傳入的MotionEvent 判斷手勢的變化 //另外它只會判斷Touch events 所以我們將Touch screen傳入的 events //送到 GestureDetector class 的 onTouchEvent(event) , 讓它判斷是哪一種手勢 //此外GestureDetector 不接受 trackball events } }
在這個class中先宣告Custom SurfaceView : mSurfaceView01 , mSurfaceView01 已經獨立出來存在另一個檔案 SView.java , 另外再使用 GestureDetector 替 mSurfaceView01 註冊監聽事件 , 讓 mSurfaceView01 可以根據手勢變化回應我們想要的動作。
當我們收到 Touch Screen 所傳入的Motion Events , 再將Events 傳入GestureDetector class 中的 onTouchEvent(event) method 去進一步的判斷是哪種手勢 , 再回應相對應的動作。
當我們收到 Touch Screen 所傳入的Motion Events , 再將Events 傳入GestureDetector class 中的 onTouchEvent(event) method 去進一步的判斷是哪種手勢 , 再回應相對應的動作。
package COM.TQC.GDD03; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.GestureDetector.OnGestureListener; public class SView extends SurfaceView implements SurfaceHolder.Callback , OnGestureListener , Runnable { SurfaceHolder mSurfaceHolder01; Canvas mCanvas; //我們已經在xml中定義了自訂的 SurfaceView , 所以系統會選用這個建構子去產生instance public SView(Context context, AttributeSet attrs) { // TODO Auto-generated constructor stub super(context,attrs); mSurfaceHolder01 = this.getHolder(); //獲取holder , 用來存取及控制 SurfaceView mSurfaceHolder01.addCallback(this); //監聽SurfaceHolder的事件 setFocusable(true); } @Override public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) { // TODO Auto-generated method stub } @Override public void surfaceCreated(SurfaceHolder arg0) { // TODO Auto-generated method stub new Thread(this).start(); //當Surface 被建立時 , 產生一個Thread來處理畫布內容的改變 } @Override public void surfaceDestroyed(SurfaceHolder arg0) { // TODO Auto-generated method stub ThreadFlag = false; //終止Thread 的運作 } @Override public void onDraw(Canvas canvas) { // onDraw() method 已經有 SurfaceHolder.lockCanvas() 的效果了 // 我們也不用額外的對canvas 執行unlock 的動作 canvas.drawColor(Color.BLACK); //把畫布填充為黑色 Paint p = new Paint(); p.setColor(Color.WHITE); if(DrawFlag) canvas.drawRect(x-5, y-5, x+5, y+5, p); // 畫一個矩形 DrawFlag = false; //關閉更新畫布開關 , 直到下一次的Touch 事件發生 } float x , y; boolean ThreadFlag = true , DrawFlag = false; @Override public boolean onDown(MotionEvent arg0) { // TODO Auto-generated method stub x = arg0.getRawX(); y = arg0.getRawY(); //獲取觸控的座標 DrawFlag = true; //開啟更新畫布的開關 return true; } @Override public void run() { // TODO Auto-generated method stub while(ThreadFlag) { if(DrawFlag) this.postInvalidate(); //由於我們是在non-UI Thread中要求要更新畫布 //所以使用 postInvalidate() method } } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { // TODO Auto-generated method stub return false; } @Override public void onLongPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // TODO Auto-generated method stub return false; } @Override public void onShowPress(MotionEvent e) { // TODO Auto-generated method stub } @Override public boolean onSingleTapUp(MotionEvent e) { // TODO Auto-generated method stub Log.d("Test", "onSingleTapUp"); return true; } }
至於我們自訂的SurfaceView 它是如何運作的? 我們會在Surface Created 的時候開始run一個non-UI Thread , 這個Thread 專門負責去呼叫 postInvalidate() method 來更新Canvas , 此外postInvalidate() method 會去執行 onDraw() method , 所以我們將畫布要呈現的內容都寫在onDraw() 中等著被執行 , 此外使用一個flag來控制畫布更新的時機 (是在觸控到螢幕時) , 此外當Surface Destroyed 的時候終止Thread 的運作。
接下來是 main.xml的部分 , 請注意我們是使用自訂的SurfaceView
P.S. 題目中所要求的Variable和Method皆會保留 , 也會根據題目所要求的流程去實作 , 縱使題目要求繞遠路....
沒有留言:
張貼留言