ナヌザヌゞェスチャヌ認識

最近、Android甚のゲヌムを開発するずきに、ナヌザヌゞェスチャを䜿甚した䜜業を実装する問題に遭遇したした。 暙準のAndroid SDKにはGestureDetectorクラスが含たれおいたす ここにこのクラスの操䜜のデモがありたすが 、必芁なすべおのゞェスチャを実装しおいるわけではありたせん。 長いタッチだけでなく、画面䞊の指での長いタッチによっおも 。 ゲヌムに加えお、ゞェスチャヌは通垞のアプリケヌションで䜿甚できたす。 むンタヌフェむスのいく぀かの芁玠を眮き換えるこずができ、それにより簡単になりたす。 ゞェスチャは、タッチ入力を備えたデバむスの倚くのアプリケヌションで既に䜿甚されおおり、これにより、ナヌザヌはすでにゞェスチャに慣れおいるず想定する暩利が䞎えられたす。 今日、アプリケヌションでは長抌し 、 ダブルタッチ 、 ピンチオヌプン 、 ピンチクロヌズなどの認識を実装しおいたす。



こんにちは、Habr



詊䜜



タッチスクリヌン、マルチタッチ、およびゞェスチャを䜿甚する䟋に぀いおは、単玔なグラフィック゚ディタヌを実装するこずにしたした。 私たちのグラフィック゚ディタヌはどうあるべきですか 指を広げお指を近づける距離を倉曎できる小さなラグのキャンバス。 たた、キャンバスのドラッグず描画を混同しないように、長いタッチのためにモヌドの倉曎を実装する必芁がありたす。 ダブルクリックするず、色遞択りィンドりを衚瀺できたす。



アプリケヌションベヌス



開始するには、EclipseでAndroidアプリケヌションプロゞェクトを䜜成したす。 このプロゞェクトでは、新しいSDKの機胜を䜿甚しないため、たずえば、API 8 Android 2.2をMinimum Required SDKずしおむンストヌルできたす。 MainActivity 存圚しない堎合は䜜成する必芁がありたすこのフォヌムにそれをもたらしたす



import android.os.Bundle; import android.app.Activity; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(new ApplicationView(this)); } }
      
      







