チュートリアル – Android でタッチを使用する
作業アプリケーションで前のセクションの概念を使用する方法を見てみましょう。 4 つのアクティビティを含むアプリケーションを作成します。 最初のアクティビティは、さまざまな API を示すために他のアクティビティを起動するメニューまたはスイッチボードです。 次のスクリーンショットは、メイン アクティビティを示しています。
最初のアクティビティであるタッチ サンプルでは、イベント ハンドラーを使用してビューにタッチする方法を示します。 ジェスチャ認識エンジン アクティビティでは、Android.View.Views
をサブクラス化してイベントを処理する方法と、ピンチ ジェスチャを処理する方法を示します。 3 番目と最後のアクティビティであるカスタム ジェスチャでは、カスタム ジェスチャの使用方法が示されます。 手順に簡単に従って身につけることができるように、このチュートリアルをセクションに分けて、各セクションでアクティビティの 1 つに焦点を当てます。
タッチ サンプル アクティビティ
プロジェクト 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
にタッチしている限り、"Touch Begins" が TextView
に表示されます。 ユーザーが ImageView
にタッチしなくなったら、次のスクリーンショットに示すように、Touch Ends メッセージが TextView
に表示されます。
ジェスチャ認識エンジン アクティビティ
次に、ジェスチャ認識エンジン アクティビティを実装しましょう。 このアクティビティでは、画面の周りにビューをドラッグし、ピンチ操作によるズームを実装する 1 つの方法を示します。
アプリケーションに
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
を追加します。 この時点で、コードはまだコンパイルされません。ユーザーがピンチしたときのImageView
のサイズ変更に役立つクラスMyScaleListener
を作成する必要があります。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)); }
アクティビティに画像を描画するには、次のスニペットに示すように、View クラスの
OnDraw
メソッドをオーバーライドする必要があります。 このコードは、_posX
と_posY
によって指定された位置にImageView
を移動し、拡大縮小率に従って画像をサイズ変更します。protected override void OnDraw(Canvas canvas) { base.OnDraw(canvas); canvas.Save(); canvas.Translate(_posX, _posY); canvas.Scale(_scaleFactor, _scaleFactor); _icon.Draw(canvas); canvas.Restore(); }
次に、ユーザーが
ImageView
をピンチしたときに、インスタンス変数_scaleFactor
を更新する必要があります。MyScaleListener
というクラスを追加します。 このクラスでは、ユーザーがImageView
をピンチしたときに Android によって発生するスケール イベントをリッスンします。 次の内部クラスを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 の位置と、画面にタッチした最初のポインターの ID を記録します。
ユーザーが画面でタッチを移動した場合、ユーザーがポインターを移動した距離を確認します。
ユーザーが画面から指を離した場合は、ジェスチャの追跡を停止します。
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 アプリケーションでピンチ操作によるズームが実装されました。 ちょっと一息入れてから、カスタム ジェスチャを使用して、このチュートリアルの 3 番目と最後のアクティビティに進みましょう。
カスタム ジェスチャ アクティビティ
このチュートリアルの最後の画面では、カスタム ジェスチャを使用します。
このチュートリアルでは、ジェスチャ ライブラリはジェスチャ ツールを使用して既に作成されており、Resources/raw/gestures ファイル内のプロジェクトに追加されています。 このハウスキープ処理が片付いたので、チュートリアルの最後のアクティビティに取り掛かりましょう。
custom_gesture_layout.axml という名前のレイアウト ファイルを次の内容でプロジェクトに追加します。 プロジェクトには、Resources フォルダー内のすべての画像が既に含まれています。
<?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
という名前を付けます。 次の 2 つのコード行に示すように、2 つのインスタンス変数をクラスに追加します。private GestureLibrary _gestureLibrary; private ImageView _imageView;
次のコードのようになるように、このアクティビティの
OnCreate
メソッドを編集します。 このコードで何が起こっているのかを説明するために少し時間を取ります。 まず、GestureOverlayView
のインスタンスを作成し、それをアクティビティのルート ビューとして設定します。 また、イベント ハンドラーをGestureOverlayView
のGesturePerformed
イベントに割り当てます。 次に、先ほど作成したレイアウト ファイルを拡張し、それを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 アプリケーションにタッチとジェスチャを統合する方法を説明しました。