Android Application Development Tutorial: Hvordan håndtere Multi-Touch

I den siste Android tutorial, så vi på håndtering av en berøringsskjerm hendelse. Dette behandles bare en enkelt peker, skjønt. Android kan håndtere flere pekere samtidig, noe som reflekterer hva som skjer når du har mer enn én finger på skjermen samtidig. Dette er hvordan multi-touch gester (som pinch-zoom) arbeid.

Faktisk kan vi se Android støtter flere tips ved å demonstrere en bug i den siste opplæringen kode. Kjør BubbleMove app fra den siste opplæringen, og begynne å dra boblen rundt på skjermen. Hvis du putter en annen finger på den første skjermen mens du gjør dette, og løft den første finger, boblen vil hoppe over skjermen til den andre fingeren plassering. Dette er fordi koden slik den står i dag håndterer bare standardpekeren. Den første finger ned er standardpekeren; men når det er tatt av skjermen, blir den andre fingeren (som den eneste gjenværende pekeren) standard, og boblen hopper over til denne nye standardpekeren. La oss se på hvordan man skal håndtere flere pekere eksplisitt

En rask notat:. Dessverre emulatoren har bare eksperimentell støtte for multi-touch. Å følge med denne opplæringen, må du koble opp din maskinvareenhet for testing, eller eksperimentere med tethered alternativet enheten ved forrige lenke

Håndtere flere pekere. Grunnleggende

Vi starter vår reise inn i multi-touch håndtering ved å fikse at flere peker bug. For å gjøre dette, må vi ta pekeren ID hensyn til i vår kode.

Den eneste delen av koden for den siste tutorial som vi trenger å endre er onTouchEvent (). Redigere det skal se slik ut (med et ekstra par private klassevariabler):

 private static final int INVALID_POINTER_ID = -1; private int activePointer = INVALID_POINTER_ID; public boolean onTouchEvent (MotionEvent e) {switch (e. getActionMasked ()) {case MotionEvent.ACTION_DOWN: thread.setBubble (e.getX (), e.getY ()); activePointer = e.getPointerId (0); gå i stykker; tilfellet MotionEvent.ACTION_MOVE: if (! activePointer = INVALID_POINTER_ID) {int pointerIndex = e.findPointerIndex (activePointer); thread.setBubble (e.getX (pointerIndex), e.getY (pointerIndex)); }      gå i stykker; tilfellet MotionEvent.ACTION_UP: if (! activePointer = INVALID_POINTER_ID) {showTotalTime (e.getEventTime () - e.getDownTime ()); activePointer = INVALID_POINTER_ID; }      gå i stykker; case MotionEvent.ACTION_CANCEL: activePointer = INVALID_POINTER_ID; gå i stykker; case MotionEvent.ACTION_POINTER_UP: int pointerIndex = e.getActionIndex (); int pointerId = e.getPointerId (pointerIndex); if (pointerId == activePointer) {showTotalTime (e.getEventTime () - e.getDownTime ()); activePointer = INVALID_POINTER_ID; }      gå i stykker; } Return true;} 

Vi bruker nå getActionMasked () i stedet for getAction () for bryteren uttalelse. getAction () returnerer bare en handling hvis det er en enkelt peker, men med flere tips, returnerer det en kombinasjon av pekeren action og en forskjøvet peker indeksen. For å unngå oss skiftende ut pekeren indeksen oss selv, kan vi bruke getActionMasked (), som bare returnerer en handling (ACTION_UP, ACTION_POINTER_DOWN, etc), og hvis dette er en peker handling, bruker getActionIndex () for å få pekeren indeksen.

Ta en titt på ACTION_POINTER_UP, som er den avgjørende del av den nye koden. En ACTION_POINTER_UP hendelse betyr at a
pekeren har gått opp, men ikke den eneste pekeren (hvis det er bare en peker, og som går opp, en ACTION_UP hendelse sendes), og ikke nødvendigvis den aktive pekeren. Så vi får indeks over denne pekeren, og pekeren ID knyttet til den. Hvis dette er den aktive spisser - den første finger vi sette på skjermen - så vi er ferdige med å flytte vår boble. Med andre ord, i denne versjonen av koden, vi bare ta hensyn til det første peker, og eksplisitt ignorere ytterligere tips. Ingenting annet vil skje med boblen til alle fingrene kommer ut av skjermen og et nytt sett med berørings hendelser skjer.

Hvis den aktive pekeren har gått opp, så viser vi den totale tiden for dra gest, og sette den aktive pekeren variabel til INVALID_POINTER_ID, for å vise at det ikke lenger er aktiv.

Nå tar en titt på ACTION_DOWN. Hvis denne handlingen er sendt, så er dette den første pekeren ned; så vi behandle det som den aktive markøren, og bruk getPointerId å redde pekeren ID som aktiv pekeren.

Med både ACTION_UP og ACTION_MOVE, før du gjør noe, sjekker vi at det er en gyldig aktiv pekeren. ACTION_MOVE blir sendt fra noen aktiv pekeren på skjermen, slik at vi bare ønsker å håndtere at data om det kommer fra vår egen aktive pekeren

ACTION_UP sendes bare når den endelige pekeren går opp.; men det endelige pekeren kan bli vår andre
pekeren, og da vi ignorere det. For å gjøre det litt klarere, her er en mulig sekvens av peker handlinger:


    Peker 1 (aktiv pekeren) går ned. ACTION_DOWN

    Peker 2 (ikke-aktiv pekeren) går ned. ACTION_POINTER_DOWN

    Begge pekere flytte: ACTION_MOVE

    Peker 1 (aktiv pekeren) går opp.. ACTION_POINTER_UP

    Peker 2 (ikke-aktiv pekeren) fortsetter å bevege seg .: ACTION_MOVE

    Peker 2 (ikke-aktiv pekeren) går opp. ACTION_UP

    I den listen, vi bare ønsker å svare på de Pointer 1 arrangementene. Så hvis vi får en ACTION_UP hendelse, sjekker vi først om vi fortsatt har et gyldig aktiv pekeren. Hvis ikke, så at ACTION_UP arrangementet kommer fra Pointer 2, og vi ignorere det. Hvis den aktive pekeren er fortsatt gyldig, viser vi den totale tiden Toast melding.

    Mer pekeren håndtering

    Hva om du ønsker å håndtere en eventuell andre pekeren går ned? Dette vil sette en peker indeksen og pop en Toast melding opp:

     private int newPointer = INVALID_POINTER_ID; public boolean onTouchEvent (MotionEvent e) {//... kode som før ... tilfellet MotionEvent.ACTION_POINTER_DOWN: int newPointerIndex = e.getActionIndex (); newPointer = e.getPointerId (newPointerIndex); Toast.makeText ("! New pointer" CTX,, Toast.LENGTH_SHORT) .vis (); gå i stykker; //... Resten av kode ...} 

    Pretty grei. Her er en interessant rynke, skjønt. Hvis du putter i kode for å håndtere andre pekeren akkurat på samme måte som den første, vil du hente opp med noe sånt som dette:

     public boolean onTouchEvent (MotionEvent e) {switch (e.getActionMasked ()) {//ACTION_DOWN, ACTION_CANCEL, ACTION_POINTER_DOWN som ovennevnte sak MotionEvent.ACTION_MOVE: if (! newPointer = INVALID_POINTER_ID) {int pointerIndex = e.findPointerIndex (newPointer); thread.setBubble (e.getX (pointerIndex), e.getY (pointerIndex)); } If (activePointer = INVALID_POINTER_ID!) {Int pointerIndex = e.findPointerIndex (activePointer); thread.setBubble (e.getX (pointerIndex), e.getY (pointerIndex)); }      gå i stykker; tilfellet MotionEvent.ACTION_UP: if (! activePointer = INVALID_POINTER_ID) {showTotalTime (e.getEventTime () - e.getDownTime ()); activePointer = INVALID_POINTER_ID; } If (newPointer = INVALID_POINTER_ID!) {NewPointer = INVALID_POINTER_ID; }      gå i stykker; case MotionEvent.ACTION_POINTER_UP: //komme pointerIndex og pointerId som før if (pointerId == activePointer) {showTotalTime (e.getEventTime () - e.getDownTime ()); activePointer = INVALID_POINTER_ID; } If (pointerId == newPointer) {newPointer = INVALID_POINTER_ID; }      gå i stykker; } Return true;} 

    Kjør denne og du vil se at når du løfter en finger eller sette den ned igjen, hopp boblen mellom fingrene og holder beveger seg jevnt. Men hvis du legger to fingre ned og flytte dem begge, de boble beveger seg med første
    finger. Nå kan du prøve å bytte rekkefølgen på ACTION_MOVE fall slik ut:

     case MotionEvent.ACTION_MOVE: if (! ActivePointer = INVALID_POINTER_ID) {int pointerIndex = e.findPointerIndex (activePointer); thread.setBubble (e.getX (pointerIndex), e.getY (pointerIndex)); } If (newPointer = INVALID_POINTER_ID!) {Int pointerIndex = e.findPointerIndex (newPointer); thread.setBubble (e.getX (pointerIndex), e.getY (pointerIndex)); } Bryte; 

    kompilere og kjøre, og du vil finne at denne gangen, følger boblen andre
    pekeren rundt. Hva som skjer er at det er nesten umulig å holde en finger fortsatt på skjermen, så ACTION_MOVE arrangementer blir sendt av både pekere hele tiden. De er for raske for det menneskelige øyet å følge, slik at en som du legger merke til er avhengig av hva som er vurdert andre. Hvis du vil ha noe mer komplisert å skje - for eksempel for boblen å sprette synlig mellom de to pekere, eller å følge et gjennomsnitt på dem begge - du trenger å gjøre mer komplisert behandling for å spore og gjennomsnittlig begge pekere .

    GestureDetectors

    Hvis du ønsker å gjøre kompliserte ting med flere fingre, vil du sannsynligvis ønske å håndtere dine pekere direkte, som i denne opplæringen. Men mye av tiden, kan det være lurt å håndtere en av et sett med standardbevegelser, for eksempel klype zoom. I dette tilfellet en GestureDetector - vil trolig være nyttig - et filter objekt som snur MotionEvents inn gest hendelser. I neste opplæringen, vil vi se på bruk GestureDetector og OnGestureListener å håndtere multi-touch-hendelser. Anmeldelser