2016-01-08 7 views
8

Ich habe eine benutzerdefinierte Ansicht, die FrameLayout erweitert und implementiert ScaleGestureDetector.OnScaleGestureListener. Diese Ansicht ist, wie der Klassenname andeutet, zoombar + pannierbar. Heres die benutzerdefinierte Ansichten Klasse: https://gist.github.com/Orbyt/23c82ce9002df6c318d4Erkennen langer Klicks auf eine benutzerdefinierte Ansicht?

Ich habe versucht, einen Weg zu finden, lange Klicks auf diese Ansicht zu erkennen. Ich weiß, dass, in der Regel, ich so etwas wie dies in der Aktivität tun könnte:

GestureDetector mGestureDetector = new GestureDetector(this, this); 

     mZoomableLayout.setOnTouchListener(new View.OnTouchListener() { 

      @Override 
      public boolean onTouch(View v, MotionEvent event) { 
       return mGestureDetector.onTouchEvent(event); 
      } 
     }); 

     mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { 

      @Override 
      public void onLongPress(MotionEvent e) { 
       // do tasks here 
      } 
     }); 

diese verwenden, die Aussicht ist nicht mehr zoombar, vermutlich weil sein Abfangen alle OnTouch Ereignisse statt der Umsetzung innerhalb der Ansichten Klasse.

Also meine Frage ist, was ist der sauberste Weg, lange Klicks auf diese Ansicht zu erkennen?

+0

'view # setOnLongClickListener'? – pskink

+0

@pskink Wie würden Sie die Position der Veranstaltung bekommen? – Orbit

+0

durch Überschreiben von 'onTouchEvent' – pskink

Antwort

1

Ok, Entschuldigung, ich habe Ihr Problem überhaupt nicht verstanden. Jetzt in Ihrem MainActivity:

public yourMainConstructor() 
{ 
    [...] 

    GestureDetector sgd; 
    sgd = new GestureDetector(context,new ScaleListener()); 

    [...] 
} 

class ScaleListener extends GestureDetector.SimpleOnGestureListener { 

    @Override 
    public void onLongPress(MotionEvent e) { 

     super.onLongPress(e); 
    } 
} 

dann in der Hauptklasse onTouchEvent außer Kraft setzen()

@Override 
public boolean onTouchEvent(MotionEvent event) 
{ 
    sgd.onTouchEvent(event); 

    switch (event.getAction()) 
    { 
     case MotionEvent.ACTION_DOWN: 
     [...] 
    } 
} 
+0

Dadurch wird verhindert, dass die Ansicht zoombar oder änderbar ist. – Orbit

+0

Können Sie Ihr Beispiel etwas erklären? Wenn Sie sich auf den Code beziehen, der in die Aktivität einfließt, würde er nicht wissen, auf welche View er zugreifen soll. Wenn Sie darauf verweisen, dass der Code in die Ansicht und die onTouchEvent() -Option in die Aktivität einfließt, hätte er keinen Verweis auf den GestureDetector. – Orbit

+0

Entschuldigung, ich verweise den Code in die Aktivität –

1

Zunächst einmal müssen Sie berühren Slop verwenden, um zwischen tatsächlichen Bewegung und unbeabsichtigten Benutzern Finger unterscheiden Bewegung (siehe ACTION_MOVE). Zweitens, wenn Sie erweitern FrameLayout ist es sauber zu überschreiben onTouchEvent anstelle von this.setOnTouchListener in init().

Variablen zu Ihrer benutzerdefinierten Ansicht:

private final Handler mHandler = new Handler(); 
private ScaleGestureDetector mScaleDetector; 
private int mTouchSlop; 
private Runnable mLongPressed = new Runnable() { 
    public void run() { 
     Log.i(TAG, "Long press!"); 
     //Do your long press stuff here. 
    } 
}; 

Innen init():

mScaleDetector = new ScaleGestureDetector(context, this); 
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 

In switch-Anweisung:

