2013-04-03 9 views
45

Zastanów się nad tym, jak bardzo się pogubić (w celu lepszego zrozumienia mojego problemu). enter image description hereMotionEvent.ACTION_UP nie nazywane

Jak widać, rozważam widok listy otoczony padding. Teraz, jeśli użytkownik naciśnie element listview, jako że akcja dostarczyła mu jasnoniebieskiego koloru tła. Teraz Moja aplikacja ma do czynienia z onTouch sama Wydarzenia w celu określenia czynności, takich jak

  • Click
  • od lewej do prawej Swipe
  • prawej do lewej Swipe

Oto mój kod.

public boolean onTouch(View v, MotionEvent event) { 
     if(v == null) 
     { 
      mSwipeDetected = Action.None; 
      return false; 
     } 
     switch (event.getActionMasked()) { 
     case MotionEvent.ACTION_DOWN: { 
      downX = event.getRawX(); 
      downY = event.getRawY(); 
      mSwipeDetected = Action.Start; 

     // Find the child view that was touched (perform a hit test) 
      Rect rect = new Rect(); 
      int childCount = listView.getChildCount(); 
      int[] listViewCoords = new int[2]; 
      listView.getLocationOnScreen(listViewCoords); 
      int x = (int) event.getRawX() - listViewCoords[0]; 
      int y = (int) event.getRawY() - listViewCoords[1]; 
      View child; 
      for (int i = 0; i < childCount; i++) { 
       child = listView.getChildAt(i); 
       child.getHitRect(rect); 
       if (rect.contains(x, y)) { 
        mDownView = child; 
        break; 
       } 
      } 


      return false; // allow other events like Click to be processed 
     } 
     case MotionEvent.ACTION_MOVE: { 
      upX = event.getRawX(); 
      upY = event.getRawY(); 
      float deltaX=0,deltaY=0; 
      deltaX = downX - upX; 
      deltaY = downY - upY; 

       if(deltaY < VERTICAL_MIN_DISTANCE) 
       { 
          setTranslationX(mDownView, -(deltaX)); 
          setAlpha(mDownView, Math.max(0f, Math.min(1f, 1f - 2f * Math.abs(deltaX)/listView.getWidth()))); 
          return false; 
       } 
       else 
       { 
        forceBringBack(v); 
       } 

          return false;    

     } 
     case MotionEvent.ACTION_UP: 
     { 

      stopX = event.getX(); 
      float stopValueY = event.getRawY() - downY;    
      float stopValue = stopX - downX; 

      if(!mDownView.isPressed()) 
      { 
       forceBringBack(mDownView); 
       return false; 
      }    

      boolean dismiss = false; 
      boolean dismissRight = false; 


      if(Math.abs(stopValue)<10) 
      { 
       mSwipeDetected = Action.Start; 
      } 
      else 
      { 
       mSwipeDetected = Action.None; 

      } 
      String log = ""; 
      Log.d(log, "Here is Y" + Math.abs(stopValueY)); 
      Log.d(log, "First Comparison of Stop Value > with/4" + (Math.abs(stopValue) > (listView.getWidth() /4))); 
      Log.d(log, "Second Comparison " + (Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE)); 
      Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value " + stopValue); 

      if((Math.abs(stopValue) > (listView.getWidth() /4))&&(Math.abs(stopValueY)<VERTICAL_MIN_DISTANCE)) 
      { 
       dismiss = true; 
       dismissRight = stopValue > 0; 

       if(stopValue>0) 
       { 
       mSwipeDetected = Action.LR; 

       } 
       else 
        mSwipeDetected = Action.RL; 
      } 
      Log.d(log, "Action Detected is " + mSwipeDetected + " with Stop Value after dissmiss" + stopValue); 

      if(dismiss) 
      { 
       if(dismissRight) 
        mSwipeDetected = Action.LR; 
       else 
        mSwipeDetected = Action.RL; 
       animate(mDownView) 
       .translationX(dismissRight ? listView.getWidth() : - listView.getWidth()) 
       .alpha(0) 
       .setDuration(mAnimationTime) 
       .setListener(new AnimatorListenerAdapter() { 
        public void onAnimationEnd(Animator animation) 
        { 

        } 
       }); 
      } 
      else 
      { 
       animate(mDownView) 
       .translationX(0) 
       .alpha(1) 
       .setDuration(mAnimationTime) 
       .setListener(null); 
      } 


      break;   

     } 
     } 
     return false; 
    } 

Jak widać, ustalam wykonaną akcję w MotionEvent.ACTION_UP i odpowiednio ustawiam wartość Enum Action. Ta logika działa jak zaklęcie, jeśli użytkownik nie przekracza granicy widoku listy.

Teraz, jeśli użytkownik przesuwając (lub konkretnie), przesuwając palcem wzdłuż elementu listy przesuwa się z niebieskiego na pomarańczowy, funkcja MotionEvent.ACTION_UP nie zostanie przekazana do widoku listy, co spowoduje, że mój kod nie podejmie decyzji i ze względu na metodę translationX() i setAlpha(), ponieważ w tym przypadku nie zostanie określona żadna akcja, ten konkretny element listy zostanie pusty.

