Vytváření aplikací pro Android pomocí fragmentů je dnes jedním z nejběžnějších přístupů, který umožňuje vytvářet dynamické a přizpůsobitelné uživatelské rozhraní. Fragmenty umožňují oddělení různých částí aplikace do modulárních komponent, které lze opakovaně využívat a spravovat. V tomto článku se podíváme na implementaci komunikace mezi fragmenty v kontextu Master/Detail vzoru a jak správně předávat data mezi těmito fragmenty.

V začátku procesu je nutné vytvořit fragmenty, které budou obsluhovat různé části aplikace. Představme si, že máme dvě hlavní části - "MasterFragment", který bude obsahovat seznam položek (například názvy zemí), a "DetailFragment", který bude zobrazovat detaily o zvolené položce. Tento vzor je velmi efektivní při tvorbě aplikací, kde je potřeba mít oddělené oblasti pro výběr a zobrazení podrobností.

V prvním kroku je důležité správně inicializovat oba fragmenty v aktivitě. Pokud aplikace běží na zařízení s více panely (například v režimu na šířku), oba fragmenty (MasterFragment a DetailFragment) budou zobrazeny současně. V opačném případě (například na mobilních telefonech v režimu na výšku) bude DetailFragment zobrazen pouze po výběru položky v seznamu.

Klíčem k efektivní komunikaci mezi těmito fragmenty je správná implementace posluchačů (listeners). V MasterFragmentu definujeme posluchače pro výběr položky z seznamu. Tento posluchač (OnMasterSelectedListener) zajistí, že jakmile uživatel vybere položku, bude zvolené jméno země předáno DetailFragmentu.

java
masterFragment.setOnMasterSelectedListener(new MasterFragment.OnMasterSelectedListener() { @Override
public void onItemSelected(String countryName) {
sendCountryName(countryName); } });

Metoda sendCountryName() se stará o to, aby byla data (v tomto případě název země) předána do DetailFragmentu. V závislosti na tom, zda máme jednu nebo dvě zobrazené obrazovky, se použije různý přístup. Pokud je aplikace v režimu na šířku, kde máme dva panely, data jsou předávána přímo do již zobrazeného DetailFragmentu. Naopak v režimu na výšku (jediný panel) je nutné vytvořit nový DetailFragment a nahradit stávající obsah pomocí fragment transakce.

java
private void sendCountryName(String countryName) { DetailFragment detailFragment; if (dualPane) { // Dvojpanelový layout detailFragment = (DetailFragment) getSupportFragmentManager().findFragmentById(R.id.frameLayoutDetail); detailFragment.showSelectedCountry(countryName); } else { // Jednopanelový layout detailFragment = new DetailFragment();
Bundle bundle = new Bundle();
bundle.putString(DetailFragment.KEY_COUNTRY_NAME, countryName); detailFragment.setArguments(bundle);
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); fragmentTransaction.replace(R.id.frameLayout, detailFragment); fragmentTransaction.addToBackStack(null); fragmentTransaction.commit(); } }

Tento proces je velmi podobný pro oba typy layoutu – pro oba se používá metoda showSelectedCountry(), která zobrazuje informace o zvolené zemi. Pokud je aplikace v režimu na šířku, metoda je volána přímo na existujícím DetailFragmentu. Naopak v režimu na výšku je vytvořen nový DetailFragment a zobrazen na hlavní obrazovce.

Dalším důležitým aspektem je zachování správného chování při změně orientace obrazovky. Při změně orientace (např. z portrétu na krajinu) je nutné správně uchovat stav fragmentů, aby uživatel nepřišel o data, která již vybral. Tento proces je zajištěn správnou implementací fragment transakcí a použitím addToBackStack(), které umožní návrat do předchozího fragmentu pomocí tlačítka zpět.

Důležité je také zajistit, že když je aktivita nastavena na použití posluchače (listener), tento posluchač je skutečně nastavený před tím, než dojde k pokusu o komunikaci mezi fragmenty. Pokud není posluchač nastaven, může dojít k výjimce, která způsobí pád aplikace. Proto je doporučeno před každým voláním posluchače prověřit, zda je skutečně inicializován:

java
if (mOnMasterSelectedListener != null) {
mOnMasterSelectedListener.onItemSelected(countryName); }

Komunikace mezi fragmenty je tedy zajištěna prostřednictvím posluchačů a správného používání fragment transakcí. Tento přístup je velmi flexibilní a umožňuje vytvářet aplikace, které se přizpůsobí různým typům zařízení a rozlišení obrazovky.

V případě potřeby lze tento systém rozšířit o více fragmentů a složitější logiku pro komunikaci mezi nimi. Také je důležité mít na paměti, že každý fragment by měl být co nejvíce nezávislý na ostatních, což zjednodušuje údržbu aplikace a její testování.

Jak spravovat blesk a notifikace v aplikacích pro Android

Při vývoji aplikací pro Android se často setkáváme s potřebou přístupů k různým funkcím zařízení, jako je například ovládání blesku fotoaparátu nebo zasílání notifikací. Tato kapitola se zaměřuje na implementaci těchto funkcí, včetně ovládání blesku a tvorby notifikací pro uživatele, kteří potřebují rychlý přístup k těmto funkcím.

Nejprve je nutné provést určité úpravy v souboru AndroidManifest.xml, aby byla aplikace schopna využívat kameru a blesk. Poté přistoupíme k samotné úpravě rozhraní a logiky aplikace.

Začněte úpravou layoutu souboru activity_main.xml, kde bude místo základního tlačítka pro zapnutí/vypnutí blesku implementováno tlačítko s možností přepínání (ToggleButton). Tento prvek nám umožní ovládat stav blesku pomocí jednoduchého stisku tlačítka. V kódu ActivityMain.java je třeba definovat několik globálních proměnných pro správu kamery a blesku.

java
private static final String ACTION_STOP="STOP"; private CameraManager mCameraManager; private String mCameraId=null; private ToggleButton mButtonLight;

Dále nastavíme onCreate(), aby se inicializovala kamera a tlačítko pro blesk. Pokud je blesk dostupný, tlačítko se aktivuje; pokud ne, deaktivuje se.

java
mButtonLight = (ToggleButton)findViewById(R.id.buttonLight);
mCameraManager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE); mCameraId = getCameraId(); if (mCameraId == null) { mButtonLight.setEnabled(false); } else { mButtonLight.setEnabled(true); }

Pokud jde o zajištění správné funkce blesku, bude potřeba přidat metody pro kontrolu a ovládání jeho stavu. Pokud uživatel aktivuje tlačítko pro zapnutí blesku, aplikace vytvoří notifikaci, která umožní rychlé vypnutí blesku přímo z notifikace. Tento krok zahrnuje vytvoření metody pro odeslání notifikace s tlačítkem pro vypnutí blesku.

java
private void showNotification() {
Intent activityIntent = new Intent(this, MainActivity.class); activityIntent.setAction(ACTION_STOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, activityIntent, 0); final Builder notificationBuilder = new Builder(this) .setContentTitle("Flashlight") .setContentText("Press to turn off the flashlight") .setSmallIcon(R.mipmap.ic_launcher) .setContentIntent(pendingIntent) .setVibrate(new long[]{DEFAULT_VIBRATE}) .setPriority(PRIORITY_MAX);
NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(
0, notificationBuilder.build()); }

Tato notifikace bude obsahovat akci pro vypnutí blesku, což umožňuje uživateli jednoduchý přístup k této funkci bez nutnosti otevření samotné aplikace. Jakmile uživatel na notifikaci klikne, blesk se vypne. V souvisejícím kódu musíme zajistit, aby při otevření aplikace došlo k detekci akce a vypnutí blesku, pokud je to požadováno.

java
@Override
protected void onNewIntent(Intent intent) { super.onNewIntent(intent); if (ACTION_STOP.equals(intent.getAction())) { setFlashlight(false); } }

V neposlední řadě musíme přidat metody pro samotné ovládání blesku. Tlačítko ToggleButton bude přepínat stav blesku mezi zapnutým a vypnutým. Při aktivaci blesku se rovněž zobrazí notifikace informující uživatele o možnosti vypnutí.

java
public void clickLight(View view) { setFlashlight(mButtonLight.isChecked()); if (mButtonLight.isChecked()) { showNotification(); } }
private void setFlashlight(boolean enabled) {
mButtonLight.setChecked(enabled);
try { mCameraManager.setTorchMode(mCameraId, enabled); } catch (CameraAccessException e) { e.printStackTrace(); } }

Pokud by aplikace měla běžet na reálném zařízení, je nutné, aby mělo minimálně Android 6.0 a k dispozici zpětný fotoaparát s bleskem. Tento krok je důležitý, protože na starších verzích systému Android nebo zařízeních bez blesku nebude tento kód fungovat správně.

Je důležité také poznamenat, že pro správnou funkci notifikací musí být nastaveno vysoké prioritní zpracování, které zajistí, že notifikace budou uživateli zobrazeny jako "Heads-Up" notifikace, což znamená, že se zobrazí přímo na obrazovce i v případě, že uživatel používá jinou aplikaci.

Kromě toho je třeba mít na paměti, že systémové omezení v Androidu mohou zabránit zobrazení některých typů notifikací, pokud jsou nastaveny nesprávně, zejména pokud jde o nastavení vibrací nebo zvuku.

Jak přepínat mezi aktivitami a předávat data v Android aplikacích

Vytvoření více aktivit je základem většiny aplikací pro Android. Přepínání mezi nimi a předávání dat je klíčové pro interakci mezi uživatelským rozhraním a logikou aplikace. Tento proces nejenže usnadňuje navigaci, ale také umožňuje efektivní správu informací mezi jednotlivými částmi aplikace. V této kapitole se zaměříme na přepínání mezi aktivitami, předávání dat a vracení výsledků z jedné aktivity do druhé.

Začneme vytvořením druhé aktivity. Otevřete projekt ActivitySwitcher a v Android Studiu přejděte na Soubor | Nový | Aktivita | Prázdná aktivita. V dialogovém okně pro přizpůsobení aktivity nechte výchozí název aktivity, který je "Main2Activity", nebo ho změňte na "SecondActivity". Poté otevřete soubor MainActivity.java a přidejte následující funkci:

java
public void onClickSwitchActivity(View view) {
Intent intent = new Intent(this, SecondActivity.class); startActivity(intent); }

Tato metoda je základem pro přepnutí mezi aktivitami. Pomocí objektu Intent specifikujeme, že chceme spustit SecondActivity. Nyní otevřete soubor activity_main.xml, který se nachází ve složce \res\layout, a přidejte XML kód pro vytvoření tlačítka, které tuto funkci aktivuje.

Až budete mít hotovo, spusťte aplikaci a zkontrolujte, že druhá aktivita se správně zobrazí. Abychom aplikaci ještě vylepšili, přidáme do druhé aktivity tlačítko pro její zavření a návrat na první obrazovku. Otevřete soubor SecondActivity.java a přidejte následující metodu:

java
public void onClickClose(View view) {
finish(); }

Tato metoda jednoduše uzavře aktuální aktivitu a vrátí nás zpět na předchozí obrazovku díky zásobníku aktivit. Poté přidejte tlačítko pro zavření do layoutu druhé aktivity ve souboru activity_second.xml.

Pokud nyní spustíte aplikaci, uvidíte v obou aktivitách funkční tlačítka. Druhá aktivita bude uzavřena po kliknutí na tlačítko a uživatel bude vrácen na první obrazovku. Tento proces přepínání mezi aktivitami se stal základním vzorem pro většinu aplikací.

V případě, že bychom vytvořili aktivity ručně, museli bychom je přidat do manifestu aplikace. Využití Android Studia však znamená, že většinu nastavení automaticky za nás udělá, včetně potřebné úpravy souboru AndroidManifest.xml. Tento soubor nyní obsahuje deklaraci pro naši novou aktivitu.

Vraťme se nyní k principu předávání dat mezi aktivitami. K tomu využíváme objekt Intent, který funguje jako komunikační kanál mezi jednotlivými komponentami aplikace. Tento objekt umožňuje nejen přepínání mezi aktivitami, ale také předávání informací mezi nimi.

Pokračujeme v projektu, který nyní nazveme SendData, a vybudujeme ho na základě předchozích kroků. V activity_main.xml přidáme prvek EditText, který nám umožní zadat text, jenž následně předáme do druhé aktivity. Následně upravíme metodu onClickSwitchActivity() v souboru MainActivity.java takto:

java
public void onClickSwitchActivity(View view) {
EditText editText = (EditText)findViewById(R.id.editTextData); String text = editText.getText().toString(); Intent intent = new Intent(this, SecondActivity.class); intent.putExtra(Intent.EXTRA_TEXT, text); startActivity(intent); }

Tímto způsobem přidáme do Intent extra data, konkrétně text zadaný uživatelem. Tato data jsou předána do druhé aktivity, kde je zobrazeno v textovém poli. V souboru SecondActivity.java upravíme metodu onCreate():

java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); setContentView(R.layout.activity_second); TextView textView = (TextView)findViewById(R.id.textViewText); if (getIntent() != null && getIntent().hasExtra(Intent.EXTRA_TEXT)) { textView.setText(getIntent().getStringExtra(Intent.EXTRA_TEXT)); } }

Tento kód zajistí, že text, který uživatel zadal v první aktivitě, se objeví v TextView ve druhé aktivitě.

Výsledkem je jednoduchý mechanismus předávání dat mezi aktivitami, kde se používá metoda putExtra() pro vložení dat do Intent a getStringExtra() pro jejich načtení v cílové aktivitě. Tato metoda je však jen začátkem, protože Intent objekt podporuje i předávání dalších datových typů, nejen textových řetězců.

Tento proces nám umožňuje velmi flexibilní práci s daty mezi aktivitami a může být využit nejen pro text, ale i pro složitější datové struktury. Při použití metody putExtra() můžete předávat jakýkoli typ dat, který podporuje Android, včetně celých čísel, seznamů a objektů.

Kromě přepínání mezi aktivitami a předávání dat je důležité také porozumět, jak správně využívat životní cyklus aktivit v Androidu. Když například zavřeme aktivitu pomocí metody finish(), systém se postará o správu zásobníku aktivit, což umožňuje hladký návrat k předchozím aktivitám. Tento mech

Jak vytvořit vlastní GLSurfaceView pro OpenGL v Androidu

Pro práci s OpenGL v Androidu je nezbytné mít správně nakonfigurovaný GLSurfaceView, který poskytuje plátno pro vykreslování grafiky. Tento proces se skládá z několika kroků, od vytvoření vlastní třídy GLSurfaceView až po definování třídy renderu a samotné vykreslování na obrazovku.

Prvním krokem je vytvoření vlastní třídy, která rozšiřuje GLSurfaceView. Tento krok je zásadní, protože GLSurfaceView je zodpovědné za vytváření a správu vykreslovacího kontextu pro OpenGL. Následující ukázka ukazuje, jak vytvořit tuto třídu v Javě:

java
class CustomGLSurfaceView extends GLSurfaceView {
private final GLRenderer mGLRenderer; public CustomGLSurfaceView(Context context){ super(context); setEGLContextClientVersion(2); // Nastavení verze OpenGL ES mGLRenderer = new GLRenderer(); // Vytvoření renderu setRenderer(mGLRenderer); // Přiřazení rendereru k GLSurfaceView } }

Tato třída inicializuje renderer, který se postará o samotné vykreslování objektů. Ve skutečnosti vykreslování probíhá v renderovacím objektu, který musí implementovat tři základní metody:

  1. onSurfaceCreated(): Tato metoda se volá při vytvoření povrchu. V ní nastavujeme základní nastavení, jako například barvu pozadí.

  2. onDrawFrame(): V této metodě se vykreslují objekty na obrazovku.

  3. onSurfaceChanged(): Tato metoda je volána při změně velikosti povrchu a je vhodná pro přizpůsobení vykreslovacího prostoru.

Příklad implementace rendereru:

java
class GLRenderer implements GLSurfaceView.Renderer { public void onSurfaceCreated(GL10 unused, EGLConfig config) { GLES20.glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // Nastavení barvy pozadí } public void onDrawFrame(GL10 unused) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); // Vyčištění obrazovky } public void onSurfaceChanged(GL10 unused, int width, int height) { GLES20.glViewport(0, 0, width, height); // Nastavení velikosti výstupního okna } }

Tato část kódu se zaměřuje na přípravu prostředí pro OpenGL, ale samotné vykreslování geometrických tvarů přichází až v další fázi.

Definice tvaru a vykreslování objektů v OpenGL

Abychom začali vykreslovat objekty v OpenGL, musíme nejprve definovat tvar, který chceme zobrazit. Jedním z nejzákladnějších tvarů je trojúhelník. V OpenGL se objekty obvykle skládají z několika trojúhelníků, které definují povrch objektu. Důležité je také pochopit, jak funguje souřadnicový systém OpenGL, protože se liší od souřadnicového systému Android Canvas.

OpenGL používá souřadnicový systém, kde střed obrazovky je (0,0,0), a vrcholy objektu jsou definovány v trojrozměrném prostoru. Pro tento příklad vytvoříme třídu pro trojúhelník a implementujeme vertex shader, fragment shader a program, který propojí oba shadery.

Zde je ukázka kódu pro vytvoření třídy trojúhelníku:

java
class Triangle { private final String vertexShaderCode = "attribute vec4 vPosition;" + "void main() {" + " gl_Position = vPosition;" + "}"; private final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; final int COORDS_PER_VERTEX = 3; float triangleCoords[] = { 0.0f, 0.66f, 0.0f, // Top -0.5f, -0.33f, 0.0f, // Bottom left 0.5f, -0.33f, 0.0f // Bottom right }; float color[] = {0.63f, 0.76f, 0.22f, 1.0f}; // Barva trojúhelníku private final int mProgram; private FloatBuffer vertexBuffer; private int mPositionHandle; private int mColorHandle; private final int vertexCount = triangleCoords.length / COORDS_PER_VERTEX; private final int vertexStride = COORDS_PER_VERTEX * 4; public Triangle() { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); mProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(mProgram, vertexShader); GLES20.glAttachShader(mProgram, fragmentShader); GLES20.glLinkProgram(mProgram); ByteBuffer bb = ByteBuffer.allocateDirect(triangleCoords.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(triangleCoords); vertexBuffer.position(0); } public int loadShader(int type, String shaderCode) { int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } public void draw() { GLES20.glUseProgram(mProgram); mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition"); GLES20.glEnableVertexAttribArray(mPositionHandle); GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer); mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor"); GLES20.glUniform4fv(mColorHandle, 1, color, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertexCount); GLES20.glDisableVertexAttribArray(mPositionHandle); } }

V této fázi máme připravený trojúhelník, který se bude vykreslovat v OpenGL. Je potřeba mít na paměti, že vertex a fragment shadery jsou napsány v OpenGL Shading Language (GLSL) a musí být kompilovány a připojeny k OpenGL programu. Vertex shader definuje pozici vrcholů, zatímco fragment shader určuje barvu objektu.

Integrace do hlavní aktivity

Abychom tento trojúhelník vykreslili na obrazovce, musíme ho zavolat v metodě onDrawFrame() v hlavní aktivitě:

java
class GLRenderer implements GLSurfaceView.Renderer { private Triangle mTriangle; public void onSurfaceCreated(GL10 unused, EGLConfig config) { mTriangle = new Triangle(); // Inicializace trojúhelníku } public void onDrawFrame(GL10 unused) { mTriangle.draw(); // Vykreslení trojúhelníku } }

Tento krok je klíčový, protože skutečné vykreslování se provádí ve funkci draw(), kterou voláme v každém snímku.

Pokud vše proběhne správně, na obrazovce se objeví trojúhelník s požadovanou barvou, který je vykreslován pomocí OpenGL.