2011-01-01 16 views
16

Ho pensato che il metodo "onTouchEvent()" di un'istanza "ScaleGestureDetector" avrebbe dovuto restituire "true" solo se gestisce realmente l'evento di tocco, i. e. se rileva un gesto di ridimensionamento multi-touch (con due dita). Altrimenti pensavo che avrebbe dovuto restituire "false" per consentire agli altri gestori di gestire l'evento, e. g. una pressione prolungata per l'attivazione di un menu di scelta rapida.ScaleGestureDetector.onTouchEvent restituisce sempre 'true'

Ho osservato qualcosa di diverso: scaleGestureDetector.onTouchEvent() restituisce sempre "true" nel mio caso. Il seguente frammento di codice della mia classe MyView:

public boolean onTouchEvent(MotionEvent event) { 
    boolean retval = scaleGestureDetector.onTouchEvent(event); 
    Log.v("MyView.onTouchEvent()", "Action: " + event.getAction() + 
     "; PointerCount: " + event.getPointerCount() + 
     "; scaleGestureDetector.onTouchEvent() RetVal: " + retval); 
    return(retval); 
} 

ha prodotto l'output del registro follwing dopo ho toccato la vista per circa 1 secondo con un dito, poi eseguito un gesto scala con 2 dita:

01-01 19:09:54.484: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:54.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:54.541: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:54.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:54.820: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:54.910: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:55.050: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:55.350: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:55.400: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:09:57.160: INFO/BatteryStatsImpl(96): notePhoneSignalStrengthLocked: 4->3 
01-01 19:10:00.060: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false 
01-01 19:10:00.060: ERROR/ClockWidget(215): handleUiMessage~ in pause. msg:36867 
01-01 19:10:00.070: ERROR/ClockWidget(215): weatherClock onReceive~ mUseAnimation:false 
01-01 19:10:00.090: INFO/PI.Alarms(699): Update Alarms start 
01-01 19:10:00.090: INFO/PI.Alarms(699): Task Notifications: Already displaying the same alarms, no update 
01-01 19:10:00.100: INFO/PI.Alarms(699): Event Notifications: Already displaying the same alarms, no update 
01-01 19:10:00.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 0; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.870: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.900: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.922: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.931: VERBOSE/MyView.onTouchEvent()(5930): Action: 261; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:00.950: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.002: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.030: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.060: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.090: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.120: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.140: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.172: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.200: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.230: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.252: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.280: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.310: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.342: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.370: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.390: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.424: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.450: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.480: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.510: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.530: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.580: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.690: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.780: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.815: VERBOSE/MyView.onTouchEvent()(5930): Action: 2; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.830: VERBOSE/MyView.onTouchEvent()(5930): Action: 262; PointerCount: 2; scaleGestureDetector.onTouchEvent() RetVal: true 
01-01 19:10:01.840: VERBOSE/MyView.onTouchEvent()(5930): Action: 1; PointerCount: 1; scaleGestureDetector.onTouchEvent() RetVal: true 

Come ho detto: il valore di ritorno è sempre "vero"! È un bug di ScaleGestureDetector.onTouchEvent()? Cosa posso fare per consentire ad altri gestori di gestire tutti gli eventi di gesture non in scala (ad esempio premere a lungo con 1 dito)? Per favore aiuto!

Nemax

risposta

4

Non so se si tratta di un bug o intenzionale, ma questo è sicuramente quello che fa la fonte (ScaleGestureDetector.java:156):

public boolean onTouchEvent(MotionEvent event) { 
    final int action = event.getAction(); 
    boolean handled = true; 

    /* ... bunch of code that never updates 'handled' */ 

    return handled; 
} 

Il modo in cui ho risolto questo era quello di verificare tutti gli altri tipi di eventi tattili che potrei voler gestire per primi, quindi invocare il rilevatore di gesti, ad es

if (mLongPressGestureDetector != null && mLongPressGestureDetector.onTouchEvent(event)) 
     return true; 
    else if (mIsInMoveMode && mScaleGestureDetector != null) { 
     // Check for a move 
     if (action == MotionEvent.ACTION_MOVE && !mScaleGestureDetector.isInProgress()) { 
      handleMove(event); 
      return true; 
     } 

     // Now a scale 
     mScaleGestureDetector.onTouchEvent(event); 
     return true; 
    } 
+0

Hi Daren, grazie per la vostra risposta. Il problema è che inizialmente volevo che il gestore standard di View "View.onTouchEvent()" gestisse i clic lunghi per poter utilizzare la gestione del menu di scelta rapida standard. Che chiama automaticamente il metodo "onCreateContextMenu()" dell'attività, crea la finestra di dialogo del menu contestuale e così via. Ma chiamare "super.onTouchEvent (evento)" dal mio metodo "MyView.onTouchEvent()" restituisce sempre "true" pure !!! Vedi qualche possibilità di fare a) quello standard View.onTouchEvent() e b) il gestore di ScaleGestureDetector.onTouchEvent() coopera? Cordiali saluti, Nemax – Nemax

+0

Sì, View.onTouchEvent restituisce sempre true per le visualizzazioni selezionabili. Hai provato a chiamare sempre super.onTouchEvent()? Se ciò non funziona, aggiungi il tuo rilevatore di gesti a pressione lunga come nell'esempio che ho postato sopra, quindi chiama performLongClick() quando rilevi un clic lungo. –

2

Lo ScaleGestureDetector fornisce il metodo isInProgress() che potrebbero fare quello che vuoi ...

