Vytváření dynamických seznamů v Android aplikacích je často nezbytné pro prezentaci většího množství dat uživateli. Použití jednotlivých tlačítek v rámci LinearLayoutu pro každý prvek seznamu je nevhodné z hlediska udržitelnosti a výkonu aplikace – museli bychom ručně spravovat data, kontrolovat jejich aktualizaci a omezovat se prostorově na obrazovce. Lepší cestou je využití komponenty ListView, která umožňuje efektivně zobrazovat seznamy položek s využitím datového adaptéru.

ListView je ovládací prvek, který dynamicky vytváří a recykluje zobrazení položek na obrazovce, čímž minimalizuje spotřebu paměti a zajišťuje plynulý běh aplikace, zejména při větších datových sadách. Typicky jej naplníme pomocí adaptéru, který zprostředkuje data mezi zdrojem (např. polem názvů zemí) a vizuální reprezentací jednotlivých řádků.

Příklad jednoduchého naplnění ListView polem názvů zemí ukazuje, že stačí vytvořit pole řetězců a předat ho přes ArrayAdapter, přičemž jako layout pro jednotlivé položky lze použít předdefinovaný jednoduchý vzhled android.R.layout.simple_list_item_1. Tato volba umožní rychle zobrazit seznam bez nutnosti definovat vlastní složité rozvržení.

Kromě jednoduchého zobrazení je možné ListView rozšířit o interaktivitu – například zachytávat kliknutí na jednotlivé položky a zobrazovat informace formou Toast zprávy, což zlepšuje uživatelský zážitek. Dále lze aktivovat režim více výběrů pomocí setChoiceMode(ListView.CHOICE_MODE_MULTIPLE) a změnit vzhled položek na android.R.layout.simple_list_item_checked, čímž uživatel získá možnost vybírat více položek najednou.

Alternativou k ListView je GridView, který umožňuje zobrazení položek v mřížce s více sloupci. Přestože funguje obdobně, neexistuje pro něj speciální základní aktivita (na rozdíl od ListActivity pro ListView), a proto je potřeba jej explicitně vytvořit a nastavit jako obsah obrazovky. GridView je vhodný, pokud je třeba vizuálně zvýraznit položky ve více sloupcích, například při prezentaci obrázků či katalogů.

Důležitým aspektem je i možnost měnit rozvržení uživatelského rozhraní za běhu aplikace přímo z kódu. I když je v Androidu preferováno definovat UI pomocí XML a oddělit jej od aplikační logiky, občas je efektivnější upravovat vlastnosti layoutu programově, například změnit okraje komponent (margins) nebo počet sloupců v GridView. Pro tyto účely slouží třída LayoutParams, kterou lze získat a modifikovat dynamicky.

Celkově ListView a GridView představují základní stavební kameny pro práci s většími seznamy a mřížkami dat v Android aplikacích. Jejich správné použití přispívá k lepší správě paměti, jednodušší aktualizaci dat i příjemnější uživatelské zkušenosti.

Důležité je chápat, že ListView nenačítá všechny položky najednou, což šetří paměť, ale je třeba si také uvědomit, že pro složitější zobrazení položek je možné vytvořit vlastní layouty a vlastní adaptéry (CustomAdapter), které umožní plnou kontrolu nad vzhledem i chováním položek.

Je rovněž nezbytné rozlišovat, kdy je vhodné použít ListView nebo GridView, podle povahy dat a způsobu jejich prezentace. Znalost možností přizpůsobení zobrazení i reakce na uživatelské akce (kliknutí, výběr) umožní vytvořit intuitivní a efektivní uživatelské rozhraní.

Jak vytvořit a upravit uživatelské rozhraní v Androidu: práce s drawable, dynamické přidávání widgetů a vlastní komponenty

V Androidu je práce s uživatelským rozhraním zpravidla založena na XML definicích, které umožňují snadnou deklaraci vzhledu a chování aplikace. Jedním z běžných prvků jsou tzv. drawable resources, které mohou být jednoduché barvy, tvary, nebo i obrázky. Pokud místo pouhého barevného přechodu chceme použít skutečné obrázky jako grafiku v různých stavech komponent, stačí změnit odkazovaný drawable soubor na požadovaný obrázek umístěný v adresáři res/drawable. Pro správné zobrazení na různých zařízeních s odlišnou hustotou obrazových bodů (ldpi, mdpi, hdpi, xhdpi) je možné mít více verzí obrázků ve specifických složkách. Android automaticky vybere nejvhodnější verzi podle hustoty displeje, což zaručuje kvalitní a ostrou grafiku.

