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;
}