在上一章我 我們沒有把標題欄和狀態列給去掉 , 如果在遊戲中 是不會顯示 顯示標題欄和狀態列的, 如何去掉了, 很簡單, 在mainActivity 的onCreate方法中加入下面兩句 即可 :
requestWindowFeature(Window.FEATURE_NO_TITLE); //設定無標題
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); //設定全屏
趕緊去試試吧 , 寫程式就是要 多試 嘻嘻..
圖片有了 ,我們怎麼樣才能讓圖片有行為呢 ? 那就需要人機互動了,通過觸控式螢幕讓遊戲具有行為
先在這裡 說明下android 的座標系
android 的座標系 左上定點為原點座標(0,0), 向右為X軸,向下為Y軸 說明這個位置是因為有些遊戲引擎是 以 左下為原點的哦 , 大家要記住喔,後面如果用引擎的話 也有個概念
下面 我們來看看android 的 觸控式螢幕事件是怎麼處理的
我們先來分析下 現在的使用者介面 都是通過事件驅動 實現人機互動的,當螢幕的介面接受到事件時 根據不同情況 進行不同的處理 就可以實現人機互動了
android 支援的觸控式螢幕事件有:按下、彈起、移動、雙擊、長按、滑動。
按下、彈起、移動(down、move、up)是簡單的觸控式螢幕事件 我們本章就來說說這個東東.
而雙擊、長按、滑動、滾動需要根據運動的軌跡來做識別的。在Android中有專門的類去識別,android.view.GestureDetector。 這一塊我們後面的章節 在講
那如何實現呢?
在Android中任何一個控制元件和Activity都是間接或者直接繼承於android.view.View。一個View物件可以處理測距、佈局、繪製、焦點變換、滾動條,以及觸屏區域自己表現的按鍵和手勢,因為我們的view 是繼承了surfaceView,surfaceView又是繼承view 所以要實現簡單的觸控式螢幕事件,只需要重寫父類view 裡面的onTouchEvent 方法就可以實現簡單的觸屏屏事件了
下面我們來實現一個功能 用上一章的程式 來實現 把圖片顯示在點選觸控式螢幕的地方和圖片能根據手指移動而移動
直接看程式碼
package yxqz.com;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.SurfaceHolder.Callback;
/**
* android surfaceview觸控式螢幕事件學習
* @author mahaile
*
*/
public class GameSurfaceView extends SurfaceView implements Callback{
boolean flag; //執行緒標示位 當為false時停止重新整理介面
SurfaceHolder surfaceHolder;
GameViewThread gameViewThread;
float x=0,y=0;
int direction=0; //圖片執行方向 控制圖片向上 或向下運動
int width,height;
Bitmap bitmap_role;
public GameSurfaceView(Context context) {
super(context);
surfaceHolder=this.getHolder();
surfaceHolder.addCallback(this); //新增回撥
bitmap_role=BitmapFactory.decodeResource(getResources(), R.drawable.role);
//設定焦點 如果不設定焦點的話 在該介面下 點選觸控式螢幕是無效的 預設為false
setFocusable(true);
}
public void onDraw(Canvas canvas){
canvas.drawColor(Color.BLACK);
canvas.drawBitmap(bitmap_role, x-bitmap_role.getWidth()/2, y-bitmap_role.getHeight()/2, null);
}
//重寫父類中的 onTouchEvent就可以監聽到 觸控事件了 記住要設定焦點喔
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){ //處理螢幕屏點下事件 手指點選螢幕時觸發
x=event.getX();
y=event.getY();
}else if(event.getAction()==MotionEvent.ACTION_UP){//處理螢幕屏抬起事件 手指離開螢幕時觸發
}else if(event.getAction()==MotionEvent.ACTION_MOVE){//處理移動事件 手指在螢幕上移動時觸發
x=event.getX();
y=event.getY();
}
return true; //此處需要返回true 才可以正常處理move事件 詳情見後面的 說明
}
public void surfaceChanged(SurfaceHolder surfaceHolder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder surfaceHolder) {
//獲取螢幕的 寬高 只有在 surface建立的時候 才有效 ,才構造方法中獲取 寬高是獲取不到的
width=this.getWidth();
height=this.getHeight();
//初始化繪圖執行緒
gameViewThread=new GameViewThread();
gameViewThread.flag=true;
gameViewThread.start();
}
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
gameViewThread.flag=false; //銷燬執行緒
}
class GameViewThread extends Thread{
public boolean flag;
public void run(){
while(flag){
Canvas canvas=null;
try{
canvas=surfaceHolder.lockCanvas(); //鎖定畫布 並獲取canvas
onDraw(canvas);//呼叫onDraw 渲染到螢幕
surfaceHolder.unlockCanvasAndPost(canvas); //此步不要忘記了喔 否則介面上顯示不出來的
}catch(Exception e){
e.printStackTrace();
}
try {
Thread.sleep(10);//執行緒休眠時間 控制幀數
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} //每10毫秒重新整理一次
}
}
}
}
解釋:
onTouchEvent(),預設使用Oeverride這個方法,通常情況下去呼叫super.onTouchEvent()並傳回布林值。但是這裡要注意一點,預設如果去呼叫super.onTouchEvent()則很有可能super裡面並沒做任何事,並且回傳false回來,一旦回傳false回來,很可能後面的event (例如:Action_Move、Action_Up) 都會收不到了,所以為了確保保後面event能順利收到,要注意是否要直接呼super.TouchEvent()。
下一張 我們看看如果使用android的手勢識別
最後還要注意一點:在初始化的時候不要忘記setFocusableInTouchMode(true);觸屏模式獲取焦點,比較類似 setFocusable(true);
——setFocusable(true);//此方法是用來響應按鍵!如果是自己定義一個繼承自View的類,重新實現onKeyDown方法後,只有當該View獲得焦點時才會呼叫onKeyDown方法,Actvity中的onKeyDown方法是當所有控制元件均沒有處理該按鍵事件時,才會呼叫.
原始碼下載地址
http://download.csdn.net/detail/ma_haile/4215113
補充點 :
在android 中使用觸控式螢幕 在模擬機中 我們的滑鼠當點選一次模擬器螢幕然後釋放,先觸發 ACTION_DOWN 然後 ACTION_UP ;如果是在螢幕上移動那麼才會觸發 ACTION_MOVE 的動作;這個很正常, 但在真機中呢 ,是不是 也是這樣的呢 ? 答案是否定的 如果我們那真機測試的話 流程如下
先觸發 ACTION_DOWN 如果手指不抬起的話 會一直觸發ACTION_MOVE事件(就是不移動也會觸發) 然後 ACTION_UP
原因有兩點:第一點是因為,Android 對於觸屏事件很敏感!第二點:雖然我們的手指感覺是靜止沒有移動,其實事實不是如此!當我們的手指觸控到手機螢幕上之後,感覺靜止沒動,其實手指在不停的微顫抖震動。 所以才會一直觸發action_move事件
這樣的情況對我們的程式有什麼影響呢
比如我們app執行緒繪圖時間每次用了10ms,當手指觸控螢幕,這短暫的0.1秒內大概會產生10個左右的MotionEvent ,並且系統會盡可能快的把這些event發給監聽執行緒, 這樣的話在這一段時間內cpu可能忙於處理onTouchEvent事件 從而造成app 的介面沒有足夠資源去處理,而照成介面重新整理一卡一卡的。
那麼我們其實根本用不著按鍵響應這麼多次,而是需要在我們每次繪圖後,或者繪圖前接受一次使用者觸控事件就OK了,這樣能讓幀率不至於下降的太厲害不是麼?!如果我們能把觸屏監聽事件 觸發的事件 給慢下來 不就是可以解決這個問題了嗎 ,嘻嘻 就是這麼優化的
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==MotionEvent.ACTION_DOWN){
}else if(event.getAction()==MotionEvent.ACTION_UP){
}else if(event.getAction()==MotionEvent.ACTION_MOVE){
}
synchronized(this){
try{
this.wait(Time); //讓事件執行緒休眠 減少觸發次數
}catch(InterruptedException e){
e.printStackTrace();
}
}
return true;
}
上面的程式碼 加到你的onTouch 裡面 但有一點要注意喔 ,上面的執行緒同步物件使用了this ,如果這個類 也被別的類作為同步物件的話 ,可能發生死鎖喔, 如果這個類已經被作為了同步物件的話 , 我們重新初始化的時候 新new 一個物件 作為onTouch的同步物件就可以了