V rámci vývoje je ale někdy třeba dynamicky měnit uživatelské rozhraní během běhu aplikace. I když se obvykle UI deklaruje staticky v XML, je možné vytvářet a upravovat widgety přímo v Java kódu. Například přidání widgetu DatePicker do existujícího layoutu lze provést tak, že nejdříve získáme referenci na rodičovský layout pomocí findViewById(), a poté do něj přidáme novou komponentu pomocí metody addView(). Tento přístup je vhodný v případech, kdy je potřeba UI přizpůsobit kontextu nebo uživatelským akcím, které nelze předvídat při statickém návrhu.

Některé aplikace však vyžadují zcela vlastní komponenty, které nejsou k dispozici v základní sadě Android widgetů. V takových případech je možné vytvořit vlastní třídu, která rozšiřuje základní třídu View. To umožňuje definovat jedinečné chování a vzhled prvku. Základem je implementace konstruktoru s kontextem, přepsání metod onMeasure() (pro určení rozměrů komponenty) a onDraw() (pro vykreslení obsahu na obrazovku). V metodě onDraw() získáváme objekt Canvas, který slouží jako plocha, na kterou lze kreslit text, tvary, barvy či obrázky. Tato flexibilita dává programátorovi možnost vytvořit originální vizuální prvky přizpůsobené specifickým požadavkům aplikace.

Při práci s vlastními komponentami je důležité si uvědomit, že absence přepsání metody onMeasure() znamená, že komponenta bude mít výchozí rozměr 100 x 100 pixelů. Pro správnou integraci a případné další použití komponenty v layoutu je vhodné ji také označit ID, aby bylo možné ji v kódu později snadno najít a modifikovat.

Důležitým aspektem je také správné uvolňování a obnovování komponent v rámci cyklu životnosti aktivity – například při volání invalidate() v metodě onDraw() dochází k neustálému překreslování komponenty, což může být žádoucí v případě animací, ale naopak zbytečně zatěžovat výkon, pokud není správně řízeno.

Pro programátory, kteří chtějí vytvářet uživatelská rozhraní zcela dynamicky, bez použití XML, je vhodné vědět, že kompletní layout lze vytvořit přímo v Java kódu. I když to není běžná praxe kvůli přehlednosti a údržbě kódu, v některých situacích to může zjednodušit řešení specifických problémů nebo umožnit rychlou modifikaci UI podle aktuálních potřeb aplikace.

V neposlední řadě je důležité si uvědomit, že správné a efektivní používání zdrojů, dynamické přidávání komponent a tvorba vlastních widgetů představuje základní nástroje profesionálního Android vývojáře. Díky nim je možné aplikace výrazně přizpůsobit a optimalizovat pro různé typy zařízení, uživatelské scénáře a grafické nároky.


Vždy je třeba mít na paměti, že práce s UI v Androidu zahrnuje nejen tvorbu vizuálních prvků, ale i jejich správnou integraci do životního cyklu aplikace, zajištění správné optimalizace výkonu a kompatibility s různými verzemi operačního systému. Kvalitní návrh UI zahrnuje také zvážení uživatelského komfortu a ergonomie, která přímo ovlivňuje spokojenost a loajalitu uživatele.

Jak funguje práce s kamerou v Androidu pomocí starého a nového API?

Práce s kamerou v Androidu se v průběhu let výrazně vyvinula. Původní API android.hardware.Camera, ač dnes již zastaralé a označené jako deprecated, představovalo základní způsob, jak v Android aplikacích pořizovat fotografie. Toto API je relativně jednoduché a zahrnuje dva hlavní kroky: nastavení náhledu (preview) a samotné pořízení snímku. V praxi to znamená, že nejdříve musíme získat povrch (Surface) pro zobrazení náhledu kamery, což se obvykle realizuje pomocí TextureView, a poté reagovat na stisk tlačítka, kdy se spustí pořízení fotografie pomocí metody takePicture().

Ve chvíli, kdy je náhled připravený, aplikace nastaví preview pomocí metody setPreviewTexture() a spustí náhled startPreview(). Po stisku tlačítka kamera vyvolá callback onPictureTaken(), kde se obrazová data uloží do souboru na úložiště zařízení. Důležité je také si uvědomit, že tento proces probíhá na hlavním vlákně, což může vést k zpožděním v uživatelském rozhraní. Proto je doporučeno zpracování náročnějších operací přesunout do background thread, což však původní API nepodporuje přímo a vyžaduje vlastní implementaci.

V rámci práce se starým API je potřeba zvlášť řešit správné uvolnění kamery, zastavení náhledu a správu životního cyklu kamery v metodách SurfaceTextureListener, zejména při zničení nebo změně velikosti povrchu. Kromě toho se musí brát v potaz, že toto API neposkytuje nativní podporu pro přepínání mezi přední a zadní kamerou, orientaci zařízení, nebo pokročilejší nastavení jako expoziční čas, ISO či vyvážení bílé, které je možné jen omezeně ovládat pomocí parametrů.

S příchodem Androidu 5.0 (API 21) bylo zavedeno nové Camera2 API, které nabízí podstatně širší a detailnější kontrolu nad fotoaparátem a jeho funkcemi. Camera2 API je asynchronní a komplexnější, umožňuje například konfiguraci více předvoleb snímků, práci s RAW formáty, lepší správu více kamer a hluboký přístup k nastavení expozice, ostření a dalších parametrů. Vývoj s ním je však náročnější, protože vyžaduje správné řízení stavů kamery pomocí callbacků, synchronizaci a zpracování výsledků na různých vláknech.

Při použití Camera2 API je zásadní pochopit, že povrch pro preview se konfiguruje přes SurfaceTexture získaný z TextureView, stejně jako ve starém API, ale dále je potřeba sestavit CaptureRequest a CameraCaptureSession, které řídí proces snímání. Stav kamery je sledován přes CameraDevice.StateCallback, který zajišťuje inicializaci kamery po jejím otevření. Výběr správné velikosti preview a fotky je prováděn pomocí komparátorů a výběrem z podporovaných velikostí, což umožňuje přizpůsobit parametry podle schopností zařízení.

Důležitým aspektem, který vývojáři často přehlížejí, je správná správa oprávnění (permissions) pro přístup ke kameře a zápisu na úložiště, které jsou povinné a bez nichž aplikace nebude fungovat. Rovněž je nezbytné implementovat správné reakce na změny orientace zařízení a zohlednit tyto změny jak při náhledu, tak při ukládání fotografie, aby výsledné snímky nebyly otočené nebo deformované.

Vývoj kvalitní fotoaplikace vyžaduje také ošetření různých chybových stavů, jako je například nedostupnost kamery, uživatelská neochota udělit oprávnění, či problémy s hardwarem. Praktické aplikace by měly umožnit přepínání mezi kamerami (zadní, přední), nastavení různých režimů snímání a také provádět náročnější zpracování obrázků v samostatných vláknech, aby nedocházelo k zpomalení uživatelského rozhraní.

V neposlední řadě je vhodné sledovat oficiální dokumentaci a nejnovější příklady od Google, které pravidelně aktualizují doporučené postupy a pokrývají nové možnosti, jak s kamerou v Androidu pracovat efektivně a moderně.

Jak správně řešit chyby při připojování k Google API a poskytovat aktualizace o poloze uživatele

Vzhledem k neustálým změnám v Google API se uživatelé vašich aplikací mohou setkat s problémy při připojování k API, pokud mají zastaralé verze souvisejících knihoven. V předchozím příkladu jsme používali Toast pro zobrazení chybového hlášení, ale existuje lepší způsob, jak těmto problémům předejít. K dispozici máme knihovnu GoogleApiAvailability, která umožňuje zobrazit uživatelské rozhraní, jenž usnadňuje řešení problému. Pokračujeme v předchozím příkladu, kdy jsme získávali poslední polohu uživatele, a upravíme kód tak, aby místo základního Toastu nabízel interaktivní dialog pro opravu chyby.

Pokud se připojení k Google API nezdaří, můžeme v rámci zpětné vazby uživateli poskytnout konkrétní instrukce o tom, jak daný problém vyřešit. Na základě ConnectionResult se rozhodneme, zda je problém vyřešitelný (například umožněním přístupu k lokalizačním službám) nebo zda bude potřeba provést jinou akci. Představme si, že místo jednoduchého Toastu použijeme funkci pro zobrazení dialogového okna, které uživateli ukáže přesně, co má dělat.

Jak na to?

Pokud pokračujeme ve vývoji aplikace, která již používá GoogleApiClient pro získání poslední polohy, je potřeba upravit kód v několika klíčových místech.

  1. Přidejte následující proměnné do globálního prostoru vaší aktivity:

