【Interface&navigation】使視圖交互(18)

繪製用戶界面只是創建自定義視圖的一部分。您還需要使視圖以與您模仿的真實動作非常相似的方式響應用戶輸入。對象應始終以與真實對象相同的方式進行操作。例如,圖像不應該立即跳出存在,並在其他地方重新出現,因爲現實世界中的物體不會這樣做。相反,圖像應該從一個地方移動到另一個地方。

用戶還會感覺到界面中的微妙行爲或感受,並對模仿真實世界的微妙事物做出最佳反應。例如,當用戶投擲UI對象時,他們應該在開始時感覺到摩擦,從而延遲運動,然後在最後感覺到運動超出投擲的動量。

本課程演示如何使用Android框架的功能將這些現實世界的行爲添加到自定義視圖中。

除了本課,您還可以在輸入事件和 屬性動畫中找到其他相關信息 。

處理輸入手勢


和許多其他UI框架一樣,Android支持輸入事件模型。用戶操作將轉換爲觸發回調的事件,您可以覆蓋回調以自定義應用程序響應用戶的方式。Android系統中最常見的輸入事件是觸摸,觸發onTouchEvent(android.view.MotionEvent)。重寫此方法來處理事件:

   @Override
   public boolean onTouchEvent(MotionEvent event) {
    return super.onTouchEvent(event);
   }

觸摸事件本身並不是特別有用。現代觸摸UI定義了手勢方面的交互,例如點擊,拉動,推動,拋擲和縮放。爲了將原始觸摸事件轉換爲手勢,Android提供了GestureDetector。

GestureDetector通過傳入一個實現的類的實例來構造一個GestureDetector.OnGestureListener。如果您只想處理幾個手勢,則可以擴展GestureDetector.SimpleOnGestureListener而不是實現GestureDetector.OnGestureListener 接口。例如,這段代碼創建了一個擴展GestureDetector.SimpleOnGestureListener和覆蓋的類onDown(MotionEvent)。

class mListener extends GestureDetector.SimpleOnGestureListener {
   @Override
   public boolean onDown(MotionEvent e) {
       return true;
   }
}
mDetector = new GestureDetector(PieChart.this.getContext(), new mListener());

無論您是否使用GestureDetector.SimpleOnGestureListener,您都必須執行onDown()返回的 方法true。這一步是必要的,因爲所有手勢都以onDown()消息開頭 。如果false從中返回onDown(), GestureDetector.SimpleOnGestureListener系統會假定您要忽略其餘的手勢,而其他方法則 GestureDetector.OnGestureListener永遠不會被調用。你唯一應該回來false的時候onDown() 是你真的想要忽略整個手勢。一旦您實現GestureDetector.OnGestureListener 並創建了一個實例GestureDetector,您就可以使用它GestureDetector來解釋您收到的觸摸事件onTouchEvent()。

@Override
public boolean onTouchEvent(MotionEvent event) {
   boolean result = mDetector.onTouchEvent(event);
   if (!result) {
       if (event.getAction() == MotionEvent.ACTION_UP) {
           stopScrolling();
           result = true;
       }
   }
   return result;
}

當您傳遞onTouchEvent()一個觸摸事件時,它不會將其識別爲手勢的一部分,它會返回false。然後,您可以運行自己的自定義手勢檢測代碼。

創造物理上合理的運動


手勢是控制觸摸屏設備的強大方式,但除非它們產生物理上合理的結果,否則它們可能是違反直覺的,難以記憶。這方面的一個很好的例子就是揮動 手勢,用戶在屏幕上快速移動手指然後提起手指。如果用戶界面通過在投擲方向上快速移動來響應,則該姿勢是有意義的,然後放慢,就好像用戶已經推動飛輪並將其設置爲旋轉。

但是,模擬飛輪的感覺並不是微不足道的。需要大量的物理和數學才能使飛輪模型正常工作。幸運的是,Android提供了輔助類來模擬這個和其他行爲。該 Scroller班級是處理飛輪式拋動手 勢的基礎。

要開始投擲,請fling()使用起始速度以及投擲的最小和最大x和y值進行調用。對於速度值,您可以使用爲您計算的值GestureDetector。

@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
   mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);
   postInvalidate();
}

注意:儘管計算出的速度 GestureDetector在物理上是準確的,但許多開發人員認爲使用此值會使得動畫過快。通常將x和y的速度除以4到8的因子。

這個呼籲fling()建立了手勢的物理模型。之後,您需要定期Scroller撥打電話進行更新Scroller.computeScrollOffset()。通過讀取當前時間並使用物理模型計算當時的x和y位置來computeScrollOffset()更新Scroller對象的內部狀態。調用getCurrX()並getCurrY()檢索這些值。

大多數視圖直接傳遞Scroller對象的x和y位置 scrollTo()。PieChart示例稍有不同:它使用當前的滾動y位置來設置圖表的旋轉角度。

if (!mScroller.isFinished()) {
    mScroller.computeScrollOffset();
    setPieRotation(mScroller.getCurrY());
}

該Scroller級計算滾動位置給你,但它不會自動應用這些位置你的看法。您有責任確保經常獲得並應用新座標,以使滾動動畫看起來流暢。有兩種方法可以做到這一點:

調用postInvalidate()後調用 fling(),以強制重繪。這種技術要求您計算滾動偏移量onDraw() 並在postInvalidate()每次滾動偏移量更改時調用。
設置一個ValueAnimator動畫的持續時間,並添加一個偵聽器來處理動畫更新通過調用addUpdateListener()。
PieChart示例使用第二種方法。這種技術的設置稍微複雜一些,但它與動畫系統的關係更密切,不需要潛在的不必要的視圖無效。缺點是ValueAnimator 在API級別11之前不可用,所以此技術不能用於運行Android版本低於3.0的設備。

注意:您可以ValueAnimator在針對較低API級別的應用程序中使用。您只需確保在運行時檢查當前API級別,並在當前級別小於11時省略對視圖動畫系統的調用。

       mScroller = new Scroller(getContext(), null, true);
       mScrollAnimator = ValueAnimator.ofFloat(0,1);
       mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
           @Override
           public void onAnimationUpdate(ValueAnimator valueAnimator) {
               if (!mScroller.isFinished()) {
                   mScroller.computeScrollOffset();
                   setPieRotation(mScroller.getCurrY());
               } else {
                   mScrollAnimator.cancel();
                   onScrollFinished();
               }
           }
       });

讓你的轉換順利


用戶期望現代用戶界面能夠在狀態之間平滑過渡。UI元素淡入淡出而不是出現和消失。動作開始和結束順利而不是突然開始和停止。Android 3.0中引入的Android 屬性動畫框架使平滑過渡變得簡單。

要使用動畫系統,只要屬性發生變化而影響視圖外觀,請不要直接更改屬性。相反,使用ValueAnimator來進行更改。在以下示例中,修改PieChart中當前選定的餅圖切片會導致整個圖表旋轉,以便選擇指針位於所選切片的中心。ValueAnimator在幾百毫秒的時間內改變旋轉,而不是立即設置新的旋轉值。

mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0);
mAutoCenterAnimator.setIntValues(targetAngle);
mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION);
mAutoCenterAnimator.start();

如果要更改的值是基本View屬性之一,則更容易執行動畫,因爲視圖具有ViewPropertyAnimator針對多個屬性的同時動畫進行優化的內置。例如:

animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();

聯繫我

QQ:94297366
微信打賞:https://pan.baidu.com/s/1dSBXk3eFZu3mAMkw3xu9KQ

公衆號推薦:

【Interface&navigation】使視圖交互(18)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章