逐步解說 - 在 Android 中使用觸控
讓我們看看如何使用工作應用程式中上一節的概念。 我們將建立具有四個活動的應用程式。 第一個活動會是功能表或切換板,將啟動其他活動來示範各種 API。 下列螢幕快照顯示主要活動:
第一個活動觸控範例會示範如何使用事件處理程式來觸控檢視。 手勢辨識器活動將示範如何子類別 Android.View.Views
和處理事件,以及示範如何處理捏合手勢。 第三個和最後一個活動自定義 手勢會顯示如何使用自定義手勢。 為了讓事情更容易遵循並吸收,我們會將此逐步解說分成各節,每個區段都著重於其中一個活動。
觸控範例活動
開啟專案 TouchWalkthrough_Start。 MainActivity 全都準備去 - 我們由我們執行活動中的觸控行為。 如果您執行應用程式並按兩下列 活動[觸控範例],則應該會啟動下列活動:
既然我們已確認活動已啟動,請開啟檔案TouchActivity.cs,並新增 事件的
ImageView
處理程式Touch
:_touchMeImageView.Touch += TouchMeImageViewOnTouch;
接下來,將下列方法新增至 TouchActivity.cs:
private void TouchMeImageViewOnTouch(object sender, View.TouchEventArgs touchEventArgs) { string message; switch (touchEventArgs.Event.Action & MotionEventActions.Mask) { case MotionEventActions.Down: case MotionEventActions.Move: message = "Touch Begins"; break; case MotionEventActions.Up: message = "Touch Ends"; break; default: message = string.Empty; break; } _touchInfoTextView.Text = message; }
請注意,在上述程序代碼中,我們會將 和 Down
動作視為Move
相同。 這是因為即使使用者可能不會將手指從 上抬起 ImageView
,它可能會四處移動,或使用者施加的壓力可能會改變。 這些類型的變更會產生 Move
動作。
每次使用者觸碰 ImageView
時,都會引發 事件,Touch
我們的處理程式會在畫面上顯示 Touch Begins 訊息,如下列螢幕快照所示:
只要使用者觸碰 ,ImageView
就會在 中TextView
顯示 Touch Begins。 當使用者不再觸碰 ImageView
時,[觸控結束] 訊息會顯示在 中TextView
,如下列螢幕快照所示:
手勢辨識器活動
現在,讓我們實作手勢辨識器活動。 此活動將示範如何在畫面周圍拖曳檢視,並說明實作捏合至縮放的一種方式。
將新的活動新增至名為
GestureRecognizer
的應用程式。 編輯此活動的程式代碼,使其類似下列程式代碼:public class GestureRecognizerActivity : Activity { protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); View v = new GestureRecognizerView(this); SetContentView(v); } }
將新的 Android 檢視新增至專案,並將它命名為
GestureRecognizerView
。 將下列變數新增至此類別:private static readonly int InvalidPointerId = -1; private readonly Drawable _icon; private readonly ScaleGestureDetector _scaleDetector; private int _activePointerId = InvalidPointerId; private float _lastTouchX; private float _lastTouchY; private float _posX; private float _posY; private float _scaleFactor = 1.0f;
將下列建構函式新增至
GestureRecognizerView
。 此建構函式會將 新增ImageView
至我們的活動。 此時,程式代碼仍然不會編譯 – 我們需要建立類別MyScaleListener
,以協助ImageView
調整使用者對它的大小:public GestureRecognizerView(Context context): base(context, null, 0) { _icon = context.Resources.GetDrawable(Resource.Drawable.Icon); _icon.SetBounds(0, 0, _icon.IntrinsicWidth, _icon.IntrinsicHeight); _scaleDetector = new ScaleGestureDetector(context, new MyScaleListener(this)); }
若要在活動上繪製影像,我們需要覆寫
OnDraw
View 類別的 方法,如下列代碼段所示。 此程式代碼會將 移至ImageView
所_posX
指定的位置,_posY
並根據縮放比例調整影像大小:protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.Save(); canvas.Translate(_posX, _posY); canvas.Scale(_scaleFactor, _scaleFactor); _icon.Draw(canvas); canvas.Restore(); }
接下來,我們需要更新實例變數
_scaleFactor
,因為使用者會使用 。ImageView
我們將新增名為MyScaleListener
的類別。 此類別會接聽當使用者捏出 時Android所引發的ImageView
縮放事件。 將下列內部類別新增至GestureRecognizerView
。 這個類別是ScaleGesture.SimpleOnScaleGestureListener
。 當您對手勢子集感興趣時,這個類別是一種便利類別,接聽程式可以子類別:private class MyScaleListener : ScaleGestureDetector.SimpleOnScaleGestureListener { private readonly GestureRecognizerView _view; public MyScaleListener(GestureRecognizerView view) { _view = view; } public override bool OnScale(ScaleGestureDetector detector) { _view._scaleFactor *= detector.ScaleFactor; // put a limit on how small or big the image can get. if (_view._scaleFactor > 5.0f) { _view._scaleFactor = 5.0f; } if (_view._scaleFactor < 0.1f) { _view._scaleFactor = 0.1f; } _view.Invalidate(); return true; } }
我們需要覆寫的
GestureRecognizerView
OnTouchEvent
下一個方法是 。 下列程式代碼會列出這個方法的完整實作。 這裡有很多程序代碼,所以讓我們花一分鐘時間看看這裡發生了什麼事。 這個方法的第一件事是視需要調整圖示,這是藉由呼叫_scaleDetector.OnTouchEvent
來處理。 接下來,我們會嘗試找出呼叫此方法的動作:如果使用者使用 觸控螢幕,我們會記錄 X 和 Y 位置,以及觸碰螢幕的第一個指標標識碼。
如果用戶在畫面上移動其觸控,我們就會找出用戶移動指標的距離。
如果用戶已經將手指從螢幕上抬開,我們將會停止追蹤手勢。
public override bool OnTouchEvent(MotionEvent ev) { _scaleDetector.OnTouchEvent(ev); MotionEventActions action = ev.Action & MotionEventActions.Mask; int pointerIndex; switch (action) { case MotionEventActions.Down: _lastTouchX = ev.GetX(); _lastTouchY = ev.GetY(); _activePointerId = ev.GetPointerId(0); break; case MotionEventActions.Move: pointerIndex = ev.FindPointerIndex(_activePointerId); float x = ev.GetX(pointerIndex); float y = ev.GetY(pointerIndex); if (!_scaleDetector.IsInProgress) { // Only move the ScaleGestureDetector isn't already processing a gesture. float deltaX = x - _lastTouchX; float deltaY = y - _lastTouchY; _posX += deltaX; _posY += deltaY; Invalidate(); } _lastTouchX = x; _lastTouchY = y; break; case MotionEventActions.Up: case MotionEventActions.Cancel: // We no longer need to keep track of the active pointer. _activePointerId = InvalidPointerId; break; case MotionEventActions.PointerUp: // check to make sure that the pointer that went up is for the gesture we're tracking. pointerIndex = (int) (ev.Action & MotionEventActions.PointerIndexMask) >> (int) MotionEventActions.PointerIndexShift; int pointerId = ev.GetPointerId(pointerIndex); if (pointerId == _activePointerId) { // This was our active pointer going up. Choose a new // action pointer and adjust accordingly int newPointerIndex = pointerIndex == 0 ? 1 : 0; _lastTouchX = ev.GetX(newPointerIndex); _lastTouchY = ev.GetY(newPointerIndex); _activePointerId = ev.GetPointerId(newPointerIndex); } break; } return true; }
現在執行應用程式,然後啟動手勢辨識器活動。 啟動畫面時,畫面看起來應該像下面的螢幕快照:
現在,觸控圖示,並將牠拖曳到畫面上。 請嘗試捏合縮放手勢。 在某些時候,您的畫面看起來可能會像下列螢幕快照:
此時,您應該給自己一個拍拍背面:您剛剛在Android應用程式中實作捏合縮放! 快速休息,並讓繼續前往本逐步解說中的第三個和最後一個活動 – 使用自定義手勢。
自定義手勢活動
本逐步解說的最後一個畫面將會使用自定義手勢。
為了本逐步解說的目的,已使用手勢工具建立手勢連結庫,並已新增至資源/原始/手勢檔案中的專案。 透過這一點管家工作的方式,讓我們繼續進行逐步解說中的最後一個活動。
使用下列內容,將名為 custom_gesture_layout.axml 的配置檔新增至專案。 專案已在 [資源] 資料夾中擁有所有影像:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <ImageView android:src="@drawable/check_me" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="3" android:id="@+id/imageView1" android:layout_gravity="center_vertical" /> <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> </LinearLayout>
接下來,將新的活動新增至專案,並將它命名為
CustomGestureRecognizerActivity.cs
。 將兩個實例變數新增至 類別,如下列兩行程式代碼所示:private GestureLibrary _gestureLibrary; private ImageView _imageView;
OnCreate
編輯此活動的 方法,使其類似下列程序代碼。 讓我們花一分鐘時間來說明此程式代碼中發生什麼事。 我們做的第一件事是具現化 ,GestureOverlayView
並將它設定為活動的根檢視。 我們也會將事件處理程式指派給GesturePerformed
的事件GestureOverlayView
。 接下來,我們會擴充稍早建立的配置檔案,並將該檔案新增為 的GestureOverlayView
子檢視。 最後一個步驟是初始化 變數_gestureLibrary
,並從應用程式資源載入手勢檔案。 如果基於某些原因無法載入手勢檔案,則此活動無法執行太多,因此會關閉:protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); GestureOverlayView gestureOverlayView = new GestureOverlayView(this); SetContentView(gestureOverlayView); gestureOverlayView.GesturePerformed += GestureOverlayViewOnGesturePerformed; View view = LayoutInflater.Inflate(Resource.Layout.custom_gesture_layout, null); _imageView = view.FindViewById<ImageView>(Resource.Id.imageView1); gestureOverlayView.AddView(view); _gestureLibrary = GestureLibraries.FromRawResource(this, Resource.Raw.gestures); if (!_gestureLibrary.Load()) { Log.Wtf(GetType().FullName, "There was a problem loading the gesture library."); Finish(); } }
我們需要執行最後一件事來實作 方法
GestureOverlayViewOnGesturePerformed
,如下列代碼段所示。GestureOverlayView
當 偵測到手勢時,它會回呼這個方法。 第一件事,我們嘗試藉由呼叫_gestureLibrary.Recognize()
來取得IList<Prediction>
符合手勢的物件。 我們使用一些 LINQ 來取得Prediction
具有手勢最高分數的 。如果沒有具有足夠高分數的相符手勢,事件處理程式就會結束,而不需要執行任何動作。 否則,我們會檢查預測的名稱,並根據手勢的名稱變更所顯示的影像:
private void GestureOverlayViewOnGesturePerformed(object sender, GestureOverlayView.GesturePerformedEventArgs gesturePerformedEventArgs) { IEnumerable<Prediction> predictions = from p in _gestureLibrary.Recognize(gesturePerformedEventArgs.Gesture) orderby p.Score descending where p.Score > 1.0 select p; Prediction prediction = predictions.FirstOrDefault(); if (prediction == null) { Log.Debug(GetType().FullName, "Nothing seemed to match the user's gesture, so don't do anything."); return; } Log.Debug(GetType().FullName, "Using the prediction named {0} with a score of {1}.", prediction.Name, prediction.Score); if (prediction.Name.StartsWith("checkmark")) { _imageView.SetImageResource(Resource.Drawable.checked_me); } else if (prediction.Name.StartsWith("erase", StringComparison.OrdinalIgnoreCase)) { // Match one of our "erase" gestures _imageView.SetImageResource(Resource.Drawable.check_me); } }
執行應用程式並啟動自定義手勢辨識器活動。 其看起來應該類似下列螢幕快照:
現在在畫面上繪製複選標記,所顯示的點陣圖看起來應該像下一個螢幕快照所示:
最後,在螢幕上繪製一個塗鴉。 複選框應該會變更回其原始影像,如下列螢幕快照所示:
您現在已瞭解如何使用 Xamarin.Android 在 Android 應用程式中整合觸控和手勢。