Problem nie kończy się tutaj, ponieważ nie za każdym razem zawyżam widok, ten sam wiersz TranslatedX() jest napompowany za każdym razem, prowadząc do wielokrotnego pojawienia się pustego/białego elementu listy.

Czy jest coś, co można zrobić, nawet jeśli nie napotkałbym MotionEvent.ACTION_UP, nadal mógłbym podjąć decyzję?

Dzięki.

+0

zobaczyć, czy to rozwiąże pytanie: http://stackoverflow.com/questions/13283827/onintercepttouchevent-only-gets-action-down – user123321

Odpowiedz

142

Powinieneś return true; w case MotionEvent.ACTION_DOWN:, więc MotionEvent.ACTION_UP będzie obsługiwane.


Jak wyjaśniono na View.OnTouchListener:

Returns:

true jeśli słuchacz strawił zdarzenie, false w przeciwnym wypadku.

MotionEvent.ACTION_UP nie dostanie nazywa aż wystąpił MotionEvent.ACTION_DOWN, logicznym wytłumaczeniem jest to, że jest to niemożliwe, aby ACTION_UP wystąpić jeśli ACTION_DOWN nigdy wystąpił przed nim.

Ta logika umożliwia programistce blokowanie kolejnych zdarzeń po ACTION_DOWN.

+5

mam zawsze myliło się, dlaczego tak jest. Czy nigdzie nie ma wyjaśnienia logiki? –

+2

@Turbo Dodano logiczne wyjaśnienie, Wciąż myślę, że Android powinien wyjaśnić to lepiej lub naprawić zachowanie, aby było bardziej logiczne. – Danpe

+1

Pomyślałem, że "skonsumowany" oznacza, że ​​zdarzenie nie zostanie przekazane do warstwy interfejsu użytkownika pod konsumentem. Być może nadal chcę być świadomy dotknięć, ale nadal je przekazywać. Czy przywrócenie prawdziwej ochrony przed niższymi warstwowymi komponentami przed otrzymaniem zdarzenia dotyku? –

0

Jako Danpe explained in his concise answer - musiałem dodać kod ACTION_DOWN, aby ACTION_UP został rozpoznany.

  case MotionEvent.ACTION_DOWN: 

       return true; 

      case MotionEvent.ACTION_UP: 

       XyPos xyPos = new XyPos(); 
       xyPos.x = last_x; 
       xyPos.y = last_y; 
       handleViewElementPositionUpdate(xyPos); 

       break; 

miałem całą onTouch (..) Metoda zwraca Prawdę tak, więc nie jestem pewien, dlaczego to było za mało ... ale miło mieć to szybkie rozwiązanie .. (dzięki!)

4

Nie sądzę, aby dodanie return true; do sprawy MotionEvent.ACTION_DOWN: ostatecznie rozwiąże problem. To po prostu skomplikowało sytuację, w której return false mógł wykonać swoją pracę jak urok.

Co do zawiadomienia jest: MotionEvent.ACTION_DOWN: /*something*/ return true; zablokuje innych zwrotnych Listener dostępne dla widoku, nawet onClickListenerm, podczas gdy prawidłowo return false w MotionEvent.ACTION_UP: może pomóc MotionEvent być propagowane do prawego przeznaczenia.

odniesienia do jego kodu oryginalnego sourse: https://github.com/romannurik/android-swipetodismiss

+0

Uruchomiłem także projekt Roman Nurik, i zadziałało. Kiedy próbowałem zastosować kod do moich widoków, to nie zadziałało. Kiedy zmieniłem 'return false;' na 'return true;' to pomogło. – CoolMind

13

Należy również zauważyć, że w pewnych okolicznościach gest może zostać anulowana, w którym to przypadku MotionEvent.ACTION_UP NIE zostanie wysłana (np rotacje ekranu.). Zamiast tego zamiast tego zostanie wysłana MotionEvent.ACTION_CANCEL. Dlatego normalny instrukcja switch działania powinien wyglądać mniej więcej tak:

switch (event.getActionMasked()) { 
    case MotionEvent.ACTION_DOWN: 
     // check if we want to handle touch events, return true 
     // else don't handle further touch events, return false 

    case MotionEvent.ACTION_UP: 
    case MotionEvent.ACTION_CANCEL: 
     // finish handling touch events 
     // note that these methods won't be called if 'false' was returned 
     // from any previous events related to the gesture 
} 
+1

Nie wiem dlaczego, ale z jakiegoś powodu musiałem dodać anulować, aby nadrobić zaległości. – Rarw

+0

Właśnie uratowałeś mi wiele kłopotów .. wielkie dzięki :) –

+0

tylko oba rozwiązania działają dobrze. – Eugen

Powiązane problemy