piątek, 21 lutego 2014

Kurs programowania na Androida w formacie PDF i ebook

W związku z prośbą czytelników udostępniam kurs w formacie pdf i mobi.

Wersja PDF
Wersja MOBI
Kody źródłowe

W publikacji znajduje się licencja użytkowania. Proszę się z nią zapoznać.

poniedziałek, 17 lutego 2014

Bezpłatny kurs programowania na platformę Android

Tutaj znajdziecie mój bezpłatny kurs programowania na platformę Android :)

http://andrzejklusiewicz-android.blogspot.com/p/bezpatny-kurs-programowania-android-java.html

Bluetooth – czyli niebieskie pogaduszki


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.



O korzyściach płynących z możliwości korzystania z Bluetooth nie trzeba chyba nikogo przekonywać. Dziś zajmiemy się podłączaniem urządzeń z Bluetooth i komunikacją z nimi. Tradycyjnie zaczynamy od dodania niezbędnych uprawnień do pliku manifestu:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

Pierwsze uprawnienie daje nam możliwość korzystania z Bluetooth, drugie jego włączania i wyłączania (a musimy mieć taką możliwość w razie gdyby w urządzeniu BT było wyłączone).

Napiszemy aplikację która będzie w stanie :
Rozgłosić fakt swojego istnienia, tak by inne urządzenia skanując w poszukiwaniu sprzętu z BlueTooth mogły nas zobaczyć. Jeśli urządzenia nie są ze sobą sparowane, urządzenie nie siejące informacji o swoim istnieniu nie będzie widoczne.
Wyświetlić na konsoli listę sparowanych urządzeń
Wykryć inne urządzenia pozwalające się wykryć (czyli rozgłaszające fakt swojego istnienia).
Utworzyć serwer sieciowy oparty na BlueTooth, który będzie oczekiwał na podłączenie się clienta, a gdy to nastąpi prześle mu komunikat.
Połączyć się jako client do innego urządzenia na którym będzie uruchomiony serwer.

Nasza aplikacja będzie mogła działać zarówno jako client, jak i jako serwer. Zależeć to będzie od tego, który przycisk naciśniemy. Aby móc testować aplikację najlepiej będzie byś wyposażył się w dwa fizyczne urządzenia wyposażone w BlueTooth. Przyklejam parę guzików, które będą uruchamiać wcześniej omówione funkcje. Na komponencie TextView na którym aktualne wyświetlam napis „TextView” w momencie uruchomienia aplikacji pokaże się adres mac naszego urządzenia. Na TextView na którym początkowo wyświetlać się będzie napis „Połączenie nie nawiązane”, wyświetli się później tryb działania aplikacji – jako serwer lub jako client. W polu edycyjnym będziemy wpisywać mac adres urządzenia które będzie pracowało jako serwer. Wstawiłem tutaj adres swojego telefonu, żeby później nie musieć każdorazowo go podawać.





Przejdźmy teraz do kodu głównej aktywności. Na początku w metodzie onCreate podpinam komponenty wizualne do obiektów. Nic nowego ani nadzwyczajnego:



Dalej w metodzie onCreate podpinam wywołania metod realizujących poszczególne funkcjonalności do przycisków.



Wyjaśnienia mogą wymagać jedynie linie 67-69 i 75-78. W zależności od tego czy aplikacja na danym urządzeniu będzie pracować jako serwer czy client, wywoływane są wątki realizujące zadania danej strony. Oparłem to na wątkach, ponieważ np. oczekiwanie na połączenie , czy proces łączenia są operacjami które blokowałyby wątek głównej aktywności. W ten sposób wątki pracują sobie asynchronicznie, a aplikacja nie „blokuje się”. Obiekt ba z linii 76 reprezentuje fizyczny adapter Bluetooth urządzenia. Linia 77 to pobranie mac adresu wpisanego w pole edycyjne. Będzie nam ten adres potrzebny, po przekazujemy go przez parametr konstruktora do wątku clienta. Musi on wiedzieć jaki jest adres urządzenia z którym ma się łączyć. W liniach 68 i 75 w zależności od trybu działania aplikacji, wyświetlam na stosownym polu na ekranie, jaką rolę spełnia aplikacja (czy jest serwerem czy clientem).
Przejdźmy dalej. Linie 85,86 to wyświetlenie mac adresu urządzenia na ekranie oraz konsoli. W dalszej części metody onCreate aktywności (linie 87-91) obsługuję sytuację gdyby okazało się, że BlueTooth na urządzeniu jest wyłączony. Aktywność BT weryfikuję metodą isEnabled() klasy BluetoothAdapter (linia 87). Jeśli okaże się że jest wyłączony, wyświetlam komunikat z prośbą o zgodę na włączenie BT. Ponieważ intencję z prośbą wywołuję z użyciem startActivityForResult, dodałem też metodę onActivityResult która jest automatycznie wywoływana przez system kiedy użytkownik udzieli zgody na włączenie BT (albo i nie udzieli :p). Jeśli użytkownik udzieli zgody, sam system uruchomi BT a nam pozostaje tylko zainicjalizowanie obiektu którego będziemy używać do komunikowania się z fizycznym adapterem BT i ewentualnie wyświetlenie informacji na konsoli.


Przeszkoczymy na razie definicję obiektu klasy BroadcastReceiver (linie 102-117), wrócimy do niej za chwilę, a na razie przyjrzymy się metodom na końcu klasy.


Metoda dajSieWykryc() sprawia, że nasze urządzenie jest widoczne dla innych urządzeń które skanują w poszukiwaniu „kolegów” :). Jeśli o to nie zadbamy a urządzenia nie będą sparowane, nasze nie będzie widoczne dla innych. Linie 121 i 123 są odpowiedzialne za wyświetlenie zapytania o możliwość „ujawnienia się” i rozpoczęcie rozgłaszania swojego istnienia. Linia 122 to opcjonalna konfiguracja czasu rozgłaszania. Domyślnie urządzenie rozgłaszałoby przez 2 minuty, natomiast podałem mu wartość 300 (sekund) , dzięki czemu będzie widoczny przez 5 minut.





Metoda wykryjInne() powoduje rozpoczęcie poszukiwania innych urządzeń. Trwa to ok 12 sekund. Co istotne – widoczne będą tylko te urządzenia które będą rozgłaszały swoje istnienie. Kiedy jakieś urządzenie zostanie znalezione trzeba będzie obsłużyć takie zdarzenie nie przerywając przeszukiwania. Z tego powodu rejestrujemy odbiorcę komunikatu rozgłoszeniowego informującego o znalezieniu urządzenia (linia 130). Odbiorca to obiekt klasy BroadcastReceiver którego metoda onReceive jest automatycznie wywoływana w przypadku znalezienia urządzenia z włączonym rozgłaszaniem sygnału. W ramach tej metody wyciągam obiekt reprezentujący dane urządzenie fizyczne, sprawdzam czy to urządzenie zostało wcześniej sparowane i wyświetlam o nim informacje.

Łącznie działanie tych dwóch elementów przedstawia się w ten sposób, że metoda wykryjInne() rozpoczyna poszukiwanie innych urządzeń, a gdy jakiś znajdzie to zostaje automatycznie wywołana metoda onReceive obiektu klasy BroadcastReceiver zarejestrowanego jako odbiorca takiego zdarzenia. W sumie, na konsoli zostaną wyświetlone informacje o dostępnych w okolicy urządzeniach. Po uruchomieniu na konsoli widzę znalezione urządzenie (jeśli zostaną wykryte, pojawi się ich więcej ;)) :



W ramach tej klasy mamy jeszcze metodę pokazSparowane():

Jej zadaniem jest sprawdzenie listy urządzeń które wcześniej sparowaliśmy i wyświetlenie informacji o nich na ekranie. Ta metoda nie sprawdza czy urządzenia te są dostępne , a jedynie wyświetla informacje o zarejestrowanych urządzeniach które są przechowywane w systemie:


W przypadku mojego telefonu zostały wyświetlone informacje o słuchawce na Bluetooth której używam (więc siłą rzeczy musi być sparowana), oraz tablecie który musiał zostać sparowany by odbywać po BT pogaduszki z moim telefonem na potrzeby niniejszego artykułu.

Przejdźmy teraz do elementu komunikacji. Jeśli programowałeś kiedyś sieciowe połączenia client-server w Javie, na pewno rozpoznasz wiele elementów. W zasadzie jest to zwykłe połączenie po socketach z tą różnicą, że nasza komunikacja nie będzie się odbywała po sieci, a po Bluetooth. Najpierw weźmiemy na tapetę klasę odpowiadającą za działanie aplikacji jako serwer.



W linii 15 definiuję socket przy użyciu którego będzie odbywała się komunikacja. Zagadkowe mogą się wydać linie 21 i 22. Usługa musi mieć unikalny identyfikator, dzięki któremu będzie jednoznacznie rozpoznawana ( http://en.wikipedia.org/wiki/Universally_unique_identifier ). W tych liniach naszą usługę rejestruję pod nazwą „Usługa witająca” pod identyfikatorem
550e8400-e29b-41d4-a716-446655440000
Ten akurat identyfikator jest całkiem przypadkowy, skopiowałem jakiś przykład takiego identyfikatora z internetu. Linie 27-54 to klasyczna implementacja procesu nasłuchu, różnica polega na tym, że nie sieciowego a na bluetooth. Wątek czeka na kontakt ze strony serwera, po czym otwiera strumień i wysyła tekst „Witaj kolego!”.




Po tej stronie otwieram socket dla połączenia z serwerem (podobnie jak po stronie serwerowej nie socket sieciowy a po bluetooth) i odczytuję co mi serwer przysłał.
Uruchamiam teraz program na obu urządzeniach, telefon robi za serwer, tablet za clienta.

Efekty działania serwera:




Po stronie clienta:




Gdybyś zechciał rozwijać ten program, lub na jego bazie napisać coś swojego, pamiętaj że mamy do czynienia z nieco innym niż sieciowy protokołem i usługa BT z jednej strony może się łączyć tylko z jedną inną usługą.






Stosowanie usług sieciowych (Web Services) z poziomu Androida


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.


Web Services możemy stosować w Androidzie tak jak w każdym innym programie napisanym w Javie, z tą różnicą że tutaj musimy pamiętać o kilku dodatkowych wymogach. Ponieważ stosowanie z usług sieciowych wiąże się z korzystaniem z sieci (jak sama nazwa wskazuje :) ), musimy zadbać o odpowiednie uprawnienia aplikacji. Z tego tytułu dodajemy wpis :

<uses-permission android:name="android.permission.INTERNET" />

po tagu </application> a przed </manifest>. Jest jeszcze jedna bardzo ważna sprawa – jeśli chcemy korzystać w aplikacji z usług sieciowych, musimy to robić w osobnym wątku. Jeśli o tym zapomnimy, Android i tak nam o tym przypomni rzucając na konsolę komunikat :
StrictMode$AndroidBlockGuardPolicy
Ja zrobiłem to przy użyciu osobnej klasy dziedziczącej po AsyncTask, choć jeśli chcesz możesz zastosować zwyczajny obiekt klasy Thread. Na poniższej ilustracji zczytałem zawartość naszej strony internetowej i wyświetiłem ją na konsoli.



Wynik wyświetlony na konsoli:



To jest po prostu kod źródłowy strony internetowej, choć równie dobrze mógłby to być jakiś plik z danymi w XML.

Alternatywnie możemy wykorzystać gotową bibliotekę HttpClient od Apache, dzięki której nie musimy skupiać się tak bardzo na szczegółach implementacji czytania strumienia. Efekt jest zasadniczo taki sam, forma inna. Tutaj również musimy pamiętać o wydzieleniu osobnego wątku.





Zajmiemy się teraz odbieraniem obiektów XML przy użyciu usług sieciowych. Wrzuciłem na serwer przykładowy zasób XML, by mieć co przetwarzać. Ten plik tam pozostanie, więc możesz z niego skorzystać. Adres pliku: http://jsystems.pl/storage/dane.xml
Bazując na poprzednim rozwiązaniu przerobiłem klasę Watek. Kod odpowiedzialny za pobieranie danych przeniosłem do osobnej metody dawajDane. Przy okazji przerobiłem to tak, by adres serwera, port i zasób przekazywać przez parametry metody a nie robić tego „na sztywno”. Dorzuciłem też metodę dawajXml która korzystając z metody dawajDane odbierze dane w postaci String i odda jako obiekt DOM:



Metoda doInBackground korzysta z przed momentem opisanych metod i odbiera obiekt DOM. Metoda onCreate aktywności pozostaje bez zmian:


Nagrywanie video


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.


Podczas nagrywania, potrzebny nam będzie podgląd na nagrywany obraz. W tym celu w pliku layoutu aktywności wstawiam komponent SurfaceView. Będzie robił za mini ekran kamery. Wielkość i szerokość tego podglądu ustawiłem na na tyle małe aby zmieściło się na większości ekranów telefonów z Androidem (parametry layout_width i layout_height). Nie daję fill_parent, ponieważ chcę obok jeszcze umieścić guzik służący do rozpoczynania i przerywania nagrywania.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<SurfaceView
android:id="@+id/videoview"
android:layout_width="360px"
android:layout_height="240px"/>
<Button
android:id="@+id/mybutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Nagrywaj"
/>
</LinearLayout>
</LinearLayout>


Trzeba też zadbać o niezbędne uprawnienia aplikacji. Moja kamera będzie rejestrowała nie tylko obraz, ale i dźwięk – stąd też uprawnienia do nagrywania audio. Sam plik z nagraniem zostanie zapisany na zewnętrznej karcie pamięci – stąd uprawnienie WRITE_EXTERNAL_STORAGE. Jeśli zamierzasz zapisywać nagrany film w pamięci wbudowanej, tego uprawnienia nie potrzebujesz.

<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Przejdźmy teraz do kodu który będzie całość obsługiwać. Aby nasza aktywność mogła otrzymywać informacje o tym że powierzchnia podglądu została przygotowana (a będzie trzeba ją przygotować przed rozpoczęciem nagrywania), nasza aktywność musi implementować interfejs SurfaceHolder.CallBack. W związku z implementacją tego interfejsu musimy też posiadać zaimplementowaną metodę onSurfaceChanged (ale nie będziemy jej tutaj rozwijać). W linii 20 definiuję obiekt którego użyję do obsługi przycisku (dalej podpinam pod niego referencję do buttona zdefiniowanego w layoucie). W linii 21 tworzę obiekt klasy MediaRecorder. To klasa służąca do rejestracji obrazu i dźwięku. W linii 22 tworzę obiekt klasy SurfaceHolder. Z użyciem obiektu tej klasy możemy np. konfigurować wielkość i format nagrania, ale ogólnie służy do „obserwacji” tego co się dzieje z obiektem surfaceView – czyli ekranem podglądu.


Podstawowa grafika 2D


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.



Aby wykonywać jakiekolwiek rysunki, będziemy potrzebowali płótna na którym będziemy rysować. Takie płótno będzie obiektem klasy Canvas. Tworzę zwyczajny projekt, a z ekranu głównego usuwam domyślnie pojawiający się tam kompontent textView1. Zasadniczo nasze płótno przykryje całość ekranu włącznie z tym komponentem, więc kasowanie go nie jest konieczne.


Obiekt klasy Canvas początkowo będzie pusty. Możemy rysować obiekty korzystając z wyznaczania kolejnych linii wg współrzędnych, ale możemy też skorzystać z gotowych w klasie Canvas metod które narysują dla nas wybrane figury geometryczne – typu prostokąt czy okrąg. Aby stworzyć i wykorzystywać nasze „płótno”, nasza aktywność musi posiadać wewnętrzną klasę dziedziczącą po klasie View dla której przesłaniamy metodę „onDraw” przyjmującej jako parametr obiekt klasy canvas z którego będziemy korzystać. Myślę że posłużymy się tutaj obrazem. Pierwsze co robię, to do aktywności na której chcę rysować dodaję klasę wewnętrzną CanvasView (nazwa dowolna), dziedziczącą po klasie View (android.view.View). Musimy dodać do niej konstruktor przyjmujący jako parametr kontekst. W zasadzie z kontekstem nic szczególnego tutaj nie robimy, jedynie przekazujemy je do super klasy, niemniej taki konstruktor po prostu musi się tutaj pojawić (wymogi implementacyjne klasy View). Nas bardziej interesuje metoda „onDraw”, bo to w niej właśnie opisujemy wszystko co ma zostać narysowane na „dzień dobry” na naszym płótnie.

Jak na razie, nic szczególnego się nie dzieje. Musimy sprawić, by zamiast domyślnego layoutu (tego określanego w pliku XML), na ekranie pojawiło się nasze „płótno”.


Zmiany tej dokonałem podmieniając parametr dla metody setContentView w metodzie onCreate naszej aktywności. Widzimy to na powyższej ilustracji w linii 26. W linii 25 pozostawiłem (dla przykładu) wykomentowane domyślne ustawienie.
Skoro już wszystko mamy popodpinane, zajmiemy się teraz samym rysowaniem. Dodałem trzy linie do metody onDraw. W linii 19 definiuję obiekt klasy Paint. Jest to pędzel którym będziemy rysować różne kształty. Możemy dla niego ustawiać różne parametry – np. kolor – ale o tym za chwilę. Linie 20 i 21 to wywołanie metody drawLine , służącej rysowaniu linii. Przyjmuje ona 5 parametrów. Wg. Kolejności: X początkowe, Y początkowe, X końcowe, Y końcowe, obiekt klasy paint którym rysujemy (nasz pędzel). Narysowałem więc po prostu dwie linie złączone na końcach:



Efekt:


Pobawimy się teraz kolorami pędzla. Do zmiany jego koloru, służy metoda setARGB klasy Paint (której nasz pędzel jest obiektem). Przyjmuje ona cztery parametry. Pierwszy to stopień przeźroczystości linii / figury. 0 to całkowita przeźroczystość – czyli w praktyce niewidoczność, 255 to całkowita nieprzezroczystość. Kolejne trzy parametry to RGB - stopień nasycenia trzech kolorów wg kolejności : Red, Green,Blue. Przyjmowane wartości to 0-255. Przed narysowaniem pierwszej linii ustawiam kolor pędzla na czerwień (255 dla RED, reszta 0), następnie rysuję linię. Ponownie zmieniam kolor pędzla, tym razem na zielony (255 dla GREEN, reszta 0) i znowu rysuję linię:



Efekt:



Wzbogaciłem kod o kolejne kilka linii. Zmieniłem kolor pędzla na niebieski i dorysowałem kolejną linię. Doszło nam wywołanie metody setStrokeWidth dla pędzla (linia 25). Ustawia ona grubość linii pędzla:



Efekt:


Zajmiemy się teraz rysowaniem figur geometrycznych. Zaczniemy od prostokątów (i kwadratów, wszak kwadrat też jest prostokątem tyle że o równych ramionach :) ).
Stworzyłem nowy projekt, wszystko na identycznej zasadzie jak w poprzednim przykładzie. Różni się tylko zawartością metody onDraw:



Metoda setARGB jest już znana z poprzednich przykładów, jednak tutaj pobawiłem się troszkę przezroczystością. Z nowych rzeczy mamy tutaj dwukrotne wywołanie metody drawRect klasy Canvas. Metoda ta służy rysowaniu prostokątów. Pierwsze dwa argumenty to współrzędne (x,y) jednego z rogów, kolejne dwa to współrzędne(x,y) rogu przeciwległego. W tym przypadku według kolejności – lewy górny i prawy dolny. Ostatni argument to obiekt klasy Paint, czyli pędzel którym będziemy rysować. Tutaj narysowałem dwa prostokąty – jeden nieprzezroczysty fioletowy ((?) sorry, mężczyźni nie rozróżniają kolorów zbyt szczegółowo) , drugi częściowo przezroczysty w kolorze szybki przyciemnianych okularów (?). Drugi ma ustawioną częściową przezroczystość. W przypadku rysowania pierwszego prostokąta przezroczystość dla pędzla ustawiłem na 0, w drugim na 100. Efekt wygląda tak:



Rysowanie kółek nie sprawia również wielu problemów. Stworzyłem kolejny projekt, tym razem metoda onDraw wygląda tak:

Generalnie do rysowania kół służy metoda drawCircle która przyjmuje cztery parametry. Pierwsze dwa to X i Y środka koła, trzeci to promień, a czwarty to pędzel którego parametry ustawiamy na identycznych zasadach co wcześniej. Zaprzęgnąłem tutaj pętle do wyrysowania mi kilku kół, które częsciowo na siebie nachodzą. W zasadzie cały kod to rysowane w rożnych miejscach koła, ze zmianą właściwości pędzla. Efekt wygląda tak :



Robacek :) Ponieważ uznałem że przeciętny robaczek żyje na listku/trawce/gałązce/czołgu/czymś innym zielonym, musiałem zmienić kolor tła. Do tego celu użyłem (linia 20) metody drawRGB obiektu klasy Canvas. Ta metoda nie przyjmuje atrybutu przezroczystości tak metoda drawARGB dla obiektów klasy Paint. Płótno samo w sobie nie może być przezroczyste :). Kolejność kolorów jak sama nazwa wskazuje RGB - Red, Green, Blue.

Odtwarzanie video


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.



Odtwarzać możemy formaty MP4 (MPEG-4), AVC, 3GP. Mamy gotowy komponent który wystarczy przykleić gdzieś na aktywności. Sam plik video może znajdować się już w aplikacji, ale może się też znajdować np. na karcie SD. W tym przykładzie uruchomię plik video znajdujący się w aplikacji. W katalogu „res” aplikacji dodaję podkatalog „raw”. Do niego wrzucam plik video. Wklejony przeze mnie filmik to 2 i pół minuty video z tańczącymi i śpiewającymi parabolami. Nie wrzucaj zbyt dużego pliku video, ponieważ przy każdej aktualizacji kodu i ponownym uruchamianiu programu , całość będzie uploadowana na emulator lub telefon. Gdyby plik był duży, trwałoby to niemiłosiernie długo.


W kolejnym kroku przyklejam na aktywności komponent VideoView:




Następnie wprowadzam kilka zmian w metodzie onCreate aktywności głównej:



Linia 15 do podpięcie referencji do komponentu VideoView. Muszę to zrobić by w jakikolwiek sposób móc się do tego elementu odnosić (np. wskazać mu plik video). Ponieważ chcę wskazać plik video zawarty w samej aplikacji, muszę stworzyć do niego referencję w postaci obiektu klasy Uri i przekazać go do metody setVideoURI komponentu klasy VideoView (linie 16 i 17). Średnio to wygodne moim zdaniem . Szkoda że nie ma możliwości przekazania po prostu ścieżki jako Stringa. Ścieżkę do pliku muszę podać z przedrostkiem „android:resource://”, pakietem w którym znajduje się dana aktywność, katalogiem raw i nazwą pliku video bez rozszerzenia.




W linii 18 widać wykomentowany alternatywny sposób. Podaję tutaj ścieżkę do pliku wideo znajdującego się poza aplikacją, z użyciem metody setVideoPath. Tutaj dla odmiany możemy podać ścieżkę jako zwykłego Stringa.
Po uruchomieniu aplikacji wszystko działa w spodziewany sposób. Jedyna drobna uwaga – filmik nie dopasowuje się do ustawionej wielkości komponentu VideoView, a wyświetla się w swoich oryginalnych proporcjach. W związku z powyższym w orientacji pionowej u mnie (Samsung ACE 3 ) przy propocji ekranu 480:800, wideo zajmuje jaką 1/3 ekranu.


Odtwarzanie dźwięku


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.



Zaczniemy od najprostszego przykładu. Program zaraz po uruchomieniu odtworzy plik MP3 który będzie znajdował się w ramach samego programu.

Zaczynamy od stworzenia projektu, oraz utworzenia podkatalogu o nazwie „raw” w katalogu „res” projektu. Następnie do podkatalogu raw wrzucamy utwór muzyczny w którymś z formatów: wav, aac, mp3, wma, amr, ogg, midi



Wszystko co wrzucimy do podkatalogu res, jest automagicznie rejestrowane jako zasób dostępny z poziomu kodu.
Teraz już tylko mały szlif kodu głównej aktywności:



Kluczowe są tutaj linie 19-21. Żartowałem :) Interesują nas linie 16 i 17. W linii 16 tworzymy obiekt klasy MediaPlayer która jako taka służy do odtwarzania multimediów. Podczas tworzenia przekazujemy jako argumenty metody create : kontekts i zasób. Jak widać nie podajemy rozszerzenia pliku.
Linia 17 to uruchomienie odtwarzania zasobu któy wskazaliśmy w linii 16.
W sytuacji gdyby plik muzyczny który chcemy odtwarzać znajdował się poza aplikacją, np. na karcie SD nasz kod wyglądać by musiał tak:

MediaPlayer mp = new MediaPlayer();
mp.setDataSource(”/sdcard/rammstein/raise_raise.mp3”);
mp.prepare();
mp.start();

Wykorzystanie aparatu fotograficznego do robienia zdjęć


Kod źródłowy do tej lekcji znajduje się tutaj. Rozpakuj go i zaimportuj. W katalogu z kodem źródłowym znajdą się podkatalogi oznaczone numerami. Są to po prostu kolejne wersje kodu, tworzonego w ramach przykładów. Możesz analizować kolejne kroki tworzenia aplikacji, lub od razu zabrać się za kod z katalogu o najwyższym numerku.


Podobnie jak w przypadku czujników, do kamery również mamy gotowy interfejs. Zostały stworzone odpowiednie klasy i metody, wystarczy tylko z nich skorzystać. Zaczniemy od najprostszego programu robiącego zdjęcie i wyświetlające je na komponencie klasy ImageView. Przyklejam na głównej aktywności jeden przycisk i jeden imageView:



Po naciśnięciu tego przycisku zostanie uruchomiony wbudowany program do robienia zdjęć, a następnie odbierzemy zrobione zdjęcie i wyświetlimy je na ImageView. Poniżej wklejam kod aktywności związanej z tym ekranem. W liniach 26-34 oprogramowuję zdarzenie naciśnięcia przycisku. Część która jest związana z samym robieniem zdjęć jako takim to linie 30 i 31. Tworzę nową intencję której zadaniem jest wywołanie wbudowanego w Androida programu do robienia zdjęć (linia 30). W kolejnej linii uruchamiam tę intencję. Wywołanie takiej intencji wygląda nieco inaczej niż to do czego się przyzwyczailiśmy. Zazwyczaj wywoływaliśmy metodę startActivity(Intent i), a tutaj pojawia się startActivityForResult(Intent i, int x). Sama w sobie metoda zakłada że wywoływana intencja coś nam odda. Przy zakończeniu działania intencji i zwrocie danych wywoływana jest metoda onActivityResult (o tym za chwilę).Jako parametry podajemy intencję która ma zostać uruchomiona, oraz identyfikator wywołania. Poprzez drugi parametr podajemy wartość (parametr typu int) która później zostanie zwrócona po zrobieniu zdjęcia. Przydatne gdybyśmy np. wywołali kilka różnych intencji oczekując różnych zwracanych wyników. Każda z takich intencji wywoła metodę onActivityResult (która parę linii niżej implementujemy) w której możemy później po takim identyfikatorze dojść z której to intencji/wywołania intencji zostały zwrócone dane.



W liniach 38-44 implementuję metodę onActivityResult która jest automagicznie wywoływana w momencie kiedy nasza intencja wywołana przy użyciu metody startActivityForResult kończy swój przebieg. W tej właśnie metodzie odbieramy zrobione zdjęcie. Przyjrzyjmy się warunkom w linii 39. Sprawdzam parametr requestCode. To jest właśnie parametr który przekazałem przy wywoływaniu intencji (linia 31). Sprawdzam czy requestCode jest równe 1, by upewnić się że to jest wywołanie intencji związane z robieniem zdjęcia (mogłem przeciez wywołać wcześniej kilka różnych intencji i oczekiwać na wynik). Drugi parametr – resultCode jest zwracany przez wywoływaną aktywność – tutaj wbudowany program do robienia zdjęć. RESULT_OK jest zwracane w przypadku zatwierdzenia zdjęcia. Może też zostać zwrócone RESULT_CANCEL jeśli ktoś anuluje robienie zdjęcia. W liniach 40 i 41 odbieram zrobione zdjęcie. Aby odebrać fotkę muszę zawsze odwołać się w wiązce do identyfikatora „data”. Linia 42 to już po prostu ustawienie odebranego zdjęcia jako obrazek dla komponentu ImageView. Wykorzystanie kamery wymaga odpowiednich pozwoleń, dlatego dodajemy je do pliku manifestu ( AndroidManifest.xml) aplikacji w ten sposób:

</application>

<uses-permission android:name="android.permission.CAMERA" />


</manifest>


Po uruchomieniu i naciśnięciu przycisku pojawia nam się wbudowany programik do robienia fotografii:




Zapomniałem „dziubka” zrobić, to bym sobie na „fejsbuczka” wstawił „słit focię”. Po zrobieniu zdjęcia, dolny panel powinien nam się zamienić na taki:




Mogę tutaj anulować operację, powtórzyć robienie zdjęcia, lub zatwierdzić. Zatwierdzamy, a po powrocie z tej aktywności nasz ekran wygląda tak:




Za wyjątkiem może zdjęcia, na swoim zobaczysz pewnie coś ładniejszego :p Przy użyciu tej metody możemy niestety robić tylko takie małe zdjęcia. Za chwilę zrobimy to troszkę inaczej i dostaniemy pełnowymiarowe zdjęcie.

Stworzyłem nową aplikację. Aby zrobić pełnowymiarowe zdjęcie, Android będzie musiał zapisać je w pliku. Podobnie jak wcześniej dodaję prośbę o uprawnienia do kamery pliku manifestu, teraz jednak muszę też uzyskać pozwolenie na korzystanie z karty pamięci, lub jakiegoś innego folderu.

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />


Android udostępnia publicznie dostępny katalog (dostępny dla wszystkich aplikacji). Aby się do niego dobrać możemy zastosować np. taką konstrukcję:

File katalog = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

Jak zawsze rozpoczynam pracę od przyklejenia komponentów:




Dałem jeden guzik który uruchomi intencję, oraz jeden TextView na którym znajdzie się ścieżka do zrobionej fotografii.
W aktywności, w metodzie onCreate podpinam referencję do komponentów i jako obsługę naciśnięcia przycisku wywołuję metodę obslugaGuzika().



Sama metoda „obslugaGuzika” jest przestawiona poniżej:




Wyjaśniam rzeczy których nie omawiałem wcześniej. W linii 50 tworzę plik który wykorzystam za chwilę do zapisania fotografii na dysku. Linie 51-54 służą jedynie nazwaniu pliku tak, by nazwa zawierała datę i czas zrobienia zdjęcia. Jeśli chcesz, możesz je nazwać po prostu „fotka”. Linia 57 to pobranie referencji do kyatalogu przeznaczonego na obrazki. W linii 59 tworzę plik w którym znajdzie się zdjęcie. W linii 63 do intencji podaję dodatkowy parametr tj. ścieżkę do pliku w którym ma się znaleźć zdjęcie. Po zakończeniu cyklu intencji wyświetlam na textView ścieżkę do utworzonego pliku.
Efekt:





Może nam nie odpowiadać to, że musimy dodatkowo klikać na androidowej aktywności do robienia zdjęć. Chcielibyśmy by przykładowo program sam robił zdjęcie w reakcji na jakieś zdarzenie – np. pobudzenie czujnika ruchu. Konieczność dodatkowego kliknięcia przez człowieka w takim wypadku nie wchodzi w grę. Jest możliwość automatyczniego robienia zdjęć, bez wywoływania dodatkowej systemowej aktywności, ale wymaga to więcej pracy.

Stworzyłem nowy projekt i przykleiłem na ekranie button i imageView:



Założenie jest takie, że zaraz po kliknięciu przycisku zostanie wykonana fotografia i wyświetlona na imageView. Muszę też zadbać o wymagane pozwolenie:


<uses-permission android:name="android.permission.CAMERA"/>


Przechodzimy teraz do kodu.




Aby automatycznie odbierać robione zdjęcie, musimy w klasie która ma je odbierać implementować PictureCallback (linia 26). To sprawi, że w momencie zrobienia zdjęcia zostanie wywołana metoda onPictureTaken do której zostanie przekazane zdjęcie w postaci tablicy elementów typu byte... Do tej metody przejdziemy za kilka chwil. Linie 37,38 to podpięcie referencji do komponentów, nic nadzwyczajnego. Kod z linii 41-51 ma na celu sprawdzenie czy urządzenie w ogóle posiada kamerę (linia 41) oraz znalezienie jej identyfikatora i uruchomienie. W linii 45 do zmiennej liczbowej cameraId przypisuję identyfikator kamery zwracany jest przez metodę znajdzKamere. Zwraca ona wartość większą niż -1 jeśli zostanie znaleziona tylna kamera, jeśli nie zostanie znaleziona -metoda zwraca wartość -1. To mam nadzieję że wyjaśnia warunek if z linii 46. Kodem samej metody zajmiemy się za chwilę. Linia 49 to uruchomienie kamery dla identyfikatora zwróconego przez metodę znajdzKamere. Przejdźmy dalej:




Linie 53-61 to podpięcie wywołania mojej metody onC pod kliknięcie przycisku. W liniach 64-67 znajdziemy metodę, która powoduje wykonanie zdjęcia przez kamerę. Istotnym z punktu widzenia funkcjonalności jest tutaj wywołanie metody takePicture dla obiektu klasy Camera dla którego wcześniej (linia 49) podpinaliśmy identyfikator fizycznego urządzenia. Linie 70 – 81 to metoda zwracająca identyfikator tylnej kamery. Wywoływaliśmy ją wcześniej (linia 45) w celu odnalezienia identyfikatora kamery. Zasada działania jest prosta. Jest pętla, której zawartość jest wykonywana tyle razy, ile jest kamer w systemie. Przy każdym „obrocie” sprawdzam czy właśnie sprawdzana kamera to ta odpowiadająca identyfikatorowi tylnej kamery (CameraInfo.CAMERA_FACING_BACK w linii 75). Gdybyśmy zechcieli robić zdjęcia z użyciem kamery przedniej, możemy wykorzytać identyfikator CAMERA_FACING_FRONT. Przejdźmy do dalszej części kodu:



Linie 85-90 to odblokowanie kamery w przypadku np. „minimalizacji” programu, tak by inne programy również mogły korzystać z tego urządzenia. Przesłaniam tutaj domyślną metodę onPause klasy Activity. Linie 94-97 to implementacja metody onPictureTaken (konieczność wymaga z faktu że implementujemy interfejs PictureCallBack. Ta metoda jest automatycznie wywoływana w momencie zrobienia zdjęcia. Przy użyciu metody decodeByteArray klasy BitmapFactory (linia 95) konwertuję obraz w postaci tablicy elementów typu byte do bitmapy. Linia 96 to wyświetlenie fotki na komponencie klasy ImageView. Co z tego wszystkiego jest najważniejsze? Najważniejsze elementy:
Implementujemy interfejs PictureCallback (linia 26)
Podpięcie urządzenia pod obiekt klasy camera (linia 45)
Wywołanie metody takePicture obiektu klasy Camera wtedy kiedy chcemy zrobić zdjęcie (linia 65)
Implementacja automatycznie wywoływanej metody onPictureTaken i przetworzenie przekazanego nam przez argument zdjęcia (linia 94).

Te elementy to nasz „engine”, reszta to poboczny osprzęt.

Efekt po uruchomieniu i kliknięciu przycisku: