【转】android手势处理揭秘
當滑動(fling)比移動(scroll)有更高的效率時,為什么要讓用戶使用scroll操作呢?在面積很小而數據又很多的移動設備上,要顯示遠在后面的那些內容scroll是很困難的,這種情況下fling更適合。
注:scroll表示手指滑動多少距離,界面跟著顯示多少距離,而fling是根據你的滑動方向與輕重,還會自動滑動一段距離。
filing 手勢在android交互設計中應用非常廣泛:電子書的滑動翻頁、ListView滑動刪除item、滑動解鎖等。所以如何檢測用戶的fling手勢是非 常重要的。但是我們如何獲得fling消息呢?如何知道fling的方向,從哪里開始從哪里結束?又如何確定當前手勢是fling 而不是scroll?
在我最近的項目Room5中就面臨這樣的問題,這是一個已經有了的ios項目,現在要做一個android版本。
在 我們處理滑動動畫之前,我們需要檢測fling事件,對初學者而言,這要比看上去復雜一些。這是因為touch、scroll、fling三個事件之間并 沒有明顯的界限。scroll總是發生在fling之前,而touch總是發生在scroll之前,滑動屏幕(fling)總是需要先觸摸屏幕 (touch)并且在屏幕上移動(scroll)。我們如何知道滑動(fling)沒有被touch事件攔截呢?答案是使用幾個關于手勢 (gesture)處理的類。
我寫了一個演示這幾個類是如何配合使用的例子程序,你可以在這里得到完整的源碼:https://github.com/ericacooksey/FlingDemo 這個demo描述了捕捉fling事件的整個過程。下面是運行apk之后初始界面的截圖:
當接收到touch,scroll或者fling事件,事件的名稱會顯示在界面上,最近發生的顯示在最上面。下面是某一次從上往下劃動(fling)時,界面上輸出的文字:
這幅圖給了你關于事件流的一個大概認識:view收到了好幾個scroll 事件,每個scroll事件之前都伴隨著一個touch事件。scroll事件過程中y軸方向上的速度飛快增長,直到最終fling事件被觸發。讓我們來看看關鍵代碼, 這里。
首 先,我們實現了View.OnTouchListener來攔截view的touch事件。我這里暫時省略了對滑動速度追蹤的代碼(省略號),我們將在后 面討論。這里主要是實現了touch事件觸發的時候將會調用的回到方法,將此方法注冊給相應的view(這里為TextView),下面是代碼片段。
1 mTouchListener = new VelocityTrackingTouchListener(); 2 // Initialize the TextView which will be used to display the logged events 3 mTextView = (TextView) findViewById(R.id.mytextview); 4 mTextView.setOnTouchListener(mTouchListener); 5 private class VelocityTrackingTouchListener implements View.OnTouchListener { 6 @Override 7 public boolean onTouch(View view, MotionEvent motionEvent) { 8 ...... 9 mGestureDetector.onTouchEvent(motionEvent); 10 return true; 11 } 12 }mGestureDetector 是GestureDetector的實例。onTouch回調方法在將事件派發給view之前接收到了touch事件。任何可能跟touch事件相關的事 件(比如Click)都會被這個回調方法攔截。我們將touch事件交給GestureDetector,因此這里的作用其實就是在將touch事件傳遞 下去之前,將MotionEvent傳遞給GestureDetector的onTouchEvent方法,先判斷當前到底是什么手勢。下面是聲明 GestureDetector變量的代碼片段:
1 // Instantiate a gesture listener to consume scroll and fling events 2 FlingDetector flingDetector = new FlingDetector(); 3 // Pass the FlingDetector to mGestureDetector to receive the appropriate callbacks 4 mGestureDetector = new GestureDetector(this, flingDetector);?
其中FlingDetector是我們繼承自SimpleOnGestureListener的一個類。使用SimpleOnGestureListener的好處是它完成了所有對GestureDetector.OnGestureListener所有接口的空實現,因此我們只需重寫需要的回調方法。
1 private class FlingDetector extends GestureDetector.SimpleOnGestureListener { 2 public FlingDetector() { 3 super(); 4 } 5 @Override 6 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, 7 float velocityY) { 8 updateText("in onFling"); 9 return true; 10 } 11 @Override 12 public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 13 updateText(String.format("onScroll velocity = (%f, %f)", mTouchListener.xVelocity, mTouchListener.yVelocity)); 14 return false; 15 } 16 }?
作為總結,下面是對上面一系列過程的回顧:
(1)view所注冊的OnTouchListener攔截了touch事件。
(2)OnTouchListener的回調方法onTouch(View view, MotionEvent motionEvent)將MotionEvent傳遞給了GestureDetector。
(3)實現一個OnGestureListener,并將他注冊給GestureDetector,這樣OnGestureListener中處理具體手勢的回調方法就能被觸發。
注 意OnGestureListener的每個方法都會返回一個boolean值,這個返回值表示當前方法結束之后,MotionEvent是否被“消費” 掉,也就是說是否繼續傳遞下去,true表示被消費掉,反之則沒有,可以繼續傳遞。回想上面我們提到的filing發生在scroll之后,scroll 發生在touch之后,而我們希望接收到fling手勢,因此我們在onScroll中返回false,在fling中返回true。
但是 現在問題是如果我們希望一個view既可以scroll也可以fling怎么辦?比如一個賬單界面我們希望sroll查看賬單而用fling操作表示 swipe-to-pay。如果你參考上面的fling事件的截圖會發現onScroll在onFLing觸發之前發生了5次。因此如果我們響應 onScroll事件,那么用戶在fling操作的時候會感到不自然,因為scroll的對我們預期的交互產生了干擾。
我們可以直接忽略掉 scroll,將onScroll中的實現留為空,但是這樣的話,如果用戶手指慢慢滑動查看后面的內容就得不到響應。最好是能在scroll的時候判斷這 個scroll是否會導致fling的發生。這種情況下android的VelocityTracker 就派上用場了。先來瞄一眼scroll的時候的手勢輸出日志:
讓我們將這張圖中(只有scroll事件的截圖)y方向上的速度和上一張圖(產生了fling事件的截圖)y方向上的速度做一個對比。之所以用y速度是因為兩次實驗中我們都是從上到下的滑動。
?
| 65 | 30085 |
| 140 | 23359 |
| 424 | 13787 |
| 660 | 10414 |
| 847 | 7449 |
如你所見, 產生了fling事件的滾動(scroll)事件中y-velocity要比沒有產生fling的滾動(scroll)高很多。我們在OnTouchListener的實現中跟蹤速度,每收到一個觸摸事件就將之添加給 mVelocityTracker
1 switch (action) { 2 case MotionEvent.ACTION_DOWN: 3 if (mVelocityTracker == null) { 4 // Retrieve a new VelocityTracker object to watch the velocity of a motion. 5 mVelocityTracker = VelocityTracker.obtain(); 6 } else { 7 // Reset the velocity tracker back to its initial state. 8 mVelocityTracker.clear(); 9 } 10 // Add a user's movement to the tracker. 11 mVelocityTracker.addMovement(motionEvent); 12 break; 13 case MotionEvent.ACTION_MOVE: 14 mVelocityTracker.addMovement(motionEvent); 15 // When you want to determine the velocity, call 16 // computeCurrentVelocity(). Then call getXVelocity() 17 // and getYVelocity() to retrieve the velocity for each pointer ID. 18 mVelocityTracker.computeCurrentVelocity(1000); 19 // Log velocity of pixels per second 20 xVelocity = mVelocityTracker.getXVelocity(pointerId); 21 yVelocity = mVelocityTracker.getYVelocity(pointerId); 22 break; 23 case MotionEvent.ACTION_CANCEL: 24 // Return a VelocityTracker object back to be re-used by others. 25 mVelocityTracker.recycle(); 26 break; 27 }?
然后再將觸摸事件傳遞給FlingDetector去分析,在FlingDetector打印出mVelocityTracker的速度以及對應的狀態(scroll還是fling),經過多次實驗可以找到一個合理的決定scroll是否會導致fling的臨界值。從事判斷是否相應onScroll。
轉載于:https://www.cnblogs.com/liangstudyhome/p/4170487.html
總結
以上是生活随笔為你收集整理的【转】android手势处理揭秘的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类;类型
- 下一篇: Android开发中adb启动失败adb