李華明Himi 原創,轉載務必在明顯處註明:
轉載自【黑米GameDev街區】 原文連接: http://www.himigame.com/android-game/337.htmlcss
不少童鞋說個人代碼運行後,點擊home或者back後會程序異常,若是你也這樣遇到過,那麼你確定沒有仔細讀完Himi的博文,第十九篇Himi專門寫了關於這些錯誤的緣由和解決方法,這裏我在博客都補充說明下,省的童鞋們總疑惑這一塊;請點擊下面聯繫進入閱讀:html
【Android遊戲開發十九】(必看篇)SurfaceView運行機制詳解—剖析Back與Home按鍵及切入後臺等異常處理!java
本文補充:網上不少關於手勢文章都說Android 對手勢的支持是從SDK 1.6 (也就是 API 4)纔開始的,可是我用SDK1.5模擬器也能識別!。(本想測試下更低的SDK的支持效果,可是我沒有SDK低於1.5版本的....我手機SDK 2.2的 - -、),因此查了Api 發現:android
android.view.GestureDetector.OnGestureListener; since api-1 ,canvas
android.view.GestureDetector; since api-1 ,api
從API來看從api-1開始就已經支持手勢和手勢監聽器了,那麼不少說api-4才支持這句話也沒錯!由於:android.gesture 這個類是從 api-4纔開始支持的,這個類輸入法手勢識別中會用到!so~瀏覽器
結論:觸摸屏手勢識別是從API-1 就開始支持了。 而輸入法手勢識別是API-4纔開始支持的!這裏要搞清楚!ide
對於Android 的手勢不光在軟件中會常常用到,好比瀏覽器中的翻頁,滾動頁面等等;固然其實在咱們開發Android遊戲的時候加上了Android手勢操做更會讓遊戲增長一個亮點,好比通常的CAG ,PUZ等類型的遊戲選擇關卡啦、簡單背景的移動啦,均可以使用手勢來操做便可,相似前段時間很火的《讓人憤怒的小鳥!》咳咳、很差意思說錯了,是《憤怒的小鳥》,由於老是聽羣裏啊,朋友啊說小鳥出新版本啦,小鳥出PC硬盤版啦! 唉~你說可以讓人憤怒,其實說實話,小鳥這個遊戲確實不錯,我所看到的惟一的亮點是這款遊戲的創意!說實話,如今的遊戲沒有作不出來的只有想不出來的好創意、咳咳。回到話題來,那麼下面咱們來稍微瞭解下什麼是Android 手勢!函數
所謂手勢操做,相似跳舞機、EZdancer~這些利用不一樣動做和音符讓人手舞足蹈同樣,那麼Android這裏的手勢只是讓咱們在遊戲和軟件中的操做有了更多的花樣和玩法,根據玩家接觸屏幕時間的長短,在屏幕上滑動的距離,按下擡起的時間等進行了包裝,其實就是Android 對觸屏處理作了包裝和處理。測試
那麼在Android中其實有兩種手勢識別技術。一種是觸摸屏手勢識別,另外一種是輸入法手勢識別;二者比較起來第二種比較靈活,能夠自定義手勢,比較high!那麼這一節咱們先來介紹第一種手勢識別:觸摸屏手勢識別;在下篇博文中我會給童鞋們講解輸入法手勢識別!
先把兩張截圖放上來吧:
OK,老方式,先上代碼:
MySurfaceView.java
package com.himi; import java.util.Vector; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.GestureDetector.OnGestureListener; import android.view.SurfaceHolder.Callback; import android.view.View.OnTouchListener; /** *@author Himi *@ Gesture (上文)觸摸屏手勢識別 */ public class MySurfaceViewAnimation extends SurfaceView implements Callback, Runnable, OnGestureListener, OnTouchListener { private Thread th = new Thread(this); private SurfaceHolder sfh; private Canvas canvas; private Paint paint; private Bitmap bmp; private GestureDetector gd; private int bmp_x, bmp_y; private boolean isChagePage; private Vector<String> v_str;// 備註1 public MySurfaceViewAnimation(Context context) { super(context); v_str = new Vector<String>(); this.setKeepScreenOn(true); bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream); sfh = this.getHolder(); sfh.addCallback(this); paint = new Paint(); paint.setAntiAlias(true); this.setLongClickable(true); // setLongClickable( true )是必須的,由於 只有這樣, // 咱們當前的SurfaceView(view)纔可以處理不一樣於觸屏形式; // 例如:ACTION_MOVE,或者多個ACTION_DOWN this.setOnTouchListener(this);// 將本類綁定觸屏監聽器 gd = new GestureDetector(this); gd.setIsLongpressEnabled(true); } public void surfaceCreated(SurfaceHolder holder) { // 當系統調用了此方法才建立了view因此在這裏才能取到view的寬高!!有些童鞋老是把東西都放在初始化函數裏! // 線程最好放在這裏來啓動,由於放在初始化裏的畫,那view尚未呢,到了提交畫布unlockCanvasAndPost的時候就異常啦! bmp_x = (getWidth() - bmp.getWidth()) >> 2; bmp_y = (getHeight() - bmp.getHeight()) >> 2; th.start(); } public void draw() { try { canvas = sfh.lockCanvas(); if (canvas != null) { canvas.drawColor(Color.WHITE);// 畫布刷屏 canvas.drawBitmap(bmp, bmp_x, bmp_y, paint); paint.setTextSize(20);// 設置文字大小 paint.setColor(Color.WHITE); //這裏畫出一個矩形方便童鞋們看到手勢操做調用的函數都是哪些 canvas.drawRect(50, 30, 175,120, paint); paint.setColor(Color.RED);// 設置文字顏色 if (v_str != null) { for (int i = 0; i < v_str.size(); i++) { canvas.drawText(v_str.elementAt(i), 50, 50 + i * 30, paint); } } } } catch (Exception e) { Log.v("Himi", "draw is Error!"); } finally { sfh.unlockCanvasAndPost(canvas); } } @Override public void run() { // TODO Auto-generated method stub while (true) { draw(); try { Thread.sleep(100); } catch (Exception ex) { } } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } public void surfaceDestroyed(SurfaceHolder holder) { } // @Override // public boolean onTouchEvent(MotionEvent event) {// 備註2 // return true; // } @Override public boolean onTouch(View v, MotionEvent event) {// 備註3 if (v_str != null) v_str.removeAllElements(); return gd.onTouchEvent(event);// 備註4 } // --------------如下是使用OnGestureListener手勢監聽的時候重寫的函數--------- /** * @如下方法中的參數解釋: * @e1:第1個是 ACTION_DOWN MotionEvent 按下的動做 * @e2:後一個是ACTION_UP MotionEvent 擡起的動做(這裏要看下備註5的解釋) * @velocityX:X軸上的移動速度,像素/秒 * @velocityY:Y軸上的移動速度,像素/秒 */ @Override public boolean onDown(MotionEvent e) { // ACTION_DOWN v_str.add("onDown"); return false; } @Override // ACTION_DOWN 、短按不移動 public void onShowPress(MotionEvent e) { v_str.add("onShowPress"); } @Override // ACTION_DOWN 、長按不滑動 public void onLongPress(MotionEvent e) { v_str.add("onLongPress"); } @Override // ACTION_DOWN 、慢滑動 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { v_str.add("onScroll"); return false; } @Override // ACTION_DOWN 、快滑動、 ACTION_UP public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { v_str.add("onFling"); //-------備註5---------- // if(e1.getAction()==MotionEvent.ACTION_MOVE){ // v_str.add("onFling"); // }else if(e1.getAction()==MotionEvent.ACTION_DOWN){ // v_str.add("onFling"); // }else if(e1.getAction()==MotionEvent.ACTION_UP){ // v_str.add("onFling"); // } // if(e2.getAction()==MotionEvent.ACTION_MOVE){ // v_str.add("onFling"); // }else if(e2.getAction()==MotionEvent.ACTION_DOWN){ // v_str.add("onFling"); // }else if(e2.getAction()==MotionEvent.ACTION_UP){ // v_str.add("onFling"); // } if (isChagePage) bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_dream); else bmp = BitmapFactory.decodeResource(getResources(), R.drawable.himi_warm); isChagePage = !isChagePage; return false; } @Override // 短按ACTION_DOWN、ACTION_UP public boolean onSingleTapUp(MotionEvent e) { v_str.add("onSingleTapUp"); return false; } }
補充一下:代碼初始化手勢的時候有這麼一句:gd.setIsLongpressEnabled(true);這個函數標識,若是你設置true的話就是開啓了長按鍵,當你長時間觸屏不動就能獲得 onLongPress 手勢,若是設置false 那麼你長時間觸屏不移動也得不到這個手勢的支持~此函數不設置也默認設置爲true
備註1:
這裏我只是給一些不太熟悉這種定義Vector方式的童鞋簡單介紹一下:咱們通常定義容器的時候都是直接 Vector vc =new Vector();嗯,沒錯,可是這種Vector<String>的定義是種泛型定義,那麼簡單的說下區別,若是Vector vc =new Vector();這種方式裝入Object的之後,取的時候是否是要把取出的進行強轉一下類型?! 呵呵,而Vector<String>這種定義的時候就代表了這個容器我只裝String類型的元素,so~取出的時候也不用再去強轉了。
備註2 :經過測試發現,這裏仍然響應觸屏時間,即便你把觸屏焦點設置成setFocusableInTouchMode(false)也會調用!!!緣由是由於咱們本類的view綁定了觸屏事件監聽器,那麼確定會先響應備註3,而後咱們備註4這裏沒有 return true而是直接返給了手勢監聽器去監聽,讓監聽器找合適的函數來處理用戶的手勢,也就是說沒有標誌處理完成,因此咱們的重寫的onTouchEvent()也會繼續去處理!
備註5:
這裏註釋的代碼我是在測試兩個動做究竟是哪兩個,由於網上介紹Android手勢帖子都瘋傳說:
第一個是MotionEvent.ACTION_DOWN 第二個是MotionEvent.ACTION_MOVE!那麼第一個動做是按下好理解是玩家剛觸屏的動做,第二個是move!難道是移動的點都記錄下來了??
其實測試結果發現:
第一個是MotionEvent.ACTION_DOWN 第二個是MotionEvent.ACTION_UP!
唉~如今網上的帖子真是各類抄襲~就不能測試下??鬱悶! 既然這兩個動做一個是按下一個是擡起那就很明確其意義了,咱們能夠根據
這兩個動做知道用戶到底滑動的距離等等了,其距離e2.getX()-e1.getX();
總結:
1.觸屏後、一直觸屏不動、演變順序:onDown->onShowPress->onLongPress;
2.觸屏後、一直觸屏慢移動是onScroll/快移動是onFling 、手指離開屏幕;
注意 :觸屏後、一直觸屏移動,若是手指不離開屏幕一直都是onScroll,無論你移動的速度多快,永遠不會是onFling!
Ok,手勢雖然挺簡單的,可是若是熟練來使用而且加入遊戲中確定讓你的Game增色很多~
我這裏給出源碼:這個實例我只作了一個手勢的處理,由於其餘的動做都很簡單很少說了~OK 各位晚安~
源碼下載地址: http://www.himigame.com/android-game/337.html