在上一章我 我們沒有把標題欄和狀態列給去掉  ,  如果在遊戲中 是不會顯示 顯示標題欄和狀態列的, 如何去掉了, 很簡單,  在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毫秒重新整理一次

}

}

}

}

MainActivity 類  這裡就不貼程式碼了,因為和上一章一樣 ,很簡單的


onTouchEvent() 返回值 解釋

解釋:

    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的同步物件就可以了