switch (motionEvent.getAction() & MotionEvent.ACTION_MASK) { 
    case MotionEvent.ACTION_DOWN: 
     //Whatever you were doing previously here 
     mHandler.postDelayed(mLongPressed, ViewConfiguration.getLongPressTimeout()); 
     break; 
    case MotionEvent.ACTION_MOVE: 
     if (mode == Mode.DRAG) { 
      dx = motionEvent.getX() - startX; 
      dy = motionEvent.getY() - startY; 
      if(Math.abs(dx) > mTouchSlop || Math.abs(dy) > mTouchSlop) { 
       //Actual movement 
       mHandler.removeCallbacks(mLongPressed); 
      } else { 
       //Below touch slop, not a movement 
       dx = 0; 
       dy = 0; 
      } 
     } 
     break; 
    case MotionEvent.ACTION_POINTER_DOWN: 
     mHandler.removeCallbacks(mLongPressed); 
     //Whatever you were doing previously here 
     break; 
    case MotionEvent.ACTION_POINTER_UP: 
     mHandler.removeCallbacks(mLongPressed); 
     //Whatever you were doing previously here 
     break; 
    case MotionEvent.ACTION_UP: 
     mHandler.removeCallbacks(mLongPressed); 
     //Whatever you were doing previously here 
     break; 
} 
//Whatever you were doing previously here 

Nun sind alle drei Funktionen arbeiten.

Wenn Punkt langen Klick wird eine abstrakte Klasse erforderlich machen, die Runnable mit Schwimmern x und y und füllen sie in ACTION_DOWN implementiert, dann Koordinaten verwendet in run()

+0

Also das ist die Implementierung, die ich auch gefunden hatte (obwohl ich nicht über TouchSlop wusste, danke). Wenn Sie lange drücken, öffne ich einen Dialog, so dass ich die Aktivität über das Ereignis mit langer Druckdauer (sowie die x/y-Koordinaten) informieren möchte. Was wäre der beste Weg, die Aktivität in der runn() 'Methode der Runnables in diesem Fall zu benachrichtigen? – Orbit

+0

Lassen Sie Ihre Aktivität 'OnLongClickListener' implementieren und übergeben Sie' this' an Ihre benutzerdefinierte Ansicht, um 'setOnLongClickListener' aus Konsistenzgründen zu überschreiben. – InTwoMinds

2

ich, auf das ich die normalen erkannt einen Prise Zoom Kreis hatte klicken und lange klicken. Code-Snippet ist unten angegeben. In diesem habe ich einen langen Klick und einen normalen Klick nach dem Zeitintervall zwischen MotionEvent.ACTION_DOWN und MotionEvent.ACTION_UP festgestellt.

Möge dies Ihnen helfen.

 private static final int MAX_CLICK_DURATION = 200; 
    private float mScaleFactor = 1.0000000f; 
    private long mStartClickTime; 


    @Override 
    public boolean onTouchEvent(MotionEvent event) { 
    float x = event.getX(); 
    float y = event.getY(); 
    boolean right = x > (screenWidthPX/2 + ((mLayoutHeight/4) + 20) * mScaleFactor); 
    boolean left = x < (screenWidthPX/2 - ((mLayoutHeight/4) + 20) * mScaleFactor); 
    boolean top = y > (mLayoutHeight/2 + ((mLayoutHeight/4) + 20) * mScaleFactor); 
    boolean bottom = y < (mLayoutHeight/2 - ((mLayoutHeight/4) + 20) * mScaleFactor); 


    if (event.getPointerCount() > 1) { 
     if (left || right || top || bottom) { 
      // You may not need this condtion, I needed this because I had custom view of pinch zoom circle and, this condition detects the touch at outer area of circle.    
     } else { 
      mScaleGestureDetector.onTouchEvent(event); 
     } 
    } else { 
     switch (event.getAction()) { 

      case MotionEvent.ACTION_DOWN: { 
       mStartClickTime = Calendar.getInstance().getTimeInMillis(); 

       break; 
      } 
      case MotionEvent.ACTION_UP: { 
       long clickDuration = Calendar.getInstance().getTimeInMillis() - mStartClickTime; 

       if (clickDuration < MAX_CLICK_DURATION) { 
        if (left || right || top || bottom) { 

        } else { 
         Toast.makeText(mContext, "Normal CLick Detected", Toast.LENGTH_SHORT).show(); 
        } 
       } else { 
        Toast.makeText(mContext, "Long CLick Detected", Toast.LENGTH_SHORT).show(); 

       } 
      } 
     } 
    } 
    return true; 
}