java
private final int REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR = 1; boolean mResolvingError;
  1. Vytvořte metodu pro zobrazení chybového dialogu:

java
private void showGoogleAPIErrorDialog(int errorCode) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); Dialog errorDialog = googleApiAvailability.getErrorDialog(this, errorCode, REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR); errorDialog.show(); }
  1. Přepište metodu onActivityResult pro zpracování výsledků opravy chyb:

java
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR) { mResolvingError = false; if (resultCode == RESULT_OK && !mGoogleApiClient.isConnecting() && !mGoogleApiClient.isConnected()) { mGoogleApiClient.connect(); } } }
  1. V metodě onConnectionFailed() nahraďte původní Toast kód novým řešením:

java
if (mResolvingError) {
return; } else if (connectionResult.hasResolution()) { mResolvingError = true; try { connectionResult.startResolutionForResult(MainActivity.this, REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR); } catch (IntentSender.SendIntentException e) { mGoogleApiClient.connect(); } } else { showGoogleAPIErrorDialog(connectionResult.getErrorCode()); }

Tento postup nám umožní místo základního Toastu nabídnout uživateli efektivní způsob, jak opravit chyby připojení. Pokud je problém řešitelný, začne proces opravy, například aktivace lokalizačních služeb. Pokud uživatel nemá možnost tento problém vyřešit sám, dostane podrobnosti o chybě, které mu mohou pomoci provést potřebné kroky.

Pokud aplikace používá fragmenty, můžete použít následující kód pro zobrazení chybového dialogu jako fragmentu:

java
ErrorDialogFragment errorFragment = new ErrorDialogFragment();
Bundle args = new Bundle(); args.putInt("dialog_error", errorCode); errorFragment.setArguments(args); errorFragment.show(getSupportFragmentManager(), "errordialog");

Pokud vaše aplikace potřebuje časté aktualizace polohy, například každých několik sekund, můžete použít metodu requestLocationUpdates() z GoogleApiClient, abyste pravidelně získávali nové údaje o poloze. Tímto způsobem můžete zajistit, že uživatelé obdrží aktuální informace o své poloze, což je nezbytné pro aplikace, které se spoléhají na lokalizaci v reálném čase, jako jsou navigační aplikace, aplikace pro sledování fitness aktivit, nebo jakýkoli jiný typ aplikace, který pracuje s geografickými daty.

Pro implementaci pravidelných aktualizací polohy je nutné vytvořit požadavek na lokalizaci a připojit jej k GoogleApiClient. Zde je ukázka implementace, jak lze nastavit požadavek na pravidelné aktualizace polohy:

  1. Upravte AndroidManifest.xml, přidejte potřebná oprávnění pro přístup k poloze:

xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. V souboru build.gradle přidejte závislost na službu Google Play:

gradle
compile 'com.google.android.gms:play-services:8.4.0'
  1. Nastavte layout pro zobrazení dat o poloze pomocí TextView:

xml
<TextView
android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" />
  1. Ve třídě MainActivity.java vytvořte proměnné pro API klienta a posluchače lokalizace:

java
GoogleApiClient mGoogleApiClient; LocationRequest mLocationRequest; TextView mTextView;
  1. Nastavte posluchač pro změny polohy:

java
LocationListener mLocationListener = new LocationListener() {
@Override public void onLocationChanged(Location location) { if (location != null) { mTextView.setText( DateFormat.getTimeInstance().format(location.getTime()) + "\n" + "Latitude=" + location.getLatitude() + "\n" + "Longitude=" + location.getLongitude()); } } };
  1. Připojte klienta a nastavte požadavek na lokaci:

java
protected synchronized void setupLocationRequest() {
mLocationRequest = new LocationRequest(); mLocationRequest.setInterval(10000); mLocationRequest.setFastestInterval(10000); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(mConnectionCallbacks) .addOnConnectionFailedListener(mOnConnectionFailedListener) .addApi(LocationServices.API) .build(); mGoogleApiClient.connect(); }
  1. Po připojení k API klientovi, v metodě onConnected(), zavolejte requestLocationUpdates(), aby bylo možné pravidelně dostávat aktualizace polohy.

Tato metoda je vhodná pro aplikace, které vyžadují pravidelnou aktualizaci údajů o poloze. Jakmile bude aplikace připojena k Google API, začne přijímat aktualizace polohy a může je podle potřeby zpracovávat.