Az Android fejlesztés egyik alapkoncepciója, hogy az alkalmazás felhasználói felületét XML fájlok segítségével definiáljuk, amelyeket futásidőben Java vagy Kotlin kód segítségével módosíthatunk. Ugyanakkor előfordulnak olyan esetek, amikor a teljes UI-t szükséges kódból létrehozni, például dinamikus vagy testreszabott elrendezések esetén. Ilyen esetekben a layout fájlok háttérbe szorulnak, és a teljes nézetstruktúra programozottan épül fel.

Például egy DatePicker komponens hozzáadása egy meglévő RelativeLayout-hoz egyszerűen megvalósítható az onCreate() metóduson belül. Ehhez először hozzá kell adnunk egy azonosítót az XML-ben definiált layout elemhez, majd a Java kódban a findViewById() segítségével hivatkozunk rá. A komponens létrehozása után a layout.addView() metódus segítségével dinamikusan hozzáadható a megjelenítéshez. Az ilyen megközelítés lehetőséget biztosít arra, hogy a felület a futásidőbeli logikához igazodjon.

Egy lépéssel továbbmenve teljesen mellőzhetjük az XML fájlokat, és akár az egész layoutot kódból hozhatjuk létre. Ilyenkor a setContentView() metódus paramétereként közvetlenül egy új ViewGroup példányt – például RelativeLayout – adunk meg, amelyhez dinamikusan adhatunk gyermeknézeteket. Ez a megoldás különösen akkor hasznos, ha a felhasználói felület komponensei teljes mértékben az aktuális alkalmazásállapottól vagy adatoktól függenek.

A nézetek programozott létrehozása során kiemelten fontos, hogy minden nézet azonosítható legyen. Ha később hivatkozni akarunk egy nézetre, akkor vagy megőrizzük a példány referenciáját, vagy egyedi azonosítót rendelünk hozzá a setId(View.generateViewId()) segítségével.

Az Android SDK azonban nem mindig nyújt megfelelő eszköztárat az egyedi igények kiszolgálásához. Ilyenkor a legjobb megoldás egy saját komponens létrehozása a View osztályból származtatva. Egy egyedi komponens akkor is indokolt, ha egyedi megjelenítést, rajzolást vagy viselkedést szeretnénk megvalósítani, amelyre nincs beépített eszköz.

Az egyedi komponens létrehozásának első lépése egy új osztály definiálása, amely öröklődik a View osztályból. A konstruktorban be kell állítani a szükséges tulajdonságokat – például Paint objektumokat – a rajzoláshoz. A legfontosabb metódus az onDraw(), ahol a rendszer egy Canvas objektumot ad át, amelyen

Hogyan működik az Android kamera API? – A régi és új Camera2 API összehasonlítása

Az Android fejlesztésében a kamera használata alapvető funkció, amelyet sok alkalmazásban alkalmaznak, különböző céllal. Az Android kamerás API-jai, különösen az android.hardware.Camera és az android.hardware.camera2 API, számos változást hoztak az alkalmazások fejlesztésében. Ebben a fejezetben az oldott és új Camera API-t, azok használatát és az alkalmazások működését vizsgáljuk meg. A példában bemutatott megoldások segítenek a fejlesztőknek a kamera működésének megértésében és a helyes használatában.

Amikor kamerát használunk, az egyik legfontosabb dolog, amit meg kell értenünk, hogy két fő lépést kell megtennünk: először beállítjuk a kamerát és megjelenítjük a képet, majd a második lépés, hogy az adott pillanatban képet készítünk. A következő kódot elemezve lépésről lépésre látni fogjuk, hogyan működik ez a folyamat.

A kód alapján az első teendőnk az, hogy beállítjuk a TextureView-ot, amely a képernyőn a kamera képet fogja megjeleníteni. Ehhez a következő kódrészletet használjuk:

java
mTextureView = (TextureView)findViewById(R.id.textureView);
mTextureView.setSurfaceTextureListener(this);

A setSurfaceTextureListener() metódus segítségével a SurfaceTextureListener interface-t rendeljük hozzá a TextureView-hoz, amely meghatározza, mikor lesz a felület elérhető és mikor érhető el a kamera előnézeti képe. Az onSurfaceTextureAvailable() callback-en keresztül beállítjuk a kamera előnézeti felületét:

java
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mCamera = Camera.open(); if (mCamera != null) { try { mCamera.setPreviewTexture(surface); mCamera.startPreview(); } catch (IOException e) { e.printStackTrace(); } } }

Miután az előnézeti felület készen áll, a kamerát elindítjuk a mCamera.startPreview() metódussal. Ha a kamera nem érhető el, akkor az IOException hibákra kell felkészülni. Ezt követően a felhasználó kattintása során egy fényképet készíthetünk. A fénykép készítéséhez a takePicture() metódust alkalmazzuk:

java
public void takePicture(View view) {
if (mCamera != null) { mCamera.takePicture(null, null, pictureCallback); } }

Ez a kódrészlet meghívja a takePicture() metódust, amely a Camera.PictureCallback interfész segítségével elkészíti a fényképet, és az onPictureTaken() callback-ben az eredményt tároljuk el:

java
Camera.PictureCallback() { @Override
public void onPictureTaken(byte[] data, Camera camera) {
try { String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(System.currentTimeMillis()); String fileName = "PHOTO_" + timeStamp + ".jpg"; File pictureFile = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), fileName);
FileOutputStream fileOutputStream = new FileOutputStream(pictureFile.getPath());
fileOutputStream.write(data); fileOutputStream.close(); Toast.makeText(MainActivity.
this, "Picture Taken", Toast.LENGTH_SHORT).show(); } catch (Exception e) { e.printStackTrace(); } } };

Ezen kód segítségével elmenthetjük a fényképet a készülék tárolójára. A fájl neve tartalmazza a pontos időbélyeget is, így könnyen nyomon követhetjük, mikor készült a kép.

Fontos megjegyezni, hogy az android.hardware.Camera API már elavult, és a Google ajánlása szerint a Camera2 API használata preferált. A Camera2 API aszinkron működése és bonyolultsága miatt több kódot igényel, de számos új funkcióval bővíti a kamerás alkalmazások lehetőségeit.

A Camera2 API bevezetése az Android 5.0 (API 21) verzióval kezdődött, és azóta jelentős változások történtek a kamera vezérlésében. A régi Camera API-nál a fénykép készítése egyszerűbb volt, de nem adta meg a szükséges részletes irányítást a fejlesztők számára. A Camera2 API-t már sokkal kifinomultabban kell használni. A példában a CameraDevice és a CaptureRequest.Builder osztályokat használjuk a képek készítésére és a kamerabeállítások módosítására.

A következő kód segít elindítani a Camera2 API-t egy egyszerű alkalmazásban:

java
private CameraDevice mCameraDevice = null;
private CaptureRequest.Builder mCaptureRequestBuilder = null;
private CameraCaptureSession mCameraCaptureSession = null;
private TextureView mTextureView = null;
private Size mPreviewSize = null;

A CameraDevice.StateCallback interfész segít nyomon követni, hogy mikor nyílik meg a kamera eszköz és mikor áll le. A onOpened() callback-ben történik meg a kamera eszköz elindítása, és a kamera képernyőre történő irányítása:

java
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override
public void onOpened(CameraDevice camera) {
mCameraDevice = camera;
SurfaceTexture texture = mTextureView.getSurfaceTexture(); if (texture == null) { return; } texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); Surface surface = new Surface(texture); try { mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mCaptureRequestBuilder.addTarget(surface); mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() { @Override
public void onConfigured(CameraCaptureSession session) {
try { mCameraCaptureSession = session; mCaptureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); mCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { Log.e("Camera2", "Configuration failed"); } }, null); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override
public void onDisconnected(CameraDevice camera) {
camera.close(); }
@Override public void onError(CameraDevice camera, int error) { camera.close(); } };

Ez a kódrészlet elindítja a kamera előnézeti módot és a folyamatos képkészítést, majd lehetővé teszi a fénykép készítését a CaptureRequest objektummal.

Mindezeket figyelembe véve, fontos, hogy a kamera működtetése az eszköztől függően eltérhet. A különböző Android verziók és eszközök támogatása, valamint az API kompatibilitás mind olyan tényezők, amiket figyelembe kell venni. Az alkalmazás fejlesztésénél célszerű tisztában lenni a különböző eszközök képességeivel és a szükséges jogosultságokkal is.

Hogyan működik az Android kamera kezelése és képrögzítés?

Az Android operációs rendszer a fejlett kamerakezelést és képrögzítést lehetővé tevő API-kkal rendelkezik. A rendszer lehetővé teszi, hogy a fejlesztők teljes mértékben kontrollálják a kamerát, például a képminőséget, az expozíciót, a fókuszt, és sok más beállítást. Ebben a fejezetben bemutatjuk, hogyan lehet az Android kameráját programozottan kezelni, a kameraindítástól a képek mentéséig.

A kamerakezelés megkezdése előtt szükséges egy TextureView komponens hozzáadása az alkalmazás felületéhez. A TextureView lehetővé teszi, hogy az alkalmazás valós időben megjelenítse a kamera előnézeti képét. Ehhez be kell állítanunk egy SurfaceTextureListener-t, amely biztosítja, hogy az előnézeti felület megfelelően reagáljon az eseményekre. Az onSurfaceTextureAvailable() metódusban meghívjuk a openCamera() függvényt, amely elindítja a kamerát.

Miután a kamera elindult, szükség van egy CameraCaptureSession beállítására is, amely az előnézeti kép folyamatos frissítését biztosítja. Ehhez egy CameraCaptureSession.StateCallback objektumot adunk hozzá, amely lehetővé teszi, hogy az alkalmazás az előnézetet azonnal megjelenítse, miután a kamera sikeresen konfigurálódott. Az onConfigured() metódus hívásával elindul a kameraképek előnézeti megjelenítése.

A képrögzítéshez a takePicture() metódus szükséges, amely rögzíti a fényképet és menti azt a tárolóra. Az alkalmazásban először létrehozunk egy ImageReader objektumot, amely a legnagyobb felbontású képek olvasására alkalmas. A takePicture() függvény az elkészült fényképet JPEG formátumban menti a készülék galériájába. Ezt követően a felhasználó értesítést kap a sikeres fénykép készítéséről.

A képek rögzítése előtt a kamera paramétereit is testre szabhatjuk. Ehhez meg kell szerezni a kamera tulajdonságait, mint például a támogatott felbontásokat és a különböző konfigurációkat. A CameraCharacteristics segítségével lekérhetjük a kamera tulajdonságait, és azokat felhasználhatjuk a fényképek optimális minőségének biztosítására.

A fénykép mentését követően a rendszer automatikusan visszaállítja az előnézetet, így a felhasználó folytathatja a fényképezést vagy a videózást anélkül, hogy az alkalmazás leállna. Mindez lehetővé teszi, hogy folyamatosan rögzítsünk képeket, anélkül hogy megszakítanánk a felhasználói élményt.

A megfelelő kamera-kezelés érdekében elengedhetetlen, hogy az alkalmazás képes legyen kezelni az életciklus eseményeit is. A onPause() és onResume() metódusok biztosítják, hogy a kamera megfelelően bezáruljon, amikor a felhasználó elhagyja az alkalmazást, és újra elinduljon, amikor visszatér.

A kamera különböző beállításai, mint a fényképezés automatikus expozícióval, a fókusz automatikus beállítása, és a különböző fényképi módok mind fontos szerepet játszanak a kívánt képminőség elérésében. A fejlesztőknek figyelniük kell arra, hogy az alkalmazás különböző körülmények között is stabilan működjön, például gyenge fényviszonyok között.

Ezen kívül, ha a képrögzítés minőségét javítani szeretnénk, érdemes az alkalmazásban támogatni a manuális beállításokat is, például a záridőt, az érzékenységet (ISO), és a fehéregyensúlyt. A kameramodulok lehetőséget adnak arra, hogy a fejlesztők ezeket a paramétereket dinamikusan módosíthassák, hogy a felhasználó a legjobb eredményt érhesse el mindenféle fénykörnyezetben.

A fényképezés során felmerülő hibák kezelésére is szükség van. A megfelelő hibaellenőrzés, mint például a CameraAccessException és SecurityException kezelése biztosítja, hogy az alkalmazás ne álljon le váratlan módon, és a felhasználó mindig képes legyen képeket készíteni, még akkor is, ha valamilyen rendszerhiba történik.

A kamera fejlesztése tehát komplex feladat, amely magában foglalja a felhasználói élmény javítását, a képek minőségének optimalizálását és az alkalmazás stabilitásának biztosítását. Az Android rendszer különböző API-kat biztosít ezen funkciók megvalósításához, de a sikeres alkalmazás fejlesztéséhez szükséges a megfelelő programozói tudás és tapasztalat.

Hogyan kezeljük a Google API-k hibáit és frissítsük a helyadatokat Android alkalmazásban?

A Google API-k folyamatosan változó természetének köszönhetően előfordulhat, hogy a felhasználók nem tudják használni alkalmazásukat, mivel az alkalmazásukban lévő fájlok elavultak. Az előző példában csak egy egyszerű üzenetet, a Toast-ot mutattunk, de ennél többet is tehetünk. A GoogleApiAvailability könyvtár használatával egy párbeszédablakot jeleníthetünk meg, amely segíti a felhasználót a probléma megoldásában. Az alábbiakban bemutatjuk, hogyan módosíthatjuk a kódot, hogy a kapcsolat hibáját kezeljük és a felhasználónak hasznos információkat nyújtsunk a probléma megoldásához.

Mivel folytatjuk az előző példát, amelyben a felhasználó helyzetét kérdeztük le, a frissítés során csak azokat a lépéseket ismertetjük, amelyek szükségesek a kód javításához. Az ActivityMain.java fájlban az alábbiakat kell tennünk:

Először is, adjunk hozzá két globális változót a kódhoz:

java
private final int REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR = 1; boolean mResolvingError;

Ezután hozzunk létre egy metódust, amely megjeleníti a Google API hibáját:

java
private void showGoogleAPIErrorDialog(int errorCode) {
GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance(); Dialog errorDialog = googleApiAvailability.getErrorDialog( this, errorCode, REQUEST_RESOLVE_GOOGLE_CLIENT_ERROR); errorDialog.show(); }

A következő lépésben módosítjuk az onActivityResult() metódust:

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

A következő lépésben az onConnectionFailed() metódusban cseréljük ki a Toast megjelenítését az alábbi kódra:

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

Miután ezeket a módosításokat elvégeztük, futtathatjuk az alkalmazást egy eszközön vagy emulátoron, és láthatjuk, hogy a hiba megfelelő párbeszédablakkal jelenik meg.

Az alkalmazás működése egyszerű: ahelyett, hogy egy Toast üzenetet jelenítenénk meg, most a connectionResult objektum segítségével ellenőrizzük, hogy milyen lépéseket tehetünk. A GoogleApiClient a connectionResult segítségével ad tájékoztatást a szükséges lépésekről. Ha a connectionResult.hasResolution() értéke true, az azt jelenti, hogy a felhasználó valamilyen módon megoldhatja a problémát, például engedélyezheti a helymeghatározási szolgáltatásokat. Ha false, akkor a GoogleApiAvailability osztály segítségével egy hibát jelző párbeszédablakot jelenítünk meg, amely információval szolgál a probléma megoldásához. Ha a felhasználó sikeresen megoldotta a problémát, az onActivityResult() metódus visszahívódik, és a mResolvingError változó visszaállítása után ismét próbálkozhatunk a kapcsolat helyreállításával.

A Google API hibáinak kezelése mellett az alkalmazás helymeghatározásához szükséges további lépéseket is figyelembe kell venni. Ha a helyadatokat rendszeresen kell frissíteni, akkor a requestLocationUpdates() metódust kell használni a helyadatok periódikus lekérdezésére. Ennek beállítása és működése megegyezik a korábbi példában alkalmazott eljárásokkal, azzal a különbséggel, hogy itt a helyadatokat nem kérjük le egyszer, hanem folyamatos frissítéseket kapunk. Ehhez a következő lépéseket kell követnünk:

  1. Hozzunk létre egy új projektet Android Studio-ban, amelyet "LocationUpdates" néven mentünk el.

  2. A szükséges engedélyek hozzáadásához módosítsuk az AndroidManifest.xml fájlt.

  3. A build.gradle fájlban adjuk hozzá a Google Play Szolgáltatások függőséget.

  4. Módosítsuk az activity_main.xml fájlt, hogy a helyadatokat egy TextView segítségével jelenítsük meg.

  5. A MainActivity.java fájlban hozzunk létre globális változókat, valamint azokat a metódusokat, amelyek kezelni fogják a helyadatokat.

A folytatásban a LocationListener és a GoogleApiClient.ConnectionCallbacks interfészek segítségével kezeljük a helyadatok frissítését és a kapcsolódási hibákat. Az új helyadatok megjelenítése után a rendszer folyamatosan értesíteni fog minket az új pozícióról.

Az ilyen típusú hibakezelés és helymeghatározási folyamatok megfelelő implementálása elengedhetetlen, hogy alkalmazásunk felhasználóbarát és hibamentes maradjon. Fontos azonban megjegyezni, hogy a Google API verziók közötti eltérések és a különböző eszközök eltérő működései további kihívásokat jelenthetnek, ezért teszteléskor figyeljünk arra, hogy az alkalmazás minden esetben megfelelően reagáljon a hibákra és a különböző verziókra.