Naučte sa vyvíjať aplikácie pre Android za 14 dní – dotyky a gestá II.

1

V predchádzajúcej časti sme ukázali detekciu viacnásobných dotykov, vrátane pohybu jednotlivých prstov po obrazovke. Android využíva pre niektoré úkony štandardné gestá, na ktoré si používatelia zvykli a očakávajú, že ich budú využívať aj v aplikáciách. 

V ponímaní operačného systému Android gesto začína v bode, ktorého sa na obrazovke dotkol prvý prst, pero, alebo iné polohovacie zariadenie. Končí vtedy, keď posledný prst alebo polohovacie zariadenie opustí povrch displeja. 

Na detekciu giest sa využíva trieda GestureDetector. Je potrebné vytvoriť inštanciu tejto triedy a implementovať interface GestureDetector.OnGestureListener. Následne prepíšete metódu  onTouchEvent().

Príklad – gesto pinch

V ankete o najpoužívanejšie heslo by určite zvíťazilo gesto pinch, ktoré sa robí položením dvoch prstov na displej a ich následným vzájomným priblížením a vzdialení, Najčastejšie sa toto gesto využíva na zoomovanie. Približovaním prstov k sebe do špičky (odtiaľ názov gesta) sa obrázok, alebo iný obsah na obrazovke zmenšuje, a opačným pohybom, kedy sa prsty od seba vzďaľujú sa obsah na obrazovke zväčšuje.  V príklade sa budeme sústrediť na identifikáciu giest, takže nebudeme riešiť zväčšovanie a zmenšovanie obsahu, ale iba vypíšeme aké gesto používateľ urobil. 

Používateľské rozhranie využijete prakticky bezo zmeny vytvorené Android Studiom pri vytváraní projektu. Jedinou úpravou je pridať ID prvku TextView

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
 
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:id="@+id/tw1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
 
</androidx.constraintlayout.widget.ConstraintLayout>

V kóde hlavnej aktivity je vytvorená trieda MojOnScaleGestureListener is declared which extends the Android SimpleOnScaleGestureListener. Je potrebné implementovať tri metódy onScale(), onScaleBegin() a onScaleEnd)(). Typ gesta sa vypíše v metóde onScale(). Metóda onTouchEvent () je volaná pri detekcii dotyku, alebo gesta. 

Pri spustení aplikácie na reálnom zariadení sú gestá dvoma prstami intuitívne. V emulátore ich môžete simulovať podržaním klávesy Ctrl a kliknutím a posunom kurzora myši.

Kód hlavnej aktivity

public class MainActivity extends AppCompatActivity {
 
    TextView tw1;
    ScaleGestureDetector sgDet;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tw1 = (TextView)findViewById(R.id.tw1);
 
        sgDet = new ScaleGestureDetector(this, new MojScaleGestureListener());
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        sgDet.onTouchEvent(event);
        return true;
    }
 
    public class MojScaleGestureListener extends
            ScaleGestureDetector.SimpleOnScaleGestureListener {
 
        @Override
        public boolean onScale(ScaleGestureDetector detector) {
            float scaleFactor = detector.getScaleFactor();
            if (scaleFactor > 1) tw1.setText("Zoom ---");
            else                 tw1.setText("Zoom +++");
            return true;
        }
 
        @Override
        public boolean onScaleBegin(ScaleGestureDetector detector) {
            return true;
        }
 
        @Override
        public void onScaleEnd(ScaleGestureDetector detector) {
 
        }
    }
}

 

Príklad – univerzálne počítadlo

Námetom aplikácie bude univerzálne počítadlo objektov. Jeho mechanický, alebo elektromechanický ekvivalent používajú napríklad letušky, či organizátori rôznych podujatí na sčítavanie návštevníkov.  Z hľadiska (technického) používateľského rozhrania sa bude jednať o interakciu medzi dotykovým tlačidlom, a výpisom obsahu premennej, ktorá sa inkrementuje dotykom tlačidla. Avšak z hľadiska fungovania aplikácie v širších súvislostiach sa jedná o interakciu vášho pohľadu, ktorý spočinie na osobe ktorú chcete pripočítať a impulzu pre kliknutie prsta na displej. Určite ste si všimli, že tu nie je žiadny priestor na pohľad na displej, ani hľadanie tlačidla. Preto musí byť tlačidlo čo najväčšie a kliknutie by malo byť nejakým nevizuálnym spôsobom potvrdené, buď zvukom, alebo vibráciou. Avšak pri veľkom tlačidle hrozí možnosť neúmyselného stlačenia pri manipulácii s telefónom. 

Pre eliminovanie nechceného dotyku sa môžete inšpirovať operačným systémom konkrétne mechanizmom odomykania gestom vodorovného posuvu, ktoré sa zvykne označovať „swipe“, alebo „fling“. Ak by ste objekty počítali týmto gestom, eliminovalo by to problém nechceného stlačenia tlačidla.

Layout hlavnej aktivity využíva kontejner ViewFlipper, nakoľko budeme animovať posun prvkov TextView obsahujúcich číselné hodnoty počítadla. ViewFlipper umožňuje animované prechody medzi dvoma a viacerými objektmi typu Views.

<?xml version="1.0" encoding="utf-8"?>
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/view_flipper"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <include
        android:id="@+id/include2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        layout="@layout/inc1" >
    </include>
 
    <include
        android:id="@+id/include1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        layout="@layout/inc2" >
    </include>
 
</ViewFlipper>

Pre správne fungovanie ViewFlippera je potrebné definovať dva vložené layouty. Môžete ich nazvať napríklad inc1.xml a inc2.xml. Obidva obsahujú jeden prvok TextView zapuzdrený v kontejneri LinearLayout. Vo výpise je layout inc1.xml. Layout inc2.xml sa líši len názvom prvku TextViev.

layout inc1.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:textSize="300sp" >
    </TextView>
 
</LinearLayout>

layout inc2.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
 
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        android:gravity="center"
        android:textSize="300sp" >
    </TextView>
 
</LinearLayout>

Nakoľko zobrazenie počítadla sa nemusí deliť o plochu displeja s tlačidlom a gesto je možné robiť cez celú obrazovku aj cez text počítadla, môžu byť číslice počítadlá veľké, aby boli dobre čitateľné.

Aby sme aplikáciu urobili čo najintuitívnejšiu, posúvajú sa čísla sprava doľava. Na prvý pohľad to vyzerá nelogicky, zdalo by sa, že lepšie by bolo robiť gestá zľava doprava. Avšak keď chceme robiť animáciu, lepšie je opačné poradie. Predstavte si postupnosť číslic, napríklad

1          2          3          4

a momentálne je zobrazené číslo 2. Akým gestom presuniete trojku na pozíciu dvojky? Predsa posuvom sprava doľava.

Ak používateľ urobí gesto sprava doľava, čiže takzvané fling gesto, potom  aktuálny TextView sa bude posúvať vľavo mimo ľavý okraj obrazovky. Zároveň sa za ním z pravej strany bude posúvať TextView s vyšším číslom. Všimnite si, že toto gesto funguje len vtedy, ak posuniete prstom sprava doľava. Ak urobíte rovnaké gesto v opačnom smere, nič sa nestane. 

V kóde hlavnej aktivity indikujeme gesto „Fling“. Keď GestureDetector detekuje fling bude volaná metóda onFling(). Gesto pre aktivovanie počítadla by malo byť trochu dynamické, energické, aby sa nedalo zameniť s náhodným pomalým vodorovným posuvom. Preto počítadlo inkrementujeme iba vtedy ak rýchlosť posuvu prsta vo vodorovnom smere prekročí hodnotu 10 bodov za sekundu.

Animácia pohybu sa realizuje prepínaním layoutov v metóde prepniLayout()

public class MainActivity extends AppCompatActivity {
 
    private ViewFlipper flipper;
    private TextView textView1, textView2;
    private int nStav, nPocitadlo;
    private GestureDetector gestDetector;
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView1 = (TextView) findViewById(R.id.textView1);
        textView2 = (TextView) findViewById(R.id.textView2);
 
        nStav = 0;
        flipper = (ViewFlipper) findViewById(R.id.view_flipper);
        textView1.setText(String.valueOf(nPocitadlo));
 
        gestDetector = new GestureDetector(this,
          new GestureDetector.SimpleOnGestureListener() {
              @Override
              public boolean onFling(MotionEvent e1, MotionEvent e2,
                                     float rychlostX, float rychlostY)  {
                  if (rychlostX <-10.0f) {
                      nStav = nStav == 0 ? 1: 0;
                      prepniLayout(nStav);
                  }
                  return true;
              }
          });
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return gestDetector.onTouchEvent(event);
    }
 
    public void prepniLayout(int switchTo) {
        nStav = switchTo;
 
        flipper.setInAnimation(animZprava());
        flipper.setOutAnimation(animZlava());
 
        nPocitadlo++;
        if (switchTo == 0)
            textView1.setText(String.valueOf(nPocitadlo));
        else
            textView2.setText(String.valueOf(nPocitadlo));
        flipper.showPrevious();
    }
 
    private Animation animZprava() {
        Animation zprava = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT, +1.0f,
                Animation.RELATIVE_TO_PARENT, 0.0f,
                Animation.RELATIVE_TO_PARENT, 0.0f,
                Animation.RELATIVE_TO_PARENT, 0.0f);
        zprava.setDuration(500);
        zprava.setInterpolator(new LinearInterpolator());
        return zprava;
    }
 
    private Animation animZlava()  {
        Animation zlava = new TranslateAnimation(
                Animation.RELATIVE_TO_PARENT, 0.0f,
                Animation.RELATIVE_TO_PARENT, -1.0f,
                Animation.RELATIVE_TO_PARENT, 0.0f,
                Animation.RELATIVE_TO_PARENT, 0.0f);
        zlava.setDuration(500);
        zlava.setInterpolator(new LinearInterpolator());
        return zlava;
    }
}

 

 

 

Rekapitulácia seriálu

1 deň – Prvá aplikácia 

2 deň – Možnosti emulátorov 

3 deň - Zorientujte sa v projekte aplikácie

4 deň – Princípy dizajnu a škálovania

5 deň – Uporiadanie prvkov používateľského rozhrania

6 deň – Obsluha udalostí

7 deň – Aplikácia s dvomi aktivitami  

8 deň – Spustenie na reálnom zariadení

9 deň – Intenty, alebo kto to urobí

10 deň – Dotyky a gestá

Luboslav Lacko

Všetky autorove články
Vývoj Android aplikácií Android Studio

1 komentár

Animácia čísel je zaujímava reakcia na: Naučte sa vyvíjať aplikácie pre Android za 14 dní – dotyky a gestá II.

1.4.2020 11:04
Super článok. Animácia aj dotyk funguje aj na reálnom zariadení. Len tak Ďalej.

Pridať komentár

Mohlo by vás zaujímať

Mohlo by vás zaujímať