設計一個全螢幕且背景為黑色的畫布程式 , 支援手指單點觸控 , 於手指按下螢幕時捕捉該觸控點的座標 , 以該觸控點座標為基準點繪製一個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 去進一步的判斷是哪種手勢 , 再回應相對應的動作。
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皆會保留 , 也會根據題目所要求的流程去實作 , 縱使題目要求繞遠路....