Gesty, animacje i menu kontekstowe Nadal kontynuujemy rozwoj projektu MenedzerZadan Menu kontekstowe 1. Wykrywanie gestów będzie kłóciło się z wyborem elementu list przez "tapnięcie". Wobec tego zmienimy nasłuchiwacza "OnItemClickListener" na "OnItemLongClickListener" Tu kod z ZadaniaActivity.onCreate, ale to samo robimy w dwóch pozostałych aktywnościach. Potem zmienimy reakcję na bardziej intuicyjne menu kontekstowe: lista.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() { public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { RunningTaskInfo zadanie=listaZadan.get(position); ... return true; } }); Przy okazji możemy zmienić wygląd okna dialogowego na menu kontekstowe: ZadaniaActivity.java: lista.setOnItemLongClickListener(new OnItemLongClickListener() { public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { RunningTaskInfo zadanie=listaZadan.get(position); final String nazwaPakietu= zadanie.baseActivity.getPackageName(); AlertDialog.Builder adb = new AlertDialog.Builder(parent.getContext()); adb.setTitle("Menedżer zadań"); String[] etykiety = {"Przywróć", "Zamknij", "Anuluj"}; adb.setItems( etykiety, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); switch(which) { case 0: Intent launchIntent = getPackageManager().getLaunchIntentForPackage(nazwaPakietu); startActivity(launchIntent); break; case 1: //am.restartPackage(nazwaPakietu); am.killBackgroundProcesses(nazwaPakietu); break; default: case 2: break; } odswiezListe(); return; } }); AlertDialog ad = adb.create(); //ad.setMessage("Czy zamknąć zadanie "+((ZadanieInfo)lista.getItemAtPosition(position)).nazwa+"?"); //alertDialog.setIcon(R.drawable.icon); ad.show(); return true; //skonsumowane } }); ProcesyActivity.java: lista.setOnItemLongClickListener(new OnItemLongClickListener() { public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { RunningAppProcessInfo proces=listaProcesow.get(position); //final int pid = proces.pid; final String[] nazwyPakietow= proces.pkgList; AlertDialog.Builder adb = new AlertDialog.Builder(parent.getContext()); adb.setTitle("Proces"); String[] etykiety = {"Przywróć", "Zamknij", "Anuluj"}; adb.setItems( etykiety, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE); switch(which) { case 0: Intent launchIntent = getPackageManager().getLaunchIntentForPackage(nazwyPakietow[0]); startActivity(launchIntent); break; case 1: for(String nazwaPakietu : nazwyPakietow) { //am.restartPackage(nazwaPakietu); am.killBackgroundProcesses(nazwaPakietu); } break; default: case 2: break; } odswiezListe(); return; } }); AlertDialog ad = adb.create(); ad.show(); return true; } }); W UslugachActivity.java pozostaje bez zmian - usługa nie ma interfejsu, który można by wyświetlić --------------------- Gesty 2. Chcemy wykrywać gesty używane na liście (kontrolce). Gesty powinny być wykrywane na całej powierzchni ekranu, a lista owija jedynie zawartość (wrap_content). Zmieńmy w pliku res/layout/activity_menedzer.xml ustawienie layout_height: 3. Zacznijmy od klasy wykrywajacej gesty - dziedziczy z GestureDetector (a konkretnie z SimpleOnGestureListener). Jej definicję umieśćmy klasie ZadaniaActivity class WykrywaczGestowSwipe extends SimpleOnGestureListener { private static final int SWIPE_ODLEGLOSC_X_MINIMALNA = 120; private static final int SWIPE_ODLEGLOSC_Y_MAKSYMALNA = 250; private static final int SWIPE_PREDKOSC_MINIMALNA = 200; @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //tu nie wykryjemy tapniecia try { //wykluczamy zbyt duzy ruch w pionie if (Math.abs(e1.getY() - e2.getY()) > SWIPE_ODLEGLOSC_Y_MAKSYMALNA) return false; //swipe - w lewo if (e1.getX() - e2.getX() > SWIPE_ODLEGLOSC_X_MINIMALNA && Math.abs(velocityX) > SWIPE_PREDKOSC_MINIMALNA) { Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show(); } //swipe - w prawo else if (e2.getX() - e1.getX() > SWIPE_ODLEGLOSC_X_MINIMALNA && Math.abs(velocityX) > SWIPE_PREDKOSC_MINIMALNA) { Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { Toast.makeText(getApplicationContext(), "Wyjątek: "+e.getMessage(), Toast.LENGTH_SHORT).show(); } return false; } } * Ta klasa mogłaby i powinna mieć modyfikator static. Na razie go nie dodajemy, żeby można było użyć metody getApplicationContext * Na dobrą sprawę w tej chwili niepotrzebna jest klasa, metodę statyczną można by dodać do klasy ZadaniaActivity, ale ją rozbudujemy 5. Tworzymy instancję nowej klasy w metodzie onCreate (na końcu) i ustawiamy nasłuchiwacz OnTouchListener: //gesty final GestureDetector wykrywaczGestow = new GestureDetector(this, new ZadaniaActivity.WykrywaczGestowSwipe()); //lista - wysokosc zmieniona na fill_content lista.setOnTouchListener( new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { return wykrywaczGestow.onTouchEvent(event); } }); } //nazwias zamykający metodę onCreate 6. Po uruchomieniu przesuwanie palcem/kursorem po ekranie w lewo i prawo powinno być wykrywane i komunikowane toastami. 7. Do klasy WykrywaczGestowSwipe dodajmy konstruktor pobierajacy informację niezbędne do przełączania aktywności: static class WykrywaczGestowSwipe extends SimpleOnGestureListener { private ActivityWithRefreshableList biezacaAktywnosc; private Class klasaLewejAktywnosci,klasaPrawejAktywnosci; public WykrywaczGestowSwipe(ActivityWithRefreshableList biezacaAktywnosc, Class klasaLewejAktywnosci, Class klasaPrawejAktywnosci) { this.biezacaAktywnosc=biezacaAktywnosc; this.klasaLewejAktywnosci=klasaLewejAktywnosci; this.klasaPrawejAktywnosci=klasaPrawejAktywnosci; } 8. W metodzie onFlig toasty zamieńmy na polecenia przełączania aktywności: przelaczAktywnosc(biezacaAktywnosc, klasaPrawejAktywnosci); i przelaczAktywnosc(biezacaAktywnosc, klasaLewejAktywnosci); 9. W konsekwencji musimy zmienic sposob tworzenia instancji w metodzie onCreate: final GestureDetector wykrywaczGestow = new GestureDetector(this, new WykrywaczGestowSwipe(this,UslugiActivity.class,ProcesyActivity.class)); * Klasę WykrywaczGestowSwipe można już oznaczyć jako static o ile w sekcji catch umieścimy throw e; 10. W klasach ProcesyActivity i UslugiActivity (to drugie w domu) dodajemy do metody onCreate: //gesty final GestureDetector wykrywaczGestow = new GestureDetector(this, new ZadaniaActivity.WykrywaczGestowSwipe(this,ZadaniaActivity.class,UslugiActivity.class)); lista.setOnTouchListener( new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { return wykrywaczGestow.onTouchEvent(event); } }); W UslugiActivity inne są argumenty konstruktora WykrywaczaGestowSwipe! ---------------------------------- Animacje przełączania aktywności 1. W katalogu res tworzymy podkatalog anim. 2. W katalogu res/anim tworzymy nowy plik. Z menu kontekstowego New, Animation Resource File plik slide_in_left.xml: kopiujemy i zmieniamy parametry plik slide_out_left.xml: plik slide_right_in.xml: plik slide_right_out.xml: plik fade_in.xml: plik fade_out.xml: 3. W klasie ZadaniaActivity.java zdefiniujmy typ wyliczeniowy i pomocniczą metodę: static enum AnimacjaPrzyZmianieAktywnosci { NoAnimation,SlideLeft,SlideRight,Fadding }; static void ustawAnimacje(Activity biezacaAktywnosc,AnimacjaPrzyZmianieAktywnosci animacja) { switch(animacja) { case SlideLeft: biezacaAktywnosc.overridePendingTransition(R.anim.slide_in_left, R.anim.slide_out_left); break; case SlideRight: biezacaAktywnosc.overridePendingTransition(R.anim.slide_in_right, R.anim.slide_out_right); break; case Fadding: biezacaAktywnosc.overridePendingTransition(R.anim.fade_in, R.anim.fade_out); break; case NoAnimation: default: biezacaAktywnosc.overridePendingTransition(0, 0); break; } }; 4. Modyfikujemy metode przelaczAktywnosc (nowy argument): static void przelaczAktywnosc(Activity biezacaAktywnosc,Class klasaNowejAktynosci,AnimacjaPrzyZmianieAktywnosci animacja) { biezacaAktywnosc.finish(); ustawAnimacje(biezacaAktywnosc, animacja); Intent intencja = new Intent(biezacaAktywnosc.getApplicationContext(), klasaNowejAktynosci); biezacaAktywnosc.startActivity(intencja); } 5. W konsekwencji musimy zmodyfikować metodę akcjaPoWyborzeElementuMenu (uruchamiana poprzez menu). Użyjmy wówczas animacji Fadding (alpha blending). Niestety w Java nie ma parametrów domyślnych. static void akcjaPoWyborzeElementuMenu(ActivityWithRefreshableList activity, int itemId) { switch(itemId) { case R.id.menu_odswiez: activity.odswiezListe(); break; case R.id.menu_zadania: przelaczAktywnosc(activity, ZadaniaActivity.class, AnimacjaPrzyZmianieAktywnosci.Fadding); break; case R.id.menu_procesy: przelaczAktywnosc(activity, ProcesyActivity.class, AnimacjaPrzyZmianieAktywnosci.Fadding); break; case R.id.menu_uslugi: przelaczAktywnosc(activity, UslugiActivity.class, AnimacjaPrzyZmianieAktywnosci.Fadding); break; case R.id.menu_info: Toast.makeText(activity.getApplicationContext(), R.string.info, Toast.LENGTH_LONG).show(); break; case R.id.menu_koniec: activity.finish(); } } 6. I na koniec zmieniamy metodę WykrywaczGestowSwipe.onFling przełączająca aktywności w efekcie użycia gestów: @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { //tu nie wykryjemy tapniecia try { //wykluczamy zbyt duzy ruch w pionie if (Math.abs(e1.getY() - e2.getY()) > SWIPE_ODLEGLOSC_Y_MAKSYMALNA) return false; //swipe - w lewo if (e1.getX() - e2.getX() > SWIPE_ODLEGLOSC_X_MINIMALNA && Math.abs(velocityX) > SWIPE_PREDKOSC_MINIMALNA) { //Toast.makeText(getApplicationContext(), "Left Swipe", Toast.LENGTH_SHORT).show(); przelaczAktywnosc(biezacaAktywnosc, klasaPrawejAktywnosci, AnimacjaPrzyZmianieAktywnosci.SlideLeft); } //swipe - w prawo else if (e2.getX() - e1.getX() > SWIPE_ODLEGLOSC_X_MINIMALNA && Math.abs(velocityX) > SWIPE_PREDKOSC_MINIMALNA) { //Toast.makeText(getApplicationContext(), "Right Swipe", Toast.LENGTH_SHORT).show(); przelaczAktywnosc(biezacaAktywnosc, klasaLewejAktywnosci, AnimacjaPrzyZmianieAktywnosci.SlideRight); } } catch (Exception e) { //Toast.makeText(getApplicationContext(), "Wyjątek: "+e.getMessage(), Toast.LENGTH_SHORT).show(); //brak reakcji } return false; }