Po pewnej przerwie kolejny odcinek, w którym przypatruję się Androidowi. Nie piszę z pozycji hiper fana, aczkolwiek jeśli zauważę jakieś plusy z mojego punktu widzenia, to o nich wspomnę. Dziś pewne kawałki wiedzy na pięć tematów:
- Fragmenty - co to takiego? Jak dla mnie odpowiednik “user control”, ale nie tylko, bo można dziedziczyć także bo bardziej specjalistycznych osiągając różne rodzaje nawigacji. Jak dla mnie podpinanie danych na liście trochę archaiczne w postaci jawnego w kodzie adaptera, jeśli porównać z deklaratywnym data bindingiem XAML czy Angular. Podpinanie zdarzeń z fragmentu do aktywności też IMHO porażka, ale w Javie nie ma zdarzeń. Trzeba więc jawnie implementować interfejsy fragmentu w aktywności i wywoływać obiekt hostującej aktywności z poziomu fragmentu. Przy scenariuszu master-details trzeba wszystko wiązać jawnie w kodzie, raczej nie ma czegoś takiego fajnego jak element databinding. Przekazywanie dynamicznie parametrów do fragmentu infrastrukturalnie jest w postaci słownika. Dużym plusem jest przenośność fragmentów na różne rodzaje urządzeń, smartfony i tablety (podobnie jak całego Androida). Aczkolwiek w WP 8.1 i Windows 8.1 również możemy bez żadnych problemów tworzyć przenośne kontrolki w XAML (jest już binarna zgodność), nie wspominając już o pełnoprawnym wsparciu dla HTML5 i JavaScript.
- Dynamiczna adaptacja dla różnych urządzeń - sporym plusem pojęcie niezależnego piksela, zasoby są automatyczne ładowane w zależności od cech urządzenia np. szerokość ekranu w niezależnych pikselach, a także np. orientacji ekranu. Automatyczne ładowanie zasobów realizowane także przez konwencję ma Windows / Windows Phone np. na gęstość pikseli (nie ma warunków na rozmiary ani orientację, w aplikacjach XAML jest API, które ułatwia zrobić behavior, w HTML5 można używać media queries, aczkolwiek ze względów wydajnościowych czasami lepiej rozdzielić pewne ekrany między Windows a Windows Phone, jak zrobili twórcy WinJS 2.1 na WP).
- ActionBar - jak dla mnie pewna wypadkowa pasków aplikacyjnych Windows i Windows Phone. Wyświetlanie ikon najczęściej używanych akcji, a rzadziej już w menu na końcu bez ikon podobnie jak w WP, tylko że w górnym pasku i niekoniecznie muszą to być maksymalnie cztery ikony. Idea podziału paska z komendami na górny i dolny kojarzy mi się trochę z kolei z dwoma paskami w Windows. Przy czym górny pasek w Android to ikona i tytuł aplikacji, a dolny – ikony akcji i menu. Windows górny pasek ma służyć do nawigacji, nie ma marnotrawienia miejsca na tytuł i ikonę aplikacji. Android ma specjalny panel boczny do nawigacji, o czym w następnym punkcie. Dwa paski pojawiają się w Android na wąskich ekranach, w rodzinie Windows są tylko w Windows, który jest obecnie od tabletów wzwyż (większe ekrany to więcej pasków). W universal apps ten sam kod spowoduje że pozycje w drugiej sekcji na Windows Phone zostaną umieszczone w menu bez ikon, Android w zależności od szerokości ekranu może podzielić pasek na dwa i w zależności od miejsca niemieszczące się pozycje przesuwa do menu. Wiązanie obsługi/budowania ActionBar z fragmentami, obok samych już aktywności wydaje mi się zaciemnieniem obrazu, ale może przez brak eventów nie dało się zrobić lepiej.
- Navigation Drawer - co to jest? “Nawigacyjna szuflada” w prostym tłumaczeniu całkiem trafnie oddaje istotę rzeczy. Z mojej perspektywy nazwałbym to raczej wjeżdżanym panelem. Lewy jest polecany do nawigacji i do ogólnych funkcji aplikacji, prawy do funkcjonalności danego ekranu. W wielu nowoczesnych aplikacjach Web lewe, rozwijane menu jest obecnie popularne. Taki motyw odnajdziemy także w niektórych aplikacjach Windows Store np. Mail. Prawy wyjeżdżany panel to w Windows 8.x dość popularny motyw - “Charms Bar”, panel właściwości, do współdzielenia danych między aplikacjami, drukowania itp. O ile jednak API w Windows jest proste, o tyle w Android poprawne oprogramowanie menu bocznego z prostą listą to już kilka klas Javy, trzeba pamiętać o różnych synchronizacjach, przeładowaniach.
- Korzystanie z aktywności pomiędzy różnymi aplikacjami - odpowiednik znanych z Windows/Windows Phone: nawigacji pomiędzy stronami w ramach jednej aplikacji, tasków dla systemowych aplikacji, klasy launcher-a, kontraktów, deklaracji, pewnych GUID-ów w manifestach. Android ma jednolite zasady na tworzenie komunikacji między stronami tej samej aplikacji, a także pomiędzy dwiema aplikacjami, zarówno firm trzecich jak i systemowych. Może to być pewnym plusem, choć korzystanie z API Windows/Windows Phone nie uważam za trudne. W Android jest ciekawe podejście, że aplikacja to zbiór aktywności, i jak wywołamy jakąś z nich z poziomu innej app-ki, to nie wstaje cała aplikacja. W Windows/Windows Phone aplikacja to jednak całość, która może być aktywowana na bardzo wiele różnych sposobów, także przez inne aplikacje.
A poniżej bardziej szczegółowe notatki na omówione na wspomniane dziś tematy.
Fragmenty
Dzielą UI na sekcje
Fragment
- grupa elementów UI (mogą być takie same jak w aktywności) + ich zachowanie
- tworzy logiczną jednostkę UI
- pozwala na reorganizację UI na różnych urządzeniach
- można używać statycznych plików layoutu
- może być dynamicznie włączany do kodu
- koncepcyjnie podobny do user control
- kluczowa większość zachowań w nawigacji
- nawigacja oparta na stronach
- nawigacja pomiędzy zakładkami
- nawigacja oparta o listę (np. rozwijana lista z górnego menu w aplikacji pocztowej)
Wsparcie
- na 99,9% aktywnych urządzeń (szeroko dostępne od drugiej połowy 2011)
- natywne wsparcie systemu na urządzeniach z Android 3.1 lub nowszym (>= API 12)
- po raz pierwszy dostępne w bibliotece w Android 3.0 (API 11)
- biblioteka na urządzeniach z Android od 1.6 do 2.3 (API 4 - API 10)
Tworzenie
- Klasa dziedzicząca po klasie Fragment
- Wyświetlana zawartość
- nadpisanie metody onCreateView i/lub onCreate
- często opis layoutu w pliku XML (podobnie jak w aktywności)
- specjalizowane klasy dziedziczące po Fragment ułatwiają pewne przypadki
- Dołączenie fragmentu do aktywności
- Element <fragment> w XML z layoutem aktywności
- Atrybut class identyfikuje pełną kwalifikowaną nazwę klasy fragmentu do utworzenia
- Możemy dołączyć do widoku dowolną ilość fragmentów
- Element <fragment> w XML z layoutem aktywności
- Kompilator od API 11
- Alternatywnie: FragmentActivity + API 4 – 10
public class XFragment extends Fragment {
string[] mStrings;
TextView mXTextView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mStrings = getResources().getStringArray(R.array.x_strings);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View theView = inflater.inflate(R.layout.fragment_x, container, false); //ostatni parametr: attachToRoot
mXTextView = (TextView) theView.findViewById(R.id.x);
return theView;
}
public void setItem(int index) {
mXTextView.setText(mStrings[index]);
}
}
arrays.xml
<?xml version=”1.0” encoding=”utf-8”?>
<resources>
<string-array name=”x_strings”>
<item>@string/android_fragments_title</item>
<item>@string/android_intents_title</item>
</string-array>
</resources>
public class YFragment extends ListFragment {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String[] strings = getResources().getStringArray(R.array.y_strings);
ArrayAdapter<String> yAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.list_item_1, strings);
setListAdapter(yAdapter);
}
}
Android Studio
- Designer –> Palette –> Custom –> <fragment> –> okno wyboru Fragments
- utworzony fragment wstawiamy do rootLayout
<LinearLayout …>
<fragment
android:layout_width=”wrap_content”
android: layout_height=”0dp”
android:name=”com.abc.fgh.XFragment”
android:id=”@+id/xFragment”
android:layout_weight=”1”
</LinearLayout>
Struktura aktywności zawierającej fragmenty
- FragmentManager
- Wewnątrz fragmentu layout z widokami
- Fragment spodziewa się, że aktywność zaimplementuje pewne interfejsy
public interface OnXSelectionChangeListener {
public void onXSelectionChanged(int index);
}
Uzupełniamy
public class YFragment extends ListFragment {
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
OnXSelectionChangeListener listener = (OnXSelectionChangeListener) getActivity();
listener.onCourseSelectionChanged(position);
}
…
}
public class MainActivity extends Activity
implements OnXSelectionChangeListener {
…
@Override
public void onXSelectionChanged(int index) {
FragmentManager fm = getFragmentManager();
XFragment xFragment = (XFragment) fm.findFragmentById(R.id.xFragment);
xFragment.setItem(index);
}
}
Można tak zrobić listview-details (po wybraniu pozycji z listy zmieniamy dane we fragmencie szczegółów)
Fragmenty z bardziej typowego punktu widzenia dla Androida
Android Studio
- New Project: Navigation Type (None, Fixed Tabs + Swipe, Scrollable Tabs + Swipe, Dropdown)
Wybieramy Scrollable Tabs + Swipe
activity_main.xml
<android.support.v4.view.ViewPager …>
<android.support.v4.view.PagerTitleStrip … />
</android.support.v4.view.ViewPager>
public class MainActivity extends FragmentActivity {
SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
//adapter zwracający fragmenty dla każdej sekcji
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
…
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
Fragment fragment = new DummySectionFragment();
Bundle args = new Bundle();
args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
return 3;
}
@Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return “Sekcja 1”.toUpperCase(l);
case 1:
return “Sekcja 2”.toUpperCase(l);
case 2:
return “Sekcja 3”. toUpperCase(l);
}
return null;
}
}
public static class DummySectionFragment extends Fragment {
…
}
}
Usuwamy z layout fragment_main_dummy.xml i inne niż podstawowe values
Tworzymy zasób dla layoutu fragmentu
public class CourseFragment extends Fragment {
…
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View theView = inflater.inflate(R.layout.fragment_course_info, container, false);
Bundle arguments = getArguments();
if (arguments != null) {
String courseTitle = arguments.get(COURSE_TITLE);
…
}
return theView;
}
}
FragmentPagerAdapter - zakłada statyczną zawartość fragmentu
FragmentStatePagerAdapter - zakłada dynamiczną zawartość fragmentu
public class CoursePagerAdapter extends FragmentPagerAdapter {
String[] titles;
public CoursePagerAdapter(FragmentManager fm, Context context) {
super(fm);
Resources resources = context.getResources();
titles = resources.getStringArray(R.array.course_titles);
}
@Override
public Fragment getItem(int i) {
Bundle arguments = new Bundle();
arguments.putString(CourseFragment.COURSE_TITLE, titles[i]);
CourseFragment courseFragment = new CourseFragment();
courseFragment.setArguments(arguments);
return courseFragment;
}
@Override
public CharSequence getPageTitle(int position) {
return titlesShort[position];
}
@Override
public int getCount() {
return titles.length;
}
}
Android Studio
- menu podręczne: Creating constructor matching super
public class MainActivity extends FragmentActivity {
CoursePagerAdapter mCoursePagerAdapter;
…
@Override
protected void onCreate(Bundle savedInstanceState) {
…
mCoursePagerAdapter = new CoursePagerAdapter(getSupportFragmentManager(), this);
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mCoursePagerAdapter);
}
}
Dynamiczna adaptacja dla różnych urządzeń
Wsparcie adaptacji przez Android
- zarządzanie layoutem
- fragmenty
- niezależność od gęstości
- dynamiczny wybór zasobów
Niezależność od gęstości
- Niezależne od gęstości piksele
- Znormalizowana jednostka zgodna z wyświetlaczem 160dpi
- Przechwycenie przez Android translacji pomiędzy niezależnym pikselem od urządzenia a fizycznym pikselem
- dp
Tablet 1: 7’’, 720px x 1280px, 600dp x 1000dp
Tablet 2: 7’’, 1080px x 1920px, 600dp x 1000dp
Dynamiczny wybór zasobu
- Charakterystyka urządzenia: gęstość ekranu, orientacja, wersja OS, rozmiar ekranu, … (http://developer.android.com/guide/topics/resources/available-resources.html, http://developer.android.com/training/basics/supporting-devices/index.html)
- Przypisanie poprzez przyrostek do folderu z zasobami (możliwe kombinacje charakterystyk), automatyczny wybór zasobu przez system
Android Studio
- New –> Android resource directory (nazwa folderu, rodzaj layoutu, kwalifikatory np. orientacja ekranu: layout-land)
android:layout_height=”match_parent”
android:layout_marginLeft=”@dimen/activity_horizontal_margin”
style=”@android:style/TextAppearance.Holo.Medium”
res/values/dimens.xml
<resources>
<dimen name=”activity_horizontal_margin”>16dp</dimen>
</resources>
Dwa sposoby na powiązanie zasobów z rozmiarami ekranu
- Grupy rozmiarów ekranu (dostępne na wszystkich urządzeniach, problematyczne, nie są ściśle określone, zbyt szeroko)
- small
- normal
- large
- xlarge
- Kwalifikatory rozmiarów ekranu (dostępne od API Level 13 / Android 3.2, ścisła kontrola, w oparciu o dp specyficzne rozmiary)
Android Studio
- New –> Android resource directory –> kwalifikatory:
- Smallest Screen Width (najmniejszy rozmiar urządzenia niezależnie od orientacji)
- Screen Width (szerokość urządzenia od lewej do prawej w bieżącej orientacji) np. layout-w600dp
- Size: Small, Normal, Large, X-Large np. layout-large
Problem – duplikacja plików w folderach layout-*
Aliasy zasobów (jeden folder layout z kilkoma wersjami zasobów, więcej folderów values-* z plikami ref.xml)
values-land/refs.xml
<resources>
<item name=”activity_main” type=”layout”>@layout/activity_main_wide</item>
</resources>
Menu –> ActionBar
ActionBar
- od API Level 11
- używa tych samych zasobów i metod co menu
- centrum interakcji
- najczęściej używane akcje
- rzadziej używane akcje przenosić do menu (unikać zagnieżdżonego)
Koncepcyjnie kojarzy się z paskiem aplikacji z Windows Phone, gdzie rzadziej używane opcje też trafiają do menu
Zachowania
- podstawowe jako menu (onCreateOptionsMenu, onOptionsItemSelected)
- atrybut pozycji showAsAction
- always (zawsze na pasku akcji)
- ifRoom (zawsze, jeśli jest miejsce, inaczej trafia do “action overfow”)
- withText (umieszcza tekst z ikoną, jeśli jest na to miejsce)
- wiele wartości oddzielamy |
<menu xmlns:android=”…”>
<item
android:id=”@+id/action_edit”
android:title=”@string/action_edit”
android:icon=”@drawable/ic_action_edit” //używana tylko na pasku
android:showAsAction=”always|withText” />
</menu>
Fragment może przechwytywać i dodawać pozycje do ActionBar
- domyślnie tylko Activity jest podłączona do pozycji ActionBar
- aby Fragment miał dostęp do akcji wołamy metodę setHasOptionsMenu
- Fragment ma dostęp do pozycji ActionBar
- metoda Fragmentu onOptionsItemSelected jest wołana przed jej odpowiednikiem w Activity
- jeśli Fragment zwróci false, to wołana jest metoda z Activity
- Fragment może dodawać pozycje do ActionBar
- pozycje Fragmentu pojawiają się po pozycjach Activity
- ActionBar automatycznie aktualizuje się przy dodawaniu i usuwaniu Fragmentów
public class XFragment extends Fragment {
…
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean handled = true;
switch (item.getItemId()) {
case R.id.action_view:
…
break;
}
return handled;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if(flag)
inflater.inflate(R.menu.course_refs, menu);
}
}
To samo menu na smartfonie pokaże mniej pozycji na pasku akcji, a na tablecie więcej
Specjalne zachowania
- Ikona ActionBar jako przycisk “Up” lub “Home”
- Podział ActionBar na 2 części
- na wąskich ekranach, ikony i menu przesunięte do paska na dole ekranu (u góry pasek z ikoną i tytułem)
- na szerokich ekranach pozostaje pojedynczy pasek
Przycisk Home:
Aktywność
onCreate
ActionBar actionBar = getActionBar();
actionBar.setTitle(title);
actionBar.setDisplayHomeAsUpEnabled(true);
Manifest (od Android 4.1)
<activity
android:name=”xxx”
android:label=”@string/title_avtivity_xxx”
android:parentActivityName=”yyy” >
<meta-data
android:name=”android.support.PARENT_ACTIVITY”
android:value=”yyy”/>
</activity>
Podział paska na dwa:
<activity …
android:uiOptions=”splitActionBarWhenNarrow” … >
Jeśli chcemy na każdej aktywności w aplikacji atrybut definiujemy na poziomie aplikacji.
ActionBar ładowany jest z właściwościami
- wyświetlanie w formie zakładek
- nawigacja z listą dropdown (na tytule)
- właściwości do autoukrywania
- …
http://developer.android.com/design/patterns/actionbar.html
Główny element, na który należy zwrócić uwagę przy pisaniu aplikacji
Nawigacja
Navigation drawer
- View, który może być wyciągnięty z boku ekranu
- Wytyczne
- z lewej - nawigacja lub zachowanie dla całej aplikacji
- z prawej - zachowanie specyficzne dla ekranu
- Aplikacyjny “drawer” powinien być zgodny dla wszystkich ekranów
- http://developer.android.com/design/patterns/navigation-drawer.html
DrawerLayout
- korzeń layoutu aktywności
- dwoje dzieci
- pierwsze dziecko: główne View
- drugie dziecko: wyciągany panel
- layout_gravity - pozycja po wyciągnięciu
- layout_gravity - left lub start dla nawigacji dla całej aplikacji
- zachowanie “swipe to open”
- przy stosowaniu nawigacji po całej aplikacji wszystkie layouty Activity potrzebują DrawerLayout
- szerokość powinna zawierać się w granicach 240dp – 320dp
<android.support.v4.widget.DrawerLayout
xmlns:android=”…”
android.id=”@+id/drawer_layout”
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
<!— Main –>
<RelativeLayout …
android:layout_width=”match_parent”
android:layout_height=”match_parent”>
</RelativeLayout>
<!—Drawer –>
<RelativeLayout …
android:layout_width=”240dp”
android:layout_gravity=”start”
android:background=”#000000”>
<TextView …
android:textColor=”#FFFFFF”
android:text=”ABC”
/>
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
Android Studio
- Ctrl + Alt + L: Reformat Code
Powiązanie z ActionBar
- Klasa ActionBarDrawerToggle wiąże panel nawigacyjny z paskiem akcji
- Włączenie ikony na ActionBar do otwierania i zamykania panelu
- Musimy wywołać ActionBar.setDisplayHomeAsUpEnabled
- Musimy wywołać ActionBarDrawerToggle.syncState z onPostCreate aktywności
- Przy otwartym panelu powinniśmy zdisablować każdą pozycję związaną z kontekstem ekranu
- Nadpisujemy metodę onPrepareOptionsMenu w aktywności do ukrywania/deaktywacji opcji
- na aktywności wywołujemy invalidateOptionsMenu do zaktualizowania menu
- Activity potrzebuje informować ActionBarDrawerToggle o znaczących zdarzeniach
- Potrzebujemy informować o wyborze użytkownika w onOptionsItemSelected
- Potrzebujemy informować o zmianach layoutu w onConfigurationChanged (zmiana charakterystyki urządzenia)
- Włączenie ikony na ActionBar do otwierania i zamykania panelu
FragmentStatePagerAdapter pozwala na zamianę zawartych fragmentów. Metoda notifyDataSetChanged informuje, że fragmenty muszą być przeładowane.
public class XAdapter extends FragmentStatePagerAdapter {
…
private void XXX() {
…
notifyDataSetChanged();
}
}
<android.support.v4.widget.DrawerLayout …>
<android.support.v4.view.ViewPager …>
</android.support.v4.view.ViewPager>
<ListView …
android:choiceMode=”singleChoice”
android:divider=”@android:color/transparent”
android:dividerHeight=”0dp”
android:background=”#111”/>
</android.support.v4.widget.DrawerLayout>
public class NavigationDrawerHelper {
DrawerLayout mDrawerLayout;
ListView mDrawerListView;
ActionBarDrawerToggle mDrawerToggle;
public void init(Activity theActivity, ListView.OnItemClickListener listener) {
mDrawerLayout = (DrawerLayout) theActivity.findViewById(R.id.drawer_layout);
mDrawerListView = (ListView) theActivity.findViewById(R.id.left_drawer);
String[] options = theActivity.getResources().getStringArray(R.array.navigation_drawer_options);
ArrayAdapter<String> navigationDrawerAdapter =
new ArrayAdapter<String>(theActivity, R.layout.drawer_option_item, options);
mDrawerListView.setAdapter(navigationDrawerAdapter);
mDrawerListView.setOnItemClickListener(listener);
mDrawerLayout.setDrawerShadow(R.drawable.draver_shadow, GravityCompat.START);
mDrawerListView.setItemChecked(0, true);
setupActionBar(theActivity);
}
private void setupActionBar(Activity theActivity) {
/*closure*/
final Activity activity = theActivity;
ActionBar actionBar = theActivity.getActionBar();
actionBar.setDisplayHomeAsUpEnabled(true);
mDrawerToggle = new ActionBarDrawerToggle {
theActivity,
mDrawerLayout,
R.drawable.ic_drawer,
R.string.open_drawer_message,
R.string.close_drawer_message
} {
@Override
public void onDrawerClosed(View drawerView) {
activity.invalidateOptionsMenu();
super.onDrawerClosed(drawerView);
}
@Override
public void onDrawerOpened(View drawerView) {
activity.invalidateOptionsMenu();
super.onDrawerOpened(drawerView);
}
}
};
}
public void handleSelect(int option) {
mDrawerListView.setItemChecked(option, true);
mDrawerLayout.closeDrawer(mDrawerListView);
}
public void handleOnPrepareOptionsMenu(Menu menu) {
boolean itemVisible = ! mDrawerLayout.isDrawerOpen(mDrawerListView);
for (int index = 0; index < menu.size(); index++) {
MenuItem item = menu.getItem(index);
item.setEnabled(itemVisible);
}
}
public void handleOptionsItemSelected(MenuItem item) {
mDrawerToggle.onOptionsItemSelected(item);
}
public void syncState() {
mDrawerToggle.syncState();
}
public void setSelection(int option) {
mDrawerListView.setItemChecked(option, true);
}
}
drawer_option_item.xml
<TextView
…
android:layout_width=”match_parent”
android:layout_height=”wrap_content”
android:textAppearance=”?android:attr/textAppearanceLarge”
android:text=”XYZ”
android:textColor=”#ffffff”
android:gravity=”center | center_vertical”
android:background=”?android:attr/activatedBackgroundIndicator”>
</TextView>
Navigation drawer najczęściej implementowany jako ListView
- ListView odgrywa rolę menu
- implementacja ListView.OnItemClickListener
- przy otwieraniu ListView powinno wskazywać aktualny wybór
MainActivity extends Activity {
…
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int optionLib, long l) {
mCoursePagerAdapter.setCourseLib(optionLib);
mNavigationDrawerHelper.handleSelect(optionLib);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
mNavigationDrawerHelper.syncState();
}
@Override
public boolean onPrepareOptionsMenu (Menu menu) {
mNavigationDrawerHelper.handleOnPrepareOptionsMenu(menu);
return super.onPrepareOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mNavigationDrawerHelper.handleOnOptionsItemSelected(item);
return super.onOptionsItemSelected(item);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
mNavigationDrawerHelper.syncState();
super.onConfigurationChanged(newConfig);
}
}
Używanie aktywności pomiędzy aplikacjami
Intent-y do wywoływania innych aktywności
- jawne używają nazwy komponentu do identyfikacji celu
- niejawne używają informacji do identyfikacji celu (wołający nie potrzebuje szczegółowej wiedzy o aktywności, która przechwyci żądanie)
- docelową aktywnością może być każda aplikacja
- aplikacja zawierająca docelową aktywność nie potrzebuje być uruchamiana
Aktywności używają filtrów Intent-ów do ogłaszania swoich funkcjonalności (capabilities) (zawierają reguły dla żądań, które aktywność może przechwycić)
Wywoływanie przez niejawne cele
<manifest …>
<application …>
<activity
android:name=”.XXX” …>
<intent-filter>
<action
android:name=”com.ps.action.XX” />
<category
android:name=”android.intent.category.DEFAULT”/>
<intent-filter>
</activity>
<activity
android:name=”.YYY” … />
</application>
</manifest>
Filtry
- android.intent.action.CALL
- android.media.action.IMAGE_CAPTURE
- android.intent.action.VIEW inne info
- android.ps.action.XX
new Intent(“com.ps.action.XX”)
Szukanie, która aktywność tak się zarejestrowała
Intencje i filtry intencji składają się maksymalnie z 3 części:
- akcja
- intencja: operacja do wykonania
- filtr intencji: operacja, która może być wykonana
- dane
- intencja: dane, na których zostanie wykonana operacja
- filtr intencji: typ lub format danych, na których operacja może być wykonana
- kategoria
- podział komponentów na podgrupy
- z tendencją do używania częściej z filtrami intencji niż intencjami
- Intent.CATEGORY_DEFAULT wymagana przez aktywności otrzymujące niejawne intencje
- Intent.CATEGORY_LAUNCHER wymagana przez aktywności do pojawienia się w launcherze aplikacji
Wywołanie rozmowy telefonicznej
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse(“tel:123.456.7890”));
startActivity(intent);
Wyświetlenie strony Web
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(“http://xxx.yyy”));
startActivity(intent);
Wywołanie niestandardowej akcji
<application …>
<activity …>
<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>
<intent-filters>
<action android:name=”com.xxx.action.BROWSE_COURSES”/>
</intent-filters>
</activity>
…
</application>
<uses-permission android:name=”android.permission.INTERNET” />
Intent intent = new Intent(“com.xxx.action.BROWSE_COURSES”);
intent.putExtra(“xxx”, y);
startActivity(intent);
Wszystkie aktywności, które wspierają startActivity muszą zawierać CATEGORY_DEFAULT.