Ecco un esempio di esso in uso:

public boolean onTouch(View v, MotionEvent event) { 

    mScaleDetector.onTouchEvent(event); 

    if (!mScaleDetector.isInProgress()) { 
     if (event.getAction() == MotionEvent.ACTION_DOWN || (event.getAction() == MotionEvent.ACTION_MOVE)) { 
      touchX = (int) event.getX(); 
      touchY = (int) event.getY(); 
      isTouched = true; 
     } 

     if (event.getAction() == MotionEvent.ACTION_UP) { 
      isTouched = false; 
     } 
    } else { 
     isTouched = false; 
    } 

    return true; 
} 
+0

Ciao lark, infatti, ho finito per usare il metodo "isInProgress()" di ScaleGestureDetector. Inoltre molti altri trucchi e metodi. Vedi la mia risposta alla mia domanda sopra. – Nemax

2

Ecco come ho risolto il problema: sovrascrivendo il metodo onDispatchTouchEvent() di Acity. Qualsiasi altra soluzione sembrava non funzionare. La cosa positiva del metodo onDispatchTouchEvent() è che viene sempre chiamato prima di inoltrare qualsiasi evento touch a qualsiasi altro ricevitore, in modo da poter intercettare qui ogni singolo evento touch.

Se l'evento viene gestito da qualche parte qui (scala o scorrimento), ritorno immediatamente senza inoltrare l'evento alla super classe, i. e. al resto della gerarchia della vista. Se non lo è, lo inoltro alla super-classe, quindi altre viste possono gestirlo, e. g. per rilevare i clic brevi o lunghi.

C'erano un paio di problemi da risolvere però: 1. Se l'utente inizia un gesto scala, ho dovuto annullare tutti i processi di rilevamento click lungo perché la vista di ricezione otterrebbe il primo evento in giù, poi niente più (dopo il secondo dito si abbassa e il ridimensionamento inizia), e poi pensa falsamente che venga eseguita una lunga pressione. 2. Quando è stata eseguita una pressione prolungata e il menu di scelta rapida è comparso, ho dovuto impedire lo scorrimento e il rilevamento di gesture in scala qui in dispatchOnTouchEvent() fino al prossimo evento UP, altrimenti lo scorrimento e il ridimensionamento sarebbero eseguiti anche se il menu di scelta rapida è presente.

Abbastanza complicato, ma ho trascorso ore e ore e un sacco di tentativi ed errori, e proprio non riuscivo a trovare alcuna soluzione più semplice. In ogni caso, gestendo 1. gesti in scala, 2. gesti di scorrimento orizzontale, 3. gesti di scorrimento verticale, 4. clic lunghi e 5. clic brevi, tutti sulla stessa vista obiettivo, non è una missione abbastanza semplice da realizzare .. .

Ecco il codice (parti rilevanti di esso):

@Override 
public boolean dispatchTouchEvent(MotionEvent e) { 
    if (eventInProgress) { 
     // View shall only receive scale gesture event if visible 
     if (targetView.isShown()) 
      scaleGestureDetector.onTouchEvent(e); 
     if (scaleGestureDetector.isInProgress()) 
      motionEventConsumed = true; 
    } 

    if (motionEventConsumed) { 
     if (e.getAction() == MotionEvent.ACTION_UP) 
      motionEventConsumed = false; 
     if (cancelLongPressEvent) { 
      cancelLongPressEvent = false; 
      targetView.cancelLongPress(); 
     } 
     return (true); 
    } 

    // Get the action that was done on this touch event 
    switch (e.getAction()) { 
    case MotionEvent.ACTION_DOWN: { 
     // store the X value when the user's finger was pressed down 
     downXValue = e.getX(); 
     downYValue = e.getY(); 
     cancelLongPressEvent = true; 
     eventInProgress = true; 
     break; 
    } 

    case MotionEvent.ACTION_MOVE: 
     // When having moved by too many x or y pixels, then 
     // cancel any ongoing long klick events 
     if (cancelLongPressEvent 
       && Math.abs(e.getX() - downXValue) 
         + Math.abs(e.getY() - downYValue) > 40) { 
      targetView.cancelLongPress(); 
      cancelLongPressEvent = false; 
     } 
     break; 

    case MotionEvent.ACTION_UP: { 
     if (eventInProgress) { 
      // Get the X value when the user released his/her finger 
      float deltaX = e.getX() - downXValue; 
      float deltaY = e.getY() - downYValue; 
      if (Math.abs(deltaX) > Math.abs(deltaY) 
        && Math.abs(deltaX) > 50) { 
       // going backwards: pushing stuff to the right 
       if (deltaX > 0) { 
        flipRight(); 
        return (true); 
       } 
       // going forwards: pushing stuff to the left 
       if (deltaX < 0) { 
        flipLeft(); 
        return (true); 
       } 
       break; 
      } 
     } 
    } 
    } 

    // If event was not handled here, then forward it to parent, 
    // i. e. to view hierarchy 
    return (super.dispatchTouchEvent(e)); 
} 

[...]

@Override 
public void onCreateContextMenu(ContextMenu menu, View v, 
     ContextMenu.ContextMenuInfo menuInfo) { 
    super.onCreateContextMenu(menu, v, menuInfo); 
    MenuInflater mi = getMenuInflater(); 
    mi.inflate(R.menu.lztv_context_menu, menu); 
    contextMenuTargetView = v; 
    eventInProgress = false; 
} 
Problemi correlati