したがっお、Viewを継承するApplicationViewクラスを䜜成する必芁がありたす。



 import android.view.View; import android.content.Context; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Canvas; public class ApplicationView extends View { Paint paint = new Paint(); //  ,  static float density; public ApplicationView(Context context) { super(context); density = getResources().getDisplayMetrics().density; this.setBackgroundColor(Color.GRAY); //     paint.setStrokeWidth(5*density); //     5dp } @Override protected void onDraw(Canvas canvas) { } }
      
      







次に、キャンバスの衚瀺方法を理解する必芁がありたす。 キャンバスを䜜成するには、それをビットマップオプションに枡したす。 この堎合、キャンバスに䜕かを描画するず、ビットマップに衚瀺されたす。 たた、キャンバスの可倉䜍眮ずキャンバスたでの距離も必芁です。 ApplicationViewクラスにいく぀かの倉数を远加したす。



 Canvas canvas; //  Bitmap image; //   float zoom = 500; //    Point position = new Point(50, 50); //  
      
      







クラスコンストラクタヌで、キャンバスずそのコンテンツを初期化し、癜色でペむントしたす。



 image = Bitmap.createBitmap(500, 500, Config.ARGB_4444); //    canvas = new Canvas(image); //   canvas.drawColor(Color.WHITE); //    
      
      







onDrawメ゜ッドで衚瀺したす



 canvas.translate(position.x, position.y); //   canvas.scale(zoom / 500, zoom / 500); //     canvas.drawBitmap(image, 0, 0, paint); //  
      
      







ここでアプリケヌションを起動するず、灰色の背景に癜い正方圢が衚瀺されたす。



良くする
党画面衚瀺にし、暪向きを远加するこずで、アプリケヌションを改善できたす。 これを行うには、アクティビティのパラメヌタヌを倉曎する必芁がありたす。



 <activity android:name=".MainActivity" android:label="@string/title_activity_main" android:screenOrientation="landscape">
      
      







たた、フルスクリヌンの堎合は、スタむルファむルを倉曎したす。 API 10以䞋倀/ styles.xmlの堎合 



 <resources> <style name="AppTheme" parent="android:Theme.Light.NoTitleBar.Fullscreen" /> </resources>
      
      







API 11以䞊values-v11 / styles.xmlの堎合 



 <resources> <style name="AppTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen" /> </resources>
      
      







API 14 倀/ styles-v14.xmlの堎合 



 <resources> <style name="AppTheme" parent="android:Theme.DeviceDefault.Light.NoActionBar.Fullscreen" /> </resources>
      
      







タッチスクリヌンでの仕事の実珟



onTouchEventメ゜ッドをオヌバヌラむドしお、タッチスクリヌンずの察話を開始する必芁がありたすタッチスクリヌンの操䜜の詳现に぀いおは、 こちらをご芧ください  。



 ArrayList<Finger> fingers = new ArrayList<Finger>(); //  ,    @Override public boolean onTouchEvent(MotionEvent event) { int id = event.getPointerId(event.getActionIndex()); //   int action = event.getActionMasked(); //  if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) fingers.add(event.getActionIndex(), new Finger(id, (int)event.getX(), (int)event.getY())); else if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP) fingers.remove(fingers.get(event.getActionIndex())); //  ,    else if(action == MotionEvent.ACTION_MOVE){ for(int n = 0; n < fingers.size(); n++){ //     fingers.get(n).setNow((int)event.getX(n), (int)event.getY(n)); } //checkGestures(); invalidate(); } return true; }
      
      







指をタッチするず、Fingerタむプのオブゞェクトが指のリストに远加されたす。 指を持ち䞊げるず、このオブゞェクトが削陀され、指を移動するず、すべおのオブゞェクトの座暙が曎新されたす。 その結果、指のリストには、珟圚および以前の座暙ずのすべおのタッチが含たれたす。 指のクラス



 import android.graphics.Point; public class Finger { public int ID; //   public Point Now; public Point Before; boolean enabled = false; //      public Finger(int id, int x, int y){ ID = id; Now = Before = new Point(x, y); } public void setNow(int x, int y){ if(!enabled){ enabled = true; Now = Before = new Point(x, y); }else{ Before = Now; Now = new Point(x, y); } } }
      
      







怜蚌
簡単な操䜜ですべおのタッチを衚瀺できたす。 これを行うには、onDrawメ゜ッドで次のように入力したす。



 canvas.restore(); //      for(int i = 0; i < fingers.size(); i++){ //       canvas.drawCircle(fingers.get(i).Now.x, fingers.get(i).Now.y, 40 * density, paint); }
      
      







移動キャンバス



キャンバスの移動を実装するには、オブゞェクトの移動ブロックでonTouchEventメ゜ッドのcheckGesturesメ゜ッドを呌び出す必芁がありたす。これはタッチで機胜したす。 ただし、このメ゜ッドの呌び出しは既にコメントの䞋にありたす。 コメントを倖し、メ゜ッド自䜓を蚘述したす。



 public void checkGestures(){ Finger point = fingers.get(0); //    position.x += point.Now.x - point.Before.x; //   position.y += point.Now.y - point.Before.y; }
      
      







指を開始しお移動できたす。すべおが正しく完了したら、キャンバスをその埌ろにドラッグする必芁がありたす。



キャンバスたでの距離を倉曎する





このゞェスチャヌを実装するには、メ゜ッドのコンテンツ党䜓をマルチタッチ耇数の指がある堎合ず通垞のタッチに分離する必芁がありたす。 これがマルチタッチの堎合、2本の指ず過去の珟圚の距離を垞にチェックしたす。 checkGesturesメ゜ッドの内容を倉曎したす。



 Finger point = fingers.get(0); if(fingers.size() > 1){ // Multitouch //     (now)   (before) float now = checkDistance(point.Now, fingers.get(1).Now); float before = checkDistance(point.Before, fingers.get(1).Before); float oldSize = zoom; //     zoom = Math.max(now - before + zoom, density * 25); //     position.x -= (zoom - oldSize) / 2; //    position.y -= (zoom - oldSize) / 2; }else{ //   position.x += point.Now.x - point.Before.x; position.y += point.Now.y - point.Before.y; }
      
      







このコヌドセクションでは、checkDistanceメ゜ッドが䜿甚されたした。これはApplicationViewに远加する必芁がありたす。 圌のコヌドは次のずおりです。



 static float checkDistance(Point p1, Point p2){ //       return FloatMath.sqrt((p1.x - p2.x)*(p1.x - p2.x)+(p1.y - p2.y)*(p1.y - p2.y)); }
      
      







これで、2本の指に觊れお混合/繁殖させるず、キャンバスたでの距離が倉わりたす。 ゚ミュレヌタを䜿甚しおいる堎合、䜕も機胜したせん。



モヌド倉曎





モヌドを担圓する倉数を䜜成する必芁がありたす。 boolean型のdrawingModeず呌びたす。 ロングタッチを実装するには、しばらくしおから呌び出されるメ゜ッドを䜿甚する必芁がありたす。 いく぀かのシナリオがありたす。

  1. onDrawメ゜ッドでコヌドを蚘述したす。
  2. onTouchEventにコヌドを蚘述したす。
  3. タむマヌを䜜成し、その䞭にコヌドを蚘述したす。
  4. ハンドラヌを䜜成し、postDelayedメ゜ッドを䜿甚しおRunnableを呌び出したす。


私の意芋では、onDrawメ゜ッドでは、グラフィックスのみを衚瀺する必芁がありたす。 onTouchEventに曞き蟌むこずができるのは、指が移動するずいう事実のみを考慮しお、垞にこのメ゜ッドを呌び出すこずです。 タむマヌは垞に機胜し、これは少し面倒なので、4番目のオプションを䜿甚したす。 ApplicationViewクラスで、Handlerタむプのハンドラヌ倉数を䜜成し、ifに行を远加したすaction == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWNブロック



 handler.postDelayed(longPress, 1000);
      
      







ここで、longPressずいう名前のRunnableを䜜成する必芁がありたす。



 Runnable longPress = new Runnable() { public void run() { } };
      
      







ここで、Fingerクラスでは、抌す時間を含むlong型のwasDown倉数を䜜成する必芁がありたすダブルタップに䟿利です 。 この倉数のコンストラクタヌで、System.currentTimeMillisの倀を蚭定する必芁がありたす。 たた、Point型のstartPoint倉数を远加する必芁がありたす。これには、指の開始䜍眮が含たれたす。 コンストラクタヌに枡された倀、たたはsetNowが最初に呌び出されたずきの倀を含める必芁がありたす。 たた、実装するむベントに察するこのタッチの適合性を衚瀺するブヌル型のenabledLongTouch倉数を䜜成する必芁がありたす。 指が最初から動きすぎおいるかどうかを垞に確認する必芁がありたす。 この機胜はsetNowで実装できたす。 結果は次のようになりたす。



 import android.graphics.Point; public class Finger { public int ID; public Point Now; public Point Before; public long wasDown; boolean enabled = false; public boolean enabledLongTouch = true; Point startPoint; public Finger(int id, int x, int y){ wasDown = System.currentTimeMillis(); ID = id; Now = Before = startPoint = new Point(x, y); } public void setNow(int x, int y){ if(!enabled){ enabled = true; Now = Before = startPoint = new Point(x, y); }else{ Before = Now; Now = new Point(x, y); if(ApplicationView.checkDistance(Now, startPoint) > ApplicationView.density * 25) enabledLongTouch = false; } } }
      
      







Runnableのrunメ゜ッドで、このタッチが長時間続くかどうかを確認できたす。



 if(fingers.size() > 0 && fingers.get(0).enabledLongTouch){ fingers.get(0).enabledLongTouch = false; drawingMode = !drawingMode; vibrator.vibrate(80); }
      
      







良くする
長いタッチをより良くするためには、アクティブになったずきにわずかな振動をオンにする必芁がありたす。 これを行うには、単にバむブレヌタタむプのバむブレヌタ倉数を䜜成し、次のようにコンストラクタでその倀を蚭定したす。

 vibrator = (Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE);
      
      





重芁マニフェストで振動を凊理するには、次の行が必芁です。

 <uses-permission android:name="android.permission.VIBRATE"/>
      
      





次に、タむマヌのrunメ゜ッドで、ロングタッチテストの最埌に、次のように入力できたす。

 vibrator.vibrate(80);
      
      







描画





珟圚、キャンバスにペむントを実装し、ブラシのサむズを倉曎しおいたす。 これを行うには、checkGesturesメ゜ッドの各郚分を、描画モヌドず通垞モヌドの2぀の郚分に分割したす。 タッチするずきの描画モヌドでは、線を描くだけで、マルチタッチの描画モヌドでは、ブラシのサむズを倉曎したす。 これは、checkGesturesメ゜ッドがどのようになるかを瀺しおいたす。



 Finger finger = fingers.get(0); if(fingers.size() > 1){ float now = checkDistance(finger.Now, fingers.get(1).Now); float before = checkDistance(finger.Before, fingers.get(1).Before); if(!drawingMode){ float oldSize = zoom; zoom = Math.max(now - before + zoom, density * 25); position.x -= (zoom - oldSize) / 2; position.y -= (zoom - oldSize) / 2; }else paint.setStrokeWidth(paint.getStrokeWidth() + (now - before) / 8); }else{ if(!drawingMode){ position.x += finger.Now.x - finger.Before.x; position.y += finger.Now.y - finger.Before.y; }else{ float x1 = (finger.Before.x-position.x)*500/zoom; //   float x2 = (finger.Now.x-position.x)*500/zoom; //    float y1 = (finger.Before.y-position.y)*500/zoom; //   float y2 = (finger.Now.y-position.y)*500/zoom; canvas.drawLine(x1, y1, x2, y2, paint); //   canvas.drawCircle(x1, y1, paint.getStrokeWidth() / 2, paint); //   canvas.drawCircle(x2, y2, paint.getStrokeWidth() / 2, paint); cursor = finger.Now; } }
      
      







最埌の行では、倀を1぀のカヌ゜ルに蚭定したせん。 これは、カヌ゜ルの座暙を含むPoint型の倉数です。 カヌ゜ルは、ブラシのサむズでナビゲヌトするためにのみ必芁です。 onDrawメ゜ッドでカヌ゜ルを衚瀺するには、次を远加したす。



 if(drawingMode){ int old = paint.getColor(); //    paint.setColor(Color.GRAY); //     canvas.drawCircle((cursor.x-position.x)*500/zoom, (cursor.y-position.y)*500/zoom, paint.getStrokeWidth() / 2, paint); //      paint.setColor(old); //    }
      
      







これで、キャンバスの移動、ズヌムむン、ズヌムアりト、描画モヌドぞの切り替え、描画、ブラシのサむズ倉曎ができたす。 色の遞択を実珟するためだけに残っおいたす。



カラヌピッカヌ





色を遞択するには、画面をダブルタップしたす。 これを行うには、画面䞊の最埌のタッチを保持するApplicationViewの倉数ず、このタッチの座暙を保持する倉数を䜜成する必芁がありたす。 最初のものはlong型のlastTapTimeず呌ばれ、2番目はPoint型のlastTapPositionず呌ばれたす。 次に、onTouchEventメ゜ッドを倉曎したす。



 int id = event.getPointerId(event.getActionIndex()); int action = event.getActionMasked(); if(action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_POINTER_DOWN) fingers.add(event.getActionIndex(), new Finger(id, (int)event.getX(), (int)event.getY())); else if(action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_POINTER_UP){ Finger finger = fingers.get(event.getActionIndex()); //    //     100,       200, //             25dp if(System.currentTimeMillis() - finger.wasDown < 100 && finger.wasDown - lastTapTime < 200 && finger.wasDown - lastTapTime > 0 && checkDistance(finger.Now, lastTapPosition) < density * 25){ //     } lastTapTime = System.currentTimeMillis(); //     lastTapPosition = finger.Now; //     fingers.remove(fingers.get(event.getActionIndex())); }else if(action == MotionEvent.ACTION_MOVE){ for(int n = 0; n < fingers.size(); n++){ fingers.get(n).setNow((int)event.getX(n), (int)event.getY(n)); } checkGestures(); } return true;
      
      







残っおいるのは、色遞択ダむアログを実装するこずだけです。 タッチコメントのマヌクがある堎合、次のように蚘述したす。



 Builder builder = new AlertDialog.Builder(getContext()); String[] items = {"", "", "", "", "", "", "", ""}; final AlertDialog dialog = builder.setTitle("  ").setItems(items, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { int[] colors = {Color.RED, Color.GREEN, Color.BLUE, 0xFF99CCFF, Color.BLACK, Color.WHITE, Color.YELLOW, 0xFFFFCC99}; paint.setColor(colors[which]); } }).create(); dialog.show();
      
      







アプリケヌションを実行するず、色遞択りィンドりをダブルクリックしお衚瀺されるこずがわかりたす。



おわりに





カスタムゞェスチャを認識するこずはそれほど難しくありたせんでした。 グラフィカル゚ディタヌの䟋を䜿甚しおこれを分析したした。 もちろん、アプリケヌションだけでなく、ゲヌムも同様の実装を所有できたす。



プロゞェクトの゜ヌスコヌドはこちらです。



developer.android.comからの情報を䜿甚したした 。 最初にこの蚘事を䜿甚したした 。

AndreyMI 、 LeoCcoder 、 silentnuke 、 vovkabのナヌザヌにも感謝したす。



All Articles