Date post: | 17-Jul-2015 |
Category: |
Technology |
Upload: | tomas-kypta |
View: | 38 times |
Download: | 0 times |
Android: vývoj pro telefon a tablet
Tomáš Kypta@TomasKypta
O čem to dnes bude
● vývoj pro Android tablety● co se změnilo příchodem tabletů● jak vyvíjet aplikace optimalizované pro
telefony i tablety
Současný stav
Telefon vs. tablet
● různé hardwarové vlastnosti– velikost displeje
● nutné vytvořit různý layout
– telefonní modul (data ano, volání a SMS ne), ... ● některé aplikace se na tablet nehodí (SMS jízdenka,
Free SMS Sender, ...)
– výdrž baterie
Android komponenty
● 4 komponety:
1. Activity – vizuální komponenta
2. Service
3. ContentProvider
4. broadcast Receiver
Telefon vs. tablet
● telefon = single-pane layout aktivit
Telefon vs. tablet
● table = multi-pane layout aktivit
Fragment
● část uživatelského rozhranní nebo programu, která může být umístěna v aktivitě
● od API level 11 (Android 3.0)
● backportován jako knihovna od API level 4 (Android 1.6)
● interakce přes FragmentManager
● fragment nelze použít bez Aktivity
Fragmenty - telefon<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- "Fragment A" -->
<fragment class="com.example.android.NoteListFragment"
android:id="@+id/list_frag"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
● aktivita A obsahuje fragment A● aktivita B obsahuje fragment B
Fragmenty - tablet<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent" android:layout_height="match_parent"
android:id="@+id/frags">
<!-- "Fragment A" -->
<fragment class="com.example.android.NoteListFragment" android:id="@+id/list_frag"
android:layout_width="@dimen/titles_size" android:layout_height="match_parent"/>
<!-- "Fragment B" -->
<fragment class="com.example.android.NoteEditFragment" android:id="@+id/details_frag"
android:layout_width="match_parent" android:layout_height="match_parent" />
</LinearLayout>
● aktivita A obsahuje fragmenty A i B
Fragmenty - příklad
● aplikace pro správu poznámek● fragment A = seznam poznámek● fragment B = detail/editace poznámky
Fragmenty
● nemanipulujte s jedním fragmentem z jiného● obsah fragmentu manipulujte zevnitř
fragmentu, ne z aktivity
Fragmenty
● komunikace s aktivitou přes callback interface– pro každý fragment definujeme callback interface
– pokud chce být aktivita volána z fragmentu musí implementovat interface
Fragmenty - callbackypublic class NoteListFragment extends ListFragment {
public interface NoteListEventsCallback { // callback interface
public void onShowDetails(int index);
public void onNoteDeleted();
}
private NoteListEventsCallback mContainerCallback;
@Override public void onAttach(Activity activity) {
super.onAttach(activity);
try { // check that the containing activity implements our callback
mContainerCallback = (NoteListEventsCallback) activity;
} catch (ClassCastException e) {
activity.finish();
throw new ClassCastException(activity.toString()+"must implement NoteSelectedCallback");
}
}
// implementace fragmentu
}
Fragmenty - callbackypublic class NotepadActivity extends Activity implements NoteListEventsCallback {
// implementace aktivity
public void onShowDetails(int index) { ... }
public void onNoteDeleted() {
// pokud je položka selektovaná odstraní NoteEditFragment
FragmentManager fm = getFragmentManager();
NoteEditFragment edit = (NoteEditFragment) fm.findFragmentByTag("Edit");
if (edit != null) {
FragmentTransaction ft = fm.beginTransaction();
ft.remove(edit);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
}
}
Fragmenty - callbackypublic void onShowDetails(int index) { // implementace callbacku funkce v aktivitě
if (mDualPane) {
// zkontrolujeme a případně vyměníme zobrazený fragment
NoteEditFragment edit = (NoteEditFragment)getFragmentManager().findFragmentById(R.id.edit);
if (details == null || details.getShownIndex() != index) {
// vytvoříme fragment s novým obsahem
edit = NoteEditFragment.newInstance(index);
// v transakci vyměníme fragment
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.edit, edit);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else { // v single-pane modu zobrazíme aktivitu B
Intent intent = new Intent();
intent.setClass(getActivity(), EditActivity.class);
intent.putExtra("index", index);
startActivity(intent);
} }
Fragment
● NoteEditFragment vytvoří view a naplní ho daty
● stará se o editaci poznámky
Fragment – aktivita Bpublic static class EditActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
// v dual-pane (v tomto případě v landscape), aktivitu nepotřebujeme
finish(); return;
}
if (savedInstanceState == null) {
// pokud mame data, vytvoříme edit fragment
NoteEditFragment edit = new NoteEditFragment();
edit.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().add(android.R.id.content, edit).commit();
}
}
// ....
}
DialogFragment
● nahrazuje Dialog● metody Activity.onCreateDialog(),
Activity.onPrepareDialog() jsou deprecated
Fragment Back Stack
● výměny fragmentů v rámci aktivity si můžu ukládat na zásobník fragmentů
● uživatel se pak může vrace tlačítkem zpět
Fragment Back Stack - přidávánívoid addFragmentToStack() {
mStackLevel++;
// vytvoříme nový fragment
Fragment newFragment = CountingFragment.newInstance(mStackLevel);
// přidáme fragment v aktivitě do zásobníku fragmentů
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, newFragment); ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
DialogFragment - vytvoření/** funkce v aktivitě */
void showDialog() {
// DialogFragment.show() přidá fragment do transakce
// odstraníme zobrazený dialog
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("dialog");
if (prev != null) { ft.remove(prev); }
ft.addToBackStack(null);
// vytvoříme a zobrazíme dialog
DialogFragment newFragment = MyDialogFragment.newInstance();
newFragment.show(ft, "dialog");
}
DialogFragmentpublic static class MyDialogFragment extends DialogFragment {
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_dialog, container, false);
// kliknutí na tlačítko
Button button = (Button)v.findViewById(R.id.show);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// nějaká akce
}
});
return v;
} }
AlertDialogpublic static class MyAlertDialogFragment extends DialogFragment {
public static MyAlertDialogFragment newInstance(int title) {
MyAlertDialogFragment frag = new MyAlertDialogFragment();
Bundle args = new Bundle(); args.putInt("title", title);
frag.setArguments(args); return frag;
}
@Override public Dialog onCreateDialog(Bundle savedInstanceState) {
int title = getArguments().getInt("title");
return new AlertDialog.Builder(getActivity()).setIcon(R.drawable.alert_dialog_icon).setTitle(title)
.setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doPositiveClick();
}
}
).setNegativeButton(R.string.alert_dialog_cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
((FragmentAlertDialog)getActivity()).doNegativeClick();
}
}
) .create();
}}
Worker fragment
● fragment nemusí mít UI● může to být neviditelný worker● vytvoříme pomocí funkce
FragmentTransaction.add(Fragment fragment, String tag)● ve fragmentu nebude volán callback
onCreateView()
Activity lifecycle
Fragment lifecycle
Compatibility library
● backportování funkcionality pro API level >= 4● <sdk>/extras/android/support/v4/android-support-v4.jar
● úprava manifestu<uses-sdk android:minSdkVersion="4" android:targetSdkVersion="15" />
● použití správných fragmentůimport android.support.v4.app.Fragment;
ne import android.app.Fragment;
● místo getFragmentManager() použít getSupportFragmentManager()
Compatibility library
● implementuje:● Fragment, FragmentManger, FragmentTransaction, ListFragment,
DialogFragment
● LoaderManager, Loader, AsyncTaskLoader, CursorLoader
● neimplementuje:● ActionBar (zmíním dále), přesto jej částečně podporuje
Rozlišení layoutů
● screen-size buckets– xlarge alespoň 960dp x 720dp
– large alespoň 640dp x 480dp
– normal alespoň 470dp x 320dp
– small alespoň 426dp x 320dp (Android menší displeje nepodporuje)
Rozlišení layoutů
● numerické selektory– width dp – dostupná šířka layoutu
– height dp – dostupná výška layoutu
– smallest width dp – minimální kdykoliv dostupná šířka layoutu
● použítíres/layout/main_activity.xml # telefony
res/layout-sw600dp/main_activity.xml # 7” tablety
res/layout-sw720dp/main_activity.xml # 10” tablety
res/layout/main_activity.xml # Single-pane
res/layout-w600dp/main_activity.xml # Multi-pane pokud je dostatečná šířka (tablet v landscape modu)
Telefonní aplikace bez fragmentů
● systém se sám snaží (trošku) vylepšit vzhled telefonní aplikace na tabletu
● Sceen Compatibility Mode– systém vykreslí aplikaci v rozlišení cca 320x480 a
pak roztáhne na celou obrazkovku
– od API level 13 (Android 3.2)
– systém sám nabídne pokud aplikace nepodporuje large obrazovky
Screen Compatibility Mode
● lze disablovat na starších aplikacích<supports-screens android:xlargeScreens="true" />
● lze enablovat na novějších (používajících API level >= 13)<supports-screens android:compatibleWidthLimitDp="320" />
● zařízení s menší stranou vetší než 320dp navídne compatibility mode
● max hodnota 320
<supports-screens android:largestWidthLimitDp="320" />
● vynutí compatiblity mode (nelze vypnout)
Problémy s fragmenty
● použití MapView je problematické– používá MapActivity
● lze vyřešit (ohackovat)– připojení okna aktivity do layoutu fragmentu
Bez fragmentů
● lze vytvořit různá apk pro telefony a tablety● každé apk nutné zacílit pomocí parametrů v
AndroidManifestu– <supports-screens>
– <compatible-screens>
– ne <uses-sdk>
● do Play Marketu lze nahrát více apk
<supports-screens>
<supports-screens android:smallScreens="false"
android:normalScreens="false"
android:largeScreens="true"
android:xlargeScreens="true"
android:requiresSmallestWidthDp="600" />
● obráceně – povolit malé displeje a zakázat velké
– může působit problém – Google Play to nerespektuje, aplikace bude dostupná i na zařízeních s velkým displejem
– řešení: <compatible-screens>
<compatible-screens>
<compatible-screens>
<!-- malé displeje -->
<screen android:screenSize="small" android:screenDensity="ldpi" />
<screen android:screenSize="small" android:screenDensity="mdpi" />
<screen android:screenSize="small" android:screenDensity="hdpi" />
<screen android:screenSize="small" android:screenDensity="xhdpi" />
<!-- střední displeje -->
<screen android:screenSize="normal" android:screenDensity="ldpi" />
<screen android:screenSize="normal" android:screenDensity="mdpi" />
<screen android:screenSize="normal" android:screenDensity="hdpi" />
<screen android:screenSize="normal" android:screenDensity="xhdpi" />
</compatible-screens>
ActionBar
● příchod s API level 11 (Android 3.0)● asi nejdůležitější designový prvkek
● vizuální komponenta identifikující aplikaci● navigace uživatele v rámci aplikace● větší konzistence navigace napříč aplikacemi● zvýraznění důležitých akcí
ActionBar
1. aplikační ikona
2. přepínač (účty/časové intervaly/...)
3. akční tlačítka
4. prostor pro zbylá tlačítka
ActionBar
ActionBar
1. hlavní ActionBar
2. vrchní panel
3. spodní panel
ActionBar● dříve: akce z ActionBaru se nacházely v
options menu● na tabletu options menu “není”
ActionBar
<uses-sdk android:minSdkVersion="4"
android:targetSdkVersion="11" />● na androidu 3.0 a výš automaticky použije
ActionBar
ActionBar
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.main_activity, menu);
return true;
}
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_save"
android:icon="@drawable/ic_menu_save"
android:title="@string/menu_save"
android:showAsAction="ifRoom|withText" />
</menu>
ActionBar – složitější prvky
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.options, menu);
SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView();
// konfigurace hledání
...
return super.onCreateOptionsMenu(menu);
}
ActionBar - ActionProvider
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/menu_share"
android:title="@string/share"
android:showAsAction="ifRoom"
android:actionProviderClass="com.example.android.SomeActionProvider" />
...
</menu>
● pro složitější akce● mediator pattern
ActionBar
● rozdělení ActionBaru● od API level 14● povolení přidáním atributu
uiOptions="splitActionBarWhenNarrow"
do tagu <activity>
nebo <application>
Compatibility library
● neimplementuje ActionBar● přesto nabízí možnost určit, že položka option
menu má být v ActionBaru, pokud je dostupný● třída MenuCompat
ActionBarSherlock
● 3rd party rozšíření compatibility Library● implementuje ActionBar i pro API level >= 4
Vývoj pro telefony i tablety - shrnutí
● snažit se stavět aktivity na fragmentech– lze mít pouze 1 aktivitu, ve které se mění
fragmenty
● pokud chceme můžeme vytvořit samostatná apk
● používat ActionBar● používat flexibilní layouty
Odkazy
● developer.android.com● android-developers.blogspot.com● source.android.com● stackoverflow.com
● developer.android.com/design/patterns/actionbar.html
● actionbarsherlock.com
Literatura
● Reto Meier: Professional Android 4 Application Development
Google TV
● aneb tabletem to nekončí● tvDpi● harwarové odlišnosti
– pouze landscape
– GPS, telefonní modul, NFC, proximity senzor, ...
– neřešíme výdrž baterie
– ovladač
Dotazy?