Ako sme spomínali v úvode, radostné opojenie z foťáka rýchlo vystrieda strach a hnus z reálnej implementácie na zariadeniach. Aj v ideálnom svete mal poskytovať Android dve možnosti na využitie fotoaparátu:
V prvej možnosti stačí poslať správny intent do systému, ktorým sa spustila interná fotoaplikácia a po zhotovení fotečky ste vo výsledku aktivity dostali adresu URI k výsledku. (Ideálny prípad opisuje dokumentácia).
Druhá možnosť ponechá všetko na vás, pre prípady, že reimplementujete Instagram: sami si musíte zistiť správny objektív (áno, zariadenia môžu mať predný a zadný foťák), zmanažovať si vykresľovanie náhľadu, ustanoviť poslucháčov pre odchytávanie výsledku, zapisovanie výsledku do súborov a uvoľnovanie prostriedkov (pretože k foťáku môže pristupovať len jediná appka v systéme).
A teraz k tvrdej realite: prvá možnosť je prechádzka mínovým poľom. Niektoré zariadenia bezdôvodne force-closujú, iné zariadenia nesprávne otáčajú náhľad fotky, ďalšie zase poskytujú náhľad nepoužiteľnej verzie, alebo ukladajú fotku na dve (alebo tri) miesta naraz, či pre istotu nevrátia žiaden výsledok. Riešeníe tejto situácie, ktorá pripomína Internet Explorer 6 a vývoj webových aplikácií v roku 20XX, poskytujú prinajmenšom dve knižnice:
Druhá knižnica je, v súlade s chaosom, odsúdená na kompletný prepis, ale doposiaľ (apríl 2015) sa tak nestalo.
Samotný Android pripravil alternatívne API pre prístup k fotoaparátu reprezentované balíčkom android.hardware.camera2
, ale to je k dispozícii až od API Level 21 (Android 5) a nerieši problém so starými zariadeniami.
V tomto tutoriáli si teda ukážeme len krátku tour knižnicou AndroidCameraUtil
, ktorá je v rámci možnosti použiteľná a dáva rozumné výsledky pre aplikáciu, ktorá nechce byť primárne fotoaplikáciou.
Vytvorme v Studiu nový projekt Fotr
.
Knižnica AndroidCameraUtil
zatiaľ nemá žiadne oficiálne vydania, ale zdrojáky z GitHubu sú voľne k dispozícii. Stiahnime si z repa ralfgehrehr/AndroidCameraUtil
zdrojáky a rozbaľme ich do vhodného adresára, najlepšie do adresára pre projekty Studia.
Knižnica je pripravená na priamy import do nášho projektu ako modul.
Z hlavného menu zvoľme File | Import Module... a uveďme cestu k rozbalenému adresáru s knižnicou. Studio nájde v projekte dva moduly: library
s jadrom knižnice a sample
s demonštračným kódom. Ukážkový súbor nebudeme potrebovať, preto zrušíme začiarknutie pre jeho import. Pre poriadok premenujeme modul library
na AndroidCameraUtil
.
Po ukončení sprievodcu zistíme, že v projekte sa úspešne zjavila knižnica.
Modul sample
, ktorý sme pri importe odignorovali, obsahuje kompletnú demonštračnú aplikáciu s jednou aktivitou, ktorá dokáže odfotiť a spracovať výsledok. Preštudujte si zdrojový kód a inšpirujte sa ním.
Dôležitou vecou je konfigurácia manifestu. Predovšetkým potrebujeme získať viacero oprávnení
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.FLASHLIGHT" />
Naša aplikácia žiada o oprávnenie čítať a zapisovať na SD kartu, používať foťák (CAMERA
), vibrovať (VIBRATE
) a využívať blesk (FLASHLIGHT
).
Každá appka môže definovať hardvérové a iné požiadavky, bez ktorých nedokáže fungovať. Príkladom je GPS: ak vaša aplikácia bezpodmienečne využíva GPS, môžete to deklarovať v manifeste v elemente <uses-feature>
. Na požiadavky potom prihliada obchod Google Play, ktorý odfiltruje z ponuky appky, ktoré vaše zariadenie nebude vedieť kvôli chýbajúcim požiadavkam spúšťať. Zoznam požiadaviek môžete nájsť v dokumentácii Androidu.
S požiadavkami je to však prekérne. Zoberme si pôvodný tablet od Googlu: Nexus 7. Ten má len jediný, predný fotoaparát, čo nespĺňa požiadavku na typizovaný zadný foťák. V takomto prípade musíme byť ako rozprávková princezná, čo prišla s darom-nedarom a v manifeste musíme explicitne deklarovať využívanie fotoaparátu, ale nie jeho nutnosť.
Požiadavky teda deklarujme nasledovne v manifeste:
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.front"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.autofocus"
android:required="false" />
<uses-feature
android:name="android.hardware.camera.flash"
android:required="false" />
Dodajme do layoutu hlavnej aktivity tlačidlo pre fotenie a widget pre zobrazenie miniatúry fotky:
<LinearLayout android:orientation="vertical" xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:tools="https://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:text="Shoot!"
android:onClick="onShootButtonClick"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<ImageView
android:id="@+id/thumbnailImageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true" />
</LinearLayout>
Následne dodajme kód aktivity. Najdôležitejšie metódy sú dve:
onShootButtonClick()
bola deklarovaná v layoute ako obslužná metóda pre kliknutie na tlačidlo. V nej zavoláme zdedenú metódu startCameraIntent()
, ktorou spustíme aktivitu pre fotenie a získame výsledok.onPhotoUriFound()
sa zavolá vo chvíli, keď aktivita vytvorila fotku a skončila. Vďaka základnej triede CameraIntentHelperActivity
máme k dispozácii nasledovné inštančné premenné:
photoUri
: adresa k súboru obsahujúcemu fotografiurotateXDegrees
: kompenzácie rotácie súboru, ktorá opravuje chybu v zariadeniach zbytočne rotujúcich fotku Kód zároveň opravuje chybu, kde niektoré zariadenia ukladajú fotku na dve, či tri miesta, na čo využíva pomocnú triedu BitmapHelper
z knižnice, a to odstránením nadbytočných súborov.
public class MainActivity extends CameraIntentHelperActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onShootButtonClick(View v) {
startCameraIntent();
}
@Override
protected void onPhotoUriFound() {
super.onPhotoUriFound();
Bitmap photo = BitmapHelper.readBitmap(this, this.photoUri);
if (photo != null) {
photo = BitmapHelper.shrinkBitmap(photo, 300, this.rotateXDegrees);
ImageView imageView = (ImageView) findViewById(R.id.thumbnailImageView);
imageView.setImageBitmap(photo);
}
//Delete photo in second location (if applicable)
if (this.preDefinedCameraUri != null && !this.preDefinedCameraUri.equals(this.photoUri)) {
BitmapHelper.deleteImageWithUriIfExists(this.preDefinedCameraUri, this);
}
//Delete photo in third location (if applicable)
if (this.photoUriIn3rdLocation != null) {
BitmapHelper.deleteImageWithUriIfExists(this.photoUriIn3rdLocation, this);
}
}
}
Teraz už môžeme aplikáciu bez problémov spustiť.
Aktivita CameraIntentHelperActivity
dedí od FragmentActivity
a teda nepodporuje lištu akcií. Ak napriek tomu chcete využívať lištu akcií, musíte upraviť zdrojáky knižnice:
AndroidCameraUtil
nahradiť závislosť na novšiu verziu, napr. na compile 'com.android.support:appcompat-v7:22.1.0'
ActionBarActivity
Úpravy sú nutné, ak budete chcieť napríklad zdieľať fotky priamo z aktivity.
Ak spustíme aktivitu a začneme fotiť, získame demonštračné emulované dáta z GenyMotion. Tento emulátor však dokáže prepojiť webkameru z vášho hostiteľského počítača a dodávať z nej dáta priamo do appky.
Výsledná aplikácia sa nachádza na GitHube, v repozitári novotnyr/android-fotr-demo-2015
.