tag:blogger.com,1999:blog-55935868593520745002024-03-18T12:15:56.468-07:00O Androidzie ludzkim głosemandrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.comBlogger49125tag:blogger.com,1999:blog-5593586859352074500.post-51374993519041072632019-08-30T05:56:00.004-07:002019-08-30T05:56:49.963-07:00Parę fajnych szkoleńHej,<br />
poniżej przedstawiam spis bardzo wysoko ocenianych szkoleń w JSystems, które mają aktualnie gwarantowane terminy.<br />
<br />
<h2>
Szkolenia Java</h2>
<div>
<br /></div>
<div>
<a href="http://jsystems.pl/szkolenia-java;programowanie_w_jezyku_java.szczegoly">Programowanie w Języku Java</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-java;java_dla_zaawansowanych.szczegoly">Java dla zaawansowanych</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-java;spring_mvc_i_hibernate__tworzenie_aplikacji.szczegoly">Spring MVC i Hibernate - tworzenie aplikacji</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Szkolenia Oracle</h2>
<div>
<br /></div>
<div>
<a href="http://jsystems.pl/szkolenia-oracle;zaawansowany_sql_i_programowanie_w_plsql.szczegoly">Zaawansowany SQL i programowanie w PL/SQL</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-oracle;kompleksowe_szkolenie_programowanie_w_plsql_w_oracle.szczegoly">Kompleksowe szkolenie programowanie w PL/SQL w bazach Oracle</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Szkolenia PostgreSQL</h2>
<div>
<br /></div>
<div>
<a href="http://jsystems.pl/szkolenia-postgresql;administracja_bazami_danych_postgresql_z_elementami_ha__optymalizacji_i_replikacji.szczegoly">Administracja bazami danych PostgreSQL z elementami HA, optymalizacji i replikacji</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-postgresql;tuning_baz_danych_i_sql_w_postgresql.szczegoly">Tuning baz danych i SQL w PostgreSQL</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-postgresql;zaawansowany_sql_i_programowanie_baz_danych_postgresql_w_jezyku_plpgsql.szczegoly">Zaawansowany SQL i programowanie baz danych PostgreSQL jezyku PL/pgSQL</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Szkolenia C#</h2>
<div>
<br /></div>
<div>
<a href="http://jsystems.pl/szkolenia-dotnet;programowanie_w_jezyku_c.szczegoly">Programowanie w języku C#</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-dotnet;zaawansowane_programowanie_w_c.szczegoly">Zaawansowane programowanie w języku C#</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Szkolenia Docker i Kubernetes</h2>
<div>
<br /></div>
<div>
<a href="http://jsystems.pl/szkolenia-devops;docker_%E2%80%93_konteneryzacja_i_zarzadzanie_aplikacjami_oraz_microservisami.szczegoly"> Docker – konteneryzacja i zarządzanie aplikacjami oraz micro-servisami</a></div>
<div>
<a href="http://jsystems.pl/szkolenia-devops;od_zera_do_orkiestracji_kontenera.szczegoly">Docker i Kubernetes: od zera do orkiestracji kontenera</a></div>
<div>
<br /></div>
<div>
<br /></div>
<h2>
Szkolenia Big Data</h2>
<div>
<a href="http://jsystems.pl/szkolenia-big-data;kompleksowe_wprowadzenie_do_big_data__szkolenie_w_formie_warsztatowej.szczegoly">Kompleksowe wprowadzenie do Big Data - szkolenie w formie warsztatowej</a></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<br /></div>
andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com13tag:blogger.com,1999:blog-5593586859352074500.post-10089145447324155022018-05-14T01:33:00.001-07:002018-05-14T01:38:47.948-07:00Cześć :)<br />
Tak się składa że niedawno przeniosłem JSystems do nowego biura i dodałem nową usługę. Jeśli szukasz małej sali szkoleniowej w centrum Warszawy, w przyzwoitych cenach i o wysokim standardzie, to powinieneś zajrzeć pod <a href="http://jsystems.pl/wynajemSal.do" target="_blank">ten adres</a>.<br />
<br />
Dostępna jest przerwa kawowa, catering, można też wynająć komputery. Sale szkoleniowe są przeznaczone dla grup do 14 osób. Mieszczą się w centrum Warszawy przy placu Zawiszy. Ceny zaczynają się już od 259zł za dzień.<br />
<br />
To jest jak zapewne się domyślasz artykuł zrobiony pod pozycjonowanie usług mojej firmy :)<br />
Jeszcze tylko bezczelny link pod pozycjonowanie :D<br />
<br />
<h3>
<b><a href="http://jsystems.pl/wynajemSal.do" target="_blank">Małe sale szkoleniowe Warszawa Centrum</a> </b></h3>
<br />
Parę fotek:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh48EweU-o_R2lzC2ll_uVjfjr6Q02Ac2yOiGVTSUJbKnm7bQd7DBJJ5Rij5pmNBxysCZ9aAoGTU0UBGQTcj8i9X67zdz_RJ3_41fYDlIg2bGe2o7BDvldTiyZFE1mXFqrKNHeEVuR5ahpV/s1600/1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh48EweU-o_R2lzC2ll_uVjfjr6Q02Ac2yOiGVTSUJbKnm7bQd7DBJJ5Rij5pmNBxysCZ9aAoGTU0UBGQTcj8i9X67zdz_RJ3_41fYDlIg2bGe2o7BDvldTiyZFE1mXFqrKNHeEVuR5ahpV/s320/1.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXdtl-Zc_fqkS8WBHSRFlwC58BjQMo09nZi6prPVqeJTAkWinAtioXwDbesN6w95AjFyBUtxZGT9gkp7988JY9jIQEs9OszaiWP7TTnKmu8bTWcp7Y895S5rEKoK9SZZza09NUttX_DgEh/s1600/2.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXdtl-Zc_fqkS8WBHSRFlwC58BjQMo09nZi6prPVqeJTAkWinAtioXwDbesN6w95AjFyBUtxZGT9gkp7988JY9jIQEs9OszaiWP7TTnKmu8bTWcp7Y895S5rEKoK9SZZza09NUttX_DgEh/s320/2.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg385TxMxiCRvy2_gTQ-Pis-3Sa4T7-BNUOBpfTu4mPfX9JRO032sJslvVl4bZMJrbGniVHe5hZrSXvPkrxoJxRmYakM3BART3OC1xajZ3BiO5jsGBae7D5jkgEUPsRYiCy3bkPme70bocK/s1600/3.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg385TxMxiCRvy2_gTQ-Pis-3Sa4T7-BNUOBpfTu4mPfX9JRO032sJslvVl4bZMJrbGniVHe5hZrSXvPkrxoJxRmYakM3BART3OC1xajZ3BiO5jsGBae7D5jkgEUPsRYiCy3bkPme70bocK/s320/3.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihvRj8yNK24pbjJ4eIjh4ReCz73FStVy7Uwl-Pp3QEDyvQZDlTs8d9tUJwthZYCjMOb29Umnrqcpq48lIfnB471SgupExoS1vzuJ4Jq37afrHpWroHBLpPB2L5pdyQ2aDuHMsHEkRI5Xz/s1600/4.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjihvRj8yNK24pbjJ4eIjh4ReCz73FStVy7Uwl-Pp3QEDyvQZDlTs8d9tUJwthZYCjMOb29Umnrqcpq48lIfnB471SgupExoS1vzuJ4Jq37afrHpWroHBLpPB2L5pdyQ2aDuHMsHEkRI5Xz/s320/4.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz0c8XoMyeDA0LX21Qw2EsOo_-7ofFPmnw-EVIUIa7R0iZVUCqgbvVMJwjVmAV0cewUqcoWSQ9hY_yyKXmbB-tLEeduN_ezz03Njx6XVV74NorLZCjDajFdXuGDyKA2ZTREoxz-81bSzR0/s1600/5.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1068" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhz0c8XoMyeDA0LX21Qw2EsOo_-7ofFPmnw-EVIUIa7R0iZVUCqgbvVMJwjVmAV0cewUqcoWSQ9hY_yyKXmbB-tLEeduN_ezz03Njx6XVV74NorLZCjDajFdXuGDyKA2ZTREoxz-81bSzR0/s320/5.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEIC_Pb9Ka1CsLxHLy7Ln9tqIPCBru0f7mrXA8uArcQfM7Yw89QSgUeDq4TAkpxxzQxbV_mfVDNCqo22qgv2kElM8EaNBQKkENVPIvlb2lMa_nVDEOiEiXhIgSy6YB7VSvm6VSSWakOvUl/s1600/6.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEIC_Pb9Ka1CsLxHLy7Ln9tqIPCBru0f7mrXA8uArcQfM7Yw89QSgUeDq4TAkpxxzQxbV_mfVDNCqo22qgv2kElM8EaNBQKkENVPIvlb2lMa_nVDEOiEiXhIgSy6YB7VSvm6VSSWakOvUl/s320/6.jpg" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdXnYbhuTAhvgEgmyTY_1vi58SMI1h0R0dpIUY4ksTmNCJI33jRzggdC36jFnmccOCGHijg9J4B-4xAfiPw55PdyOSPT9nG2og1txsvciKpSirWFyWonFTK7Qa0gebrK4Yg78ZiRfu4lW-/s1600/7.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1067" data-original-width="1600" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhdXnYbhuTAhvgEgmyTY_1vi58SMI1h0R0dpIUY4ksTmNCJI33jRzggdC36jFnmccOCGHijg9J4B-4xAfiPw55PdyOSPT9nG2og1txsvciKpSirWFyWonFTK7Qa0gebrK4Yg78ZiRfu4lW-/s320/7.jpg" width="320" /></a></div>
<br />
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com15tag:blogger.com,1999:blog-5593586859352074500.post-40336481709887574822014-02-21T08:09:00.002-08:002014-02-21T08:25:09.279-08:00Kurs programowania na Androida w formacie PDF i ebookW związku z prośbą czytelników udostępniam kurs w formacie pdf i mobi.<br />
<br />
<a href="http://jsystems.pl/storage/kurs_android/ebook/ebook-android.pdf">Wersja PDF</a><br />
<a href="http://jsystems.pl/storage/kurs_android/ebook/ebook-android.mobi">Wersja MOBI</a><br />
<a href="http://jsystems.pl/storage/kurs_android/ebook/zrodla.rar">Kody źródłowe</a><br />
<br />
W publikacji znajduje się licencja użytkowania. Proszę się z nią zapoznać.andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com36tag:blogger.com,1999:blog-5593586859352074500.post-11702402988470272732014-02-17T12:47:00.003-08:002014-02-17T12:48:27.547-08:00Bezpłatny kurs programowania na platformę AndroidTutaj znajdziecie mój bezpłatny kurs programowania na platformę Android :)<br />
<br />
<a href="http://andrzejklusiewicz-android.blogspot.com/p/bezpatny-kurs-programowania-android-java.html">http://andrzejklusiewicz-android.blogspot.com/p/bezpatny-kurs-programowania-android-java.html</a>andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com11tag:blogger.com,1999:blog-5593586859352074500.post-59686843776688065902014-02-17T12:03:00.001-08:002014-02-18T09:04:34.624-08:00Bluetooth – czyli niebieskie pogaduszki<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/bluetooth.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br /><br />
<br />
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:<br />
<br />
<uses-permission android:name="android.permission.BLUETOOTH" /><br />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><br />
<br />
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).<br />
<br />
Napiszemy aplikację która będzie w stanie :<br />
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. <br />
Wyświetlić na konsoli listę sparowanych urządzeń<br />
Wykryć inne urządzenia pozwalające się wykryć (czyli rozgłaszające fakt swojego istnienia).<br />
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.<br />
Połączyć się jako client do innego urządzenia na którym będzie uruchomiony serwer. <br />
<br />
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ć.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS0L9ysYzl36WxjQdYRHkWLGFzf3Xj31qJx7IU6KGE39WY7knNJ_U-y0hap94w28Q04bgKQ6Y9vmbThBSAp0Xq9ouAA4XGlxU6AU9pW3Zw9I76w89dDsmudh7awYw-trs2R6uIe72mLJO1/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS0L9ysYzl36WxjQdYRHkWLGFzf3Xj31qJx7IU6KGE39WY7knNJ_U-y0hap94w28Q04bgKQ6Y9vmbThBSAp0Xq9ouAA4XGlxU6AU9pW3Zw9I76w89dDsmudh7awYw-trs2R6uIe72mLJO1/s1600/1.png" /></a></div>
<br />
<br />
<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeZqdizT7Ia19n8Fgpuo0MlcsGS1Zp9M2gjKzyfwJ30C5BDIZtupA_Qc-40W2tYuR8nZiqMdBRqxyOhWJ1IfNRCIDupw4qKs_52DHx3Yh-QJ5Bxrbk88AHoKcLUKmPTC5F7wC7NN3_OsKW/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeZqdizT7Ia19n8Fgpuo0MlcsGS1Zp9M2gjKzyfwJ30C5BDIZtupA_Qc-40W2tYuR8nZiqMdBRqxyOhWJ1IfNRCIDupw4qKs_52DHx3Yh-QJ5Bxrbk88AHoKcLUKmPTC5F7wC7NN3_OsKW/s1600/2.png" /></a></div>
<br />
<br />
Dalej w metodzie onCreate podpinam wywołania metod realizujących poszczególne funkcjonalności do przycisków.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKwvR5UH5VxdJ4_QpWLYewiYxdSLDUx_SB9SkVZvltO83LtQvaqXK38q-0XLB73-oulRxr53I6NFw80crj4TL9WGqlUXWGmWoAsThu6QA_w6tArPyirlEjHlxBQnd9DhHjjVQQQ-y_s-PD/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKwvR5UH5VxdJ4_QpWLYewiYxdSLDUx_SB9SkVZvltO83LtQvaqXK38q-0XLB73-oulRxr53I6NFw80crj4TL9WGqlUXWGmWoAsThu6QA_w6tArPyirlEjHlxBQnd9DhHjjVQQQ-y_s-PD/s1600/3.png" /></a></div>
<br />
<br />
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).<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDBx5g-gHewoTYNaO2deZEeV-hUsBqksxXcZJOwQZ3wCI7gBMXd4bHAVl_p5FFGNXENVZ8pizw7gKJ510NQ3eq_MhGKM2BpjvK5MU_Z5PxNgYxVNnjeEDPYlB-1W6uImU4SzkBL5NKZQ8P/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDBx5g-gHewoTYNaO2deZEeV-hUsBqksxXcZJOwQZ3wCI7gBMXd4bHAVl_p5FFGNXENVZ8pizw7gKJ510NQ3eq_MhGKM2BpjvK5MU_Z5PxNgYxVNnjeEDPYlB-1W6uImU4SzkBL5NKZQ8P/s1600/4.png" /></a></div>
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDsuFz9I8wVCcjkjHHYVgwQdfjArZ0k2hVuNudkIkFj3iYj4wWq8fS-EvecC-PRdKwbojpYwvhIJY3OtQZeabFc95KWACEOfsfc5XapS1UuS6ZALeSwwMFfGOFOCuTtLpwjV_dsKp9onDA/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDsuFz9I8wVCcjkjHHYVgwQdfjArZ0k2hVuNudkIkFj3iYj4wWq8fS-EvecC-PRdKwbojpYwvhIJY3OtQZeabFc95KWACEOfsfc5XapS1UuS6ZALeSwwMFfGOFOCuTtLpwjV_dsKp9onDA/s1600/5.png" /></a></div>
<br />
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.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmKApA5c5zj_qc5nxoHOq7hBfu5KRhmHjj_laePTAcIrNGNa3k-jklNuw3l7zQ-vY7GtDEhb1C_2UbtFpTItE3xqmlVYxkAHQ7CAEa8PrQnQaOP-qfO_CVDaqqeJd6ZWfc7zLNrZkOcVyO/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmKApA5c5zj_qc5nxoHOq7hBfu5KRhmHjj_laePTAcIrNGNa3k-jklNuw3l7zQ-vY7GtDEhb1C_2UbtFpTItE3xqmlVYxkAHQ7CAEa8PrQnQaOP-qfO_CVDaqqeJd6ZWfc7zLNrZkOcVyO/s1600/6.png" /></a></div>
<br />
<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUN_Udv2H0F3OG_uT0aODUidzN0VPVdabW2eJzpx-6pT0ADoswqc-eQ5_MsOExMSjd4ZTEhYGaGPB9SP_TSlrcGQ7dH7aIaK6tD7nyX06x95s-0QJkWiEkUiDCZAltJoSs3le9G42vxgs8/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUN_Udv2H0F3OG_uT0aODUidzN0VPVdabW2eJzpx-6pT0ADoswqc-eQ5_MsOExMSjd4ZTEhYGaGPB9SP_TSlrcGQ7dH7aIaK6tD7nyX06x95s-0QJkWiEkUiDCZAltJoSs3le9G42vxgs8/s1600/7.png" /></a></div>
Łą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 ;)) :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5XViRcmIIe3jWeMPTFDXgx5-b2Yvg2gFQMDcNhuYde-0gNNtAXR_wWAofGXpkDto2CA_aVJGzInGJLOa3b3C6IqP5E1K9b77yPvRTSPn0BHEw_IYS5_duG1I3Af4RUlsItqyKLaLU8yUb/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5XViRcmIIe3jWeMPTFDXgx5-b2Yvg2gFQMDcNhuYde-0gNNtAXR_wWAofGXpkDto2CA_aVJGzInGJLOa3b3C6IqP5E1K9b77yPvRTSPn0BHEw_IYS5_duG1I3Af4RUlsItqyKLaLU8yUb/s1600/8.png" /></a></div>
<br />
<br />
W ramach tej klasy mamy jeszcze metodę pokazSparowane():<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWNh9EI19jS6SCXoGmxOvAl1EIQuVWu9flfUpq0A6PUF3xFQ4p5y3IhNqFj57AAbYzfL8OuBS-dxp639SlChAyC59TFIFXuX5fbEyl5mPq1g7BNnZtFp0meqNwUoExOGiLiderSH92w-iL/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWNh9EI19jS6SCXoGmxOvAl1EIQuVWu9flfUpq0A6PUF3xFQ4p5y3IhNqFj57AAbYzfL8OuBS-dxp639SlChAyC59TFIFXuX5fbEyl5mPq1g7BNnZtFp0meqNwUoExOGiLiderSH92w-iL/s1600/9.png" /></a></div>
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR07k8T_NI89CH2uL9ebnfFsuWX6kbNtJ-OUpgR1rFIj47qaJ7Q7n4Hn-xSwBDIcOZFe6YbQUc7MFbE-NyWEaHBssCpu4vNl5FMuMh8OZtjxEcrhJjDvwkWmpCdlXAb8PuE8punbMIpySw/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhR07k8T_NI89CH2uL9ebnfFsuWX6kbNtJ-OUpgR1rFIj47qaJ7Q7n4Hn-xSwBDIcOZFe6YbQUc7MFbE-NyWEaHBssCpu4vNl5FMuMh8OZtjxEcrhJjDvwkWmpCdlXAb8PuE8punbMIpySw/s1600/10.png" /></a></div>
<br />
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.<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7pDUzKeFeva-UHO73REAlsBARQO3eCDjNXDtQCUVXgULt5Es9LEkIxGLB5qsbKTcpU89VwMYT1JJuZ1WQa4C0PxXieez5cFIYgGBozw7S-EleBtLoymuvpEoov4nD4W6L_7pxzCeBxY4e/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7pDUzKeFeva-UHO73REAlsBARQO3eCDjNXDtQCUVXgULt5Es9LEkIxGLB5qsbKTcpU89VwMYT1JJuZ1WQa4C0PxXieez5cFIYgGBozw7S-EleBtLoymuvpEoov4nD4W6L_7pxzCeBxY4e/s1600/11.png" /></a></div>
<br />
<br />
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 <br />
550e8400-e29b-41d4-a716-446655440000<br />
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!”. <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaadkDYImt9YlMRi3PjLdi-xxErKU5P3siCyr5tGhw-e9b65NWqrp96x2cO33hSRCd20L8XFlB9kMxtok0VwVEyveP7ZELIqCmrU8d4-YMq6wUEkOVDKxfl8n5riVLEBhqfI1Vfm8t7pA6/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaadkDYImt9YlMRi3PjLdi-xxErKU5P3siCyr5tGhw-e9b65NWqrp96x2cO33hSRCd20L8XFlB9kMxtok0VwVEyveP7ZELIqCmrU8d4-YMq6wUEkOVDKxfl8n5riVLEBhqfI1Vfm8t7pA6/s1600/12.png" /></a></div>
<br />
<br />
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ł.<br />
Uruchamiam teraz program na obu urządzeniach, telefon robi za serwer, tablet za clienta. <br />
<br />
Efekty działania serwera:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4m_L1MUIn1RzKQ8Tuzytyq72mxZTpbGA9jwBLg0Wg8fJjUbyZ1EpYtdjm8Qfam5GL9ADL8Gj5Y6DAm_IATL5RMrzJsrK_SfKMtFaCkhhPMQ6_gRFddFzwS35GAJ_dHf5k8FiH65Apu424/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj4m_L1MUIn1RzKQ8Tuzytyq72mxZTpbGA9jwBLg0Wg8fJjUbyZ1EpYtdjm8Qfam5GL9ADL8Gj5Y6DAm_IATL5RMrzJsrK_SfKMtFaCkhhPMQ6_gRFddFzwS35GAJ_dHf5k8FiH65Apu424/s1600/13.png" /></a></div>
<br />
<br />
<br />
Po stronie clienta:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAl7Fu3Pwy2PRro3J5y6gFf7UOOnQJ2UOFt5413qWTZmVb8lxDEz4j81jygkbqZHoWWIcoWxKMSSNdn0qm31g0Zs2TwhhS068pc3JfGedwr2usWJGGSmZieX85TAGFVJJ1OfLWP3Io1j3G/s1600/14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAl7Fu3Pwy2PRro3J5y6gFf7UOOnQJ2UOFt5413qWTZmVb8lxDEz4j81jygkbqZHoWWIcoWxKMSSNdn0qm31g0Zs2TwhhS068pc3JfGedwr2usWJGGSmZieX85TAGFVJJ1OfLWP3Io1j3G/s1600/14.png" /></a></div>
<br />
<br />
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ą.<br />
<br />
<br />
<br />
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEge8IhOVOmzrbVIDfZalqiWoGWUfW6PVrhoIC_MUgEHTjOZKcJLdGBZhOzwJfYXgVZXMIQgKCZzywjeeyYjQFO5zxsSa5uVsgC2hyphenhyphendybs6wVr87FOUZCKfA16RyM9rreVZkkc_L5Qtg_B3j/s1600/6.png" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><br /></a>
<br />
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com30tag:blogger.com,1999:blog-5593586859352074500.post-59710513710322330802014-02-17T11:45:00.005-08:002014-02-18T09:04:17.314-08:00Stosowanie usług sieciowych (Web Services) z poziomu Androida<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/webservices.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
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 :<br />
<br />
<uses-permission android:name="android.permission.INTERNET" /> <br />
<br />
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 : <br />
StrictMode$AndroidBlockGuardPolicy<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0qidm_VWZSQ3aSEWdPnwDg2NzOdglXaUpIAgjym8NvWfj1Aoyso7uWR6y5O0m_eJgSwMwjRwBWrlzKjsV5PHDvuHWBpm-zEwj6NDtdepViu-lKcfpFhMpryzwKyXzOA2aI9PCXJi5xkq9/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0qidm_VWZSQ3aSEWdPnwDg2NzOdglXaUpIAgjym8NvWfj1Aoyso7uWR6y5O0m_eJgSwMwjRwBWrlzKjsV5PHDvuHWBpm-zEwj6NDtdepViu-lKcfpFhMpryzwKyXzOA2aI9PCXJi5xkq9/s1600/1.png" /></a></div>
<br />
<br />
Wynik wyświetlony na konsoli:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXHewhLCQ_E8W79-NkaoPv45N26yubQafDfz1RAu_daQ4F62Dtg1I2zPieUZgGG6T5Q46X7nrGgS95MWZBBdH1A2WKXbT05cK1LW_QmN5BIO5jDk6hiJ03vaXoPgz0p8sbwIuxkKfPmjQj/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXHewhLCQ_E8W79-NkaoPv45N26yubQafDfz1RAu_daQ4F62Dtg1I2zPieUZgGG6T5Q46X7nrGgS95MWZBBdH1A2WKXbT05cK1LW_QmN5BIO5jDk6hiJ03vaXoPgz0p8sbwIuxkKfPmjQj/s1600/2.png" /></a></div>
<br />
<br />
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.<br />
<br />
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.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfFMM1L_e1UjS-hD37_sXuX5SYGpyCDkwfOXpvo-7ntieL6ME-9PYS6krIF1YEPG6Nl7Lqixsk_yK6QfUxL55-58ruUNebBnu_N4hAwRRRYUfwQzpNU0dTbVvmZ0JikweHeV6FLZ0pQZtJ/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfFMM1L_e1UjS-hD37_sXuX5SYGpyCDkwfOXpvo-7ntieL6ME-9PYS6krIF1YEPG6Nl7Lqixsk_yK6QfUxL55-58ruUNebBnu_N4hAwRRRYUfwQzpNU0dTbVvmZ0JikweHeV6FLZ0pQZtJ/s1600/3.png" /></a></div>
<br />
<br />
<br />
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<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxFcrqNUuE-VCVTZ9_mMkKe2txTaYV1siGc4zeq73IDuEK4q9mnZyXqeMcoCoakhTqRPRRJLuwYOmgC71ekyb0Ho78gtdt_85l0ixlOsGRSVgYo342WEYOpvbmwmE9KhYhWD2DQvMbBpBB/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhxFcrqNUuE-VCVTZ9_mMkKe2txTaYV1siGc4zeq73IDuEK4q9mnZyXqeMcoCoakhTqRPRRJLuwYOmgC71ekyb0Ho78gtdt_85l0ixlOsGRSVgYo342WEYOpvbmwmE9KhYhWD2DQvMbBpBB/s1600/4.png" /></a></div>
<br />
<br />
Metoda doInBackground korzysta z przed momentem opisanych metod i odbiera obiekt DOM. Metoda onCreate aktywności pozostaje bez zmian:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlzgqYHjLxvCaSflFvNXHtgFAOzUyc0tiUtwMd4ZANGKgupjE688uNscOr1ncnJ5RadGEz0xMciBkbUGS_XTdAAX7Ct26njUcq2VWSYtok6UDLQkPX_r9R9rOjYOzAd7fRplLUHyS4dRQ_/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjlzgqYHjLxvCaSflFvNXHtgFAOzUyc0tiUtwMd4ZANGKgupjE688uNscOr1ncnJ5RadGEz0xMciBkbUGS_XTdAAX7Ct26njUcq2VWSYtok6UDLQkPX_r9R9rOjYOzAd7fRplLUHyS4dRQ_/s1600/5.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com26tag:blogger.com,1999:blog-5593586859352074500.post-58160064453511840552014-02-17T11:42:00.002-08:002014-02-18T09:01:29.849-08:00Nagrywanie video<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/nagrywanie_video.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
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.<br />
<br />
<?xml version="1.0" encoding="utf-8"?><br />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"<br />
android:orientation="vertical"<br />
android:layout_width="fill_parent"<br />
android:layout_height="fill_parent"<br />
><br />
<LinearLayout<br />
android:orientation="horizontal"<br />
android:layout_width="fill_parent"<br />
android:layout_height="fill_parent"<br />
><br />
<SurfaceView<br />
android:id="@+id/videoview"<br />
android:layout_width="360px"<br />
android:layout_height="240px"/><br />
<Button<br />
android:id="@+id/mybutton"<br />
android:layout_width="wrap_content"<br />
android:layout_height="wrap_content"<br />
android:text="Nagrywaj"<br />
/><br />
</LinearLayout><br />
</LinearLayout><br />
<br />
<br />
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.<br />
<br />
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/><br />
<br />
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.<br />
<br />
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com10tag:blogger.com,1999:blog-5593586859352074500.post-19720870279461020092014-02-17T11:41:00.007-08:002014-02-18T09:00:54.685-08:00Podstawowa grafika 2D<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/grafika_2d.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"><br /></span></span></span>
<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjotX3kXt-LDTpQLmqddHUXqppHk08pCn3PfpfmWuc8sbG4Spm2iGIjLmN1lGFHv2mTT1Edug0iN1vEi_99vM01pSO2645VU7NEBlmraxpVbu5tGvuvvgxFQQCGPxJ0KwwzD6zrcU2k-yOb/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjotX3kXt-LDTpQLmqddHUXqppHk08pCn3PfpfmWuc8sbG4Spm2iGIjLmN1lGFHv2mTT1Edug0iN1vEi_99vM01pSO2645VU7NEBlmraxpVbu5tGvuvvgxFQQCGPxJ0KwwzD6zrcU2k-yOb/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
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.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwa5Me8ZJtQVZfeVdMv3Qi9Tqs-o2aCPkg5W2iJSdhmuU2a24U0ASOOQmPV48E4voEu9_vlKn80tgesIux_mM8367JqTNa0l7QV2k8y0frPg-KDI3F9OC-JWuXSYLFEQMgBL4yzk5YLwA7/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwa5Me8ZJtQVZfeVdMv3Qi9Tqs-o2aCPkg5W2iJSdhmuU2a24U0ASOOQmPV48E4voEu9_vlKn80tgesIux_mM8367JqTNa0l7QV2k8y0frPg-KDI3F9OC-JWuXSYLFEQMgBL4yzk5YLwA7/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
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”.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeyaWR-KQ7Pq9ySsLoa1lQ6zOFSjyygOiyJ8fNVCZBVPtBjPtQ7Qzl-O37qg0yy1v2KQB6FUILrtwRKmsQ6_5mGjSDXhyphenhyphensU865SxXWdP_5b7FTbatI-aQwg6AEzpd3rSJxvdhS8rT4wxsR/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeyaWR-KQ7Pq9ySsLoa1lQ6zOFSjyygOiyJ8fNVCZBVPtBjPtQ7Qzl-O37qg0yy1v2KQB6FUILrtwRKmsQ6_5mGjSDXhyphenhyphensU865SxXWdP_5b7FTbatI-aQwg6AEzpd3rSJxvdhS8rT4wxsR/s1600/3.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
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.<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAgGAuNU7dZdY-bAEc551Vh23lrbKNkf0J2prAotxclodm7c32qTPpvUdUf_CIYmRVz2CAUxMKz2OgapJjdGhZlpzyaSjxRVox5PUMqE2PAPznbPEFBZpT-918bXxId5z8i5R1BEOdLge-/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiAgGAuNU7dZdY-bAEc551Vh23lrbKNkf0J2prAotxclodm7c32qTPpvUdUf_CIYmRVz2CAUxMKz2OgapJjdGhZlpzyaSjxRVox5PUMqE2PAPznbPEFBZpT-918bXxId5z8i5R1BEOdLge-/s1600/4.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Efekt:<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPYTCWq2t2HIN632HsoinS4Lyl8al_QUAbgmly8bjEsUxCaATcMKkeZ7ZDLiXcnmzFxg1X4nVS3kzQDW-E0kW9b_pmF3UViRofHpydmeIE5WNQ9wwGj3YJpvEX_AXrJzUNwhYwxnEY0Pdl/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPYTCWq2t2HIN632HsoinS4Lyl8al_QUAbgmly8bjEsUxCaATcMKkeZ7ZDLiXcnmzFxg1X4nVS3kzQDW-E0kW9b_pmF3UViRofHpydmeIE5WNQ9wwGj3YJpvEX_AXrJzUNwhYwxnEY0Pdl/s1600/5.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
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ę:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOSk3YkrNZXl9yxn2TKHU20MAsuAisCdoLNCu2oGDTEtwp5-Ao5gJxcvcq_pvRWRDdC4oZOoMyJ5LuADgaOEZ6Gp5k9LMmbGzGEc3tql3Yl9iPGr5K07JklUOdhjVpPQosW5heEMtrBQuL/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOSk3YkrNZXl9yxn2TKHU20MAsuAisCdoLNCu2oGDTEtwp5-Ao5gJxcvcq_pvRWRDdC4oZOoMyJ5LuADgaOEZ6Gp5k9LMmbGzGEc3tql3Yl9iPGr5K07JklUOdhjVpPQosW5heEMtrBQuL/s1600/6.png" /></a></div>
<br />
<br />
Efekt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwLyhquY702EsZc6_y3FYX1UlpT2NzcAtZV-Dr-dGdD8JLZqcON8ipD0HJENAcyphBbr3c7Nsa5-nhOcGhEUzb8YiM81wN8bD98xWDqRJpYN9aMxqXo5mdT2A_ImvKpU6BRHu5sNCtXxz3/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwLyhquY702EsZc6_y3FYX1UlpT2NzcAtZV-Dr-dGdD8JLZqcON8ipD0HJENAcyphBbr3c7Nsa5-nhOcGhEUzb8YiM81wN8bD98xWDqRJpYN9aMxqXo5mdT2A_ImvKpU6BRHu5sNCtXxz3/s1600/7.png" /></a></div>
<br />
<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmwmjmDcoBAsdXWT0NR6E39iyXc453PmCk0gaj5qAJ7rmVHFTLgvU2F8WV5vdBNinxe1rwDnz2PaNVbRpdvp-pje_1hFVVCPUyyzmrAwLI6oShTF72VASrDwYWTaKuTpvjhfcZQ1XS91OT/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmwmjmDcoBAsdXWT0NR6E39iyXc453PmCk0gaj5qAJ7rmVHFTLgvU2F8WV5vdBNinxe1rwDnz2PaNVbRpdvp-pje_1hFVVCPUyyzmrAwLI6oShTF72VASrDwYWTaKuTpvjhfcZQ1XS91OT/s1600/8.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Efekt:<br />
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhopAIlKGmCNioZbi6A8KGpJA4O_FIQnZ6hM-cvGF1yCIjx4G2e9_cyz09hIGkwUss3oCBX5Y0MJ5N-CABTXUsOkoarPgULzai3tuzb54FvPGgbXE6ZqdSXtf2ezDkAyezNs8FsZBYjIs6q/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhopAIlKGmCNioZbi6A8KGpJA4O_FIQnZ6hM-cvGF1yCIjx4G2e9_cyz09hIGkwUss3oCBX5Y0MJ5N-CABTXUsOkoarPgULzai3tuzb54FvPGgbXE6ZqdSXtf2ezDkAyezNs8FsZBYjIs6q/s1600/9.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
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 :) ).<br />
Stworzyłem nowy projekt, wszystko na identycznej zasadzie jak w poprzednim przykładzie. Różni się tylko zawartością metody onDraw:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUd2ZmvnMfWKWkpxqerobpWvNmS3H8pzPpBVaeg3usO4RlbhWzp-LaImC6NDhjYO45tscaq7N93ZTRnPSLsw3Kacoxyt98Rjiqb6AVt1jW5tOKnACk9Zlae2es4N5vfzTEEGgE6oxPLxdQ/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUd2ZmvnMfWKWkpxqerobpWvNmS3H8pzPpBVaeg3usO4RlbhWzp-LaImC6NDhjYO45tscaq7N93ZTRnPSLsw3Kacoxyt98Rjiqb6AVt1jW5tOKnACk9Zlae2es4N5vfzTEEGgE6oxPLxdQ/s1600/10.png" /></a></div>
<br />
<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEide389s0odWYtQDgkTLQ6LVlBYPZB_z76zGxZpclaUxO8L5OoXHn1bmMS0ejYDiMrOpWICmqCE4ISvObIt7TSmca5YYYbNl_ipyle33FD6iRrDW_22pjbZZqIjXFGRbF8sRAgCWtFqEcvN/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEide389s0odWYtQDgkTLQ6LVlBYPZB_z76zGxZpclaUxO8L5OoXHn1bmMS0ejYDiMrOpWICmqCE4ISvObIt7TSmca5YYYbNl_ipyle33FD6iRrDW_22pjbZZqIjXFGRbF8sRAgCWtFqEcvN/s1600/11.png" /></a></div>
<br />
<br />
Rysowanie kółek nie sprawia również wielu problemów. Stworzyłem kolejny projekt, tym razem metoda onDraw wygląda tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKWdJHPlOuxsLXKrzAkal6q31ZyoKEHi7zFKZN-81KFdTwo4j78mSz_Hb_Qebtc9ZhcG6O8-ReGEWFPI4VNb_uV4bbMLWB9doAk8mVjqNuVonkYyBBgjLNV8xd44a17uuZRyoxtLJJhDXf/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKWdJHPlOuxsLXKrzAkal6q31ZyoKEHi7zFKZN-81KFdTwo4j78mSz_Hb_Qebtc9ZhcG6O8-ReGEWFPI4VNb_uV4bbMLWB9doAk8mVjqNuVonkYyBBgjLNV8xd44a17uuZRyoxtLJJhDXf/s1600/12.png" /></a></div>
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 :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIpR3IGj1hOh1CeU-5ZStPLsm0w3ZhOjXD8FZyt3fskBENPvQZ85BP3a5_8xJztycD1IGA3g87ZVOTOQRxfRjnx3GpMBu2oAx2WJJVTRuOGVEIjUpPhXM3T7yYsrlaa-j42i2QfnnSUBia/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIpR3IGj1hOh1CeU-5ZStPLsm0w3ZhOjXD8FZyt3fskBENPvQZ85BP3a5_8xJztycD1IGA3g87ZVOTOQRxfRjnx3GpMBu2oAx2WJJVTRuOGVEIjUpPhXM3T7yYsrlaa-j42i2QfnnSUBia/s1600/13.png" /></a></div>
<br />
<br />
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.andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com15tag:blogger.com,1999:blog-5593586859352074500.post-51223062444262867992014-02-17T11:29:00.007-08:002014-02-18T09:00:26.633-08:00Odtwarzanie video<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/odtwarzanie_video.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDSrtwcZ1kTUx1jZrj8Gx3040H5-K_CzCRab1mSHFMRHUDO_ehGKkM9ne5jvBieE9Hua4Uteq1yDCJIDlPZDG2XTGkaf2lp02GmWuyhHVjnEuofx6W0Jc1zu7WVhkOwHXNgzftr6A_Y76s/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDSrtwcZ1kTUx1jZrj8Gx3040H5-K_CzCRab1mSHFMRHUDO_ehGKkM9ne5jvBieE9Hua4Uteq1yDCJIDlPZDG2XTGkaf2lp02GmWuyhHVjnEuofx6W0Jc1zu7WVhkOwHXNgzftr6A_Y76s/s1600/1.png" /></a></div>
<br />
W kolejnym kroku przyklejam na aktywności komponent VideoView:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC0EY1d5cFF-XAV2fJk2pEpKobV-62ylnC1_h5Qr9lJgRNddfrdL84ACET6hnVouiA34m0MiXuyaKqgKAQrYErv9h7aoH3qsFBrG9EYyTOAymiEYJVmJcHyyHbVjEoWvQfPXNOiq6R4E_4/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC0EY1d5cFF-XAV2fJk2pEpKobV-62ylnC1_h5Qr9lJgRNddfrdL84ACET6hnVouiA34m0MiXuyaKqgKAQrYErv9h7aoH3qsFBrG9EYyTOAymiEYJVmJcHyyHbVjEoWvQfPXNOiq6R4E_4/s1600/2.png" /></a></div>
<br />
<br />
Następnie wprowadzam kilka zmian w metodzie onCreate aktywności głównej:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYqGY2PxW_snGuEESdT2NlXkoGD6NHsmfN6jdDXN-X7GsxcPRdFNlGAu3Mq3MrZP0YM3IW3iAdeIjLKMsaQaF9blJIIhHJl-NcP9dhzIiE4K6KN0SmSPqXqI88TwTRdHZB2wguZSAbCTiK/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiYqGY2PxW_snGuEESdT2NlXkoGD6NHsmfN6jdDXN-X7GsxcPRdFNlGAu3Mq3MrZP0YM3IW3iAdeIjLKMsaQaF9blJIIhHJl-NcP9dhzIiE4K6KN0SmSPqXqI88TwTRdHZB2wguZSAbCTiK/s1600/3.png" /></a></div>
<br />
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. <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimRYgWk8OfhtLkU9vQrZv5KEWdsyz18YXEzreO9AfuGJyfqBqgPO1XgwPd5p0KlREMjOqfXd6lX0VGcXNLEn7uNNUBDOS7JHH_5Tge7Tliyq89Hi_8HPJxUUyFGN7wh4hoPFQsWpsthwAO/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimRYgWk8OfhtLkU9vQrZv5KEWdsyz18YXEzreO9AfuGJyfqBqgPO1XgwPd5p0KlREMjOqfXd6lX0VGcXNLEn7uNNUBDOS7JHH_5Tge7Tliyq89Hi_8HPJxUUyFGN7wh4hoPFQsWpsthwAO/s1600/4.png" /></a></div>
<br />
<br />
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.<br />
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.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgss4K2DG9M9qCsDn30YTmSxKIE2cY1fYMAZK1Ldv4EhJX0RSBHP4KdKQnHUA71ZUPJID1r3iciYRBZG7oNXiTCFcfWWXhASYUfSpP31lFQUvNE-MhZ4N2pzenmCNiZBViID5MwZJdMr8cI/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgss4K2DG9M9qCsDn30YTmSxKIE2cY1fYMAZK1Ldv4EhJX0RSBHP4KdKQnHUA71ZUPJID1r3iciYRBZG7oNXiTCFcfWWXhASYUfSpP31lFQUvNE-MhZ4N2pzenmCNiZBViID5MwZJdMr8cI/s1600/5.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com12tag:blogger.com,1999:blog-5593586859352074500.post-46421011227710037162014-02-17T11:26:00.003-08:002014-02-18T08:59:36.333-08:00Odtwarzanie dźwięku<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/odtwarzanie_dzwieku.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
<br />
Zaczniemy od najprostszego przykładu. Program zaraz po uruchomieniu odtworzy plik MP3 który będzie znajdował się w ramach samego programu.<br />
<br />
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<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcs48uyRZxf-k0z53hgspK_fCRhaaMH6rBfG_PzxjTW1sikvGqRniZ13ORIJBGbwXtxfJEJfbsh5SJq7-TsUHEqKJKEEwcErZ5TN2EQdi_mvoRmq6Qkjx1kwbsZVi9StjaUEt98aR5br-N/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcs48uyRZxf-k0z53hgspK_fCRhaaMH6rBfG_PzxjTW1sikvGqRniZ13ORIJBGbwXtxfJEJfbsh5SJq7-TsUHEqKJKEEwcErZ5TN2EQdi_mvoRmq6Qkjx1kwbsZVi9StjaUEt98aR5br-N/s1600/1.png" /></a></div>
<br />
<br />
Wszystko co wrzucimy do podkatalogu res, jest automagicznie rejestrowane jako zasób dostępny z poziomu kodu.<br />
Teraz już tylko mały szlif kodu głównej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr2errlTkcK_i1lcB2aBcMgeERLePflJDgFDTGVe7mCh3G7E0EtTuc8sPWnGp6kdY1HouPncEzoT9b0COn8qaghLwu87A-_rFoEFaadriUiIuWx0IqTsVSO9rx5q3IAYynJ7JWsZWuUDyQ/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhr2errlTkcK_i1lcB2aBcMgeERLePflJDgFDTGVe7mCh3G7E0EtTuc8sPWnGp6kdY1HouPncEzoT9b0COn8qaghLwu87A-_rFoEFaadriUiIuWx0IqTsVSO9rx5q3IAYynJ7JWsZWuUDyQ/s1600/2.png" /></a></div>
<br />
<br />
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.<br />
Linia 17 to uruchomienie odtwarzania zasobu któy wskazaliśmy w linii 16.<br />
W sytuacji gdyby plik muzyczny który chcemy odtwarzać znajdował się poza aplikacją, np. na karcie SD nasz kod wyglądać by musiał tak:<br />
<br />
MediaPlayer mp = new MediaPlayer();<br />
mp.setDataSource(”/sdcard/rammstein/raise_raise.mp3”);<br />
mp.prepare();<br />
mp.start();andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com13tag:blogger.com,1999:blog-5593586859352074500.post-55213593831797473122014-02-17T11:24:00.002-08:002014-02-18T08:59:00.816-08:00Wykorzystanie aparatu fotograficznego do robienia zdjęć<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/robienie_fotek.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyEQBpk8vtrpCsFDxzdcQ-ebD6HDWFLVwHqBDEk9ADuUz5q-7hBA_3GZdQGRwp39c6iG2H6g9H4HESBzxFQM3aAZslRQoVVcAnIcMtGXbObC3awCBdSrfxdp6Np7iCExciKQ197UE0qwoN/s1600/0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyEQBpk8vtrpCsFDxzdcQ-ebD6HDWFLVwHqBDEk9ADuUz5q-7hBA_3GZdQGRwp39c6iG2H6g9H4HESBzxFQM3aAZslRQoVVcAnIcMtGXbObC3awCBdSrfxdp6Np7iCExciKQ197UE0qwoN/s1600/0.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
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.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX3aaxSfX13Tf1l8SL4403ye8Rnd82R5kol_9a09PFkaQd2pzHsOz-QxrHnrk0hllj_k61o-n2TOt5D1mq7iypN5mpum9RW5giCLXcAgFL56AmhNdaMYLfo6kF73k7r6SHU2tpBTPFzZ9N/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhX3aaxSfX13Tf1l8SL4403ye8Rnd82R5kol_9a09PFkaQd2pzHsOz-QxrHnrk0hllj_k61o-n2TOt5D1mq7iypN5mpum9RW5giCLXcAgFL56AmhNdaMYLfo6kF73k7r6SHU2tpBTPFzZ9N/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
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:<br />
<br />
</application><br />
<br />
<uses-permission android:name="android.permission.CAMERA" /><br />
<br />
<br />
</manifest><br />
<br />
<br />
Po uruchomieniu i naciśnięciu przycisku pojawia nam się wbudowany programik do robienia fotografii:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ-34uWbSoXW05U4WoOxICDCPvDGZfRQlO9vuCszw3-KIULgFDEdTQHzOEsdTuMKqQ3Os-k00R6AcW6cwi23mfdJBAEMdrkX9GcArpwh4cbGllhDVawNjYvITxWKDCaaDik4egpZ3WBrlO/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQ-34uWbSoXW05U4WoOxICDCPvDGZfRQlO9vuCszw3-KIULgFDEdTQHzOEsdTuMKqQ3Os-k00R6AcW6cwi23mfdJBAEMdrkX9GcArpwh4cbGllhDVawNjYvITxWKDCaaDik4egpZ3WBrlO/s1600/2.png" /></a></div>
<br />
<br />
<br />
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:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhEPtMju5pPVeTP4IcCtSEcg_5L7gdMlAZDQcbs0j-8us0YNvXd9Fsofi8HH96oj_E4g1OW3cK74-3Ujm3oiJeBXewMm5riDda-JIrmAIX2SPLgFaIpgpQkw86gQD0K_1wPh7YeW5etGVr/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhEPtMju5pPVeTP4IcCtSEcg_5L7gdMlAZDQcbs0j-8us0YNvXd9Fsofi8HH96oj_E4g1OW3cK74-3Ujm3oiJeBXewMm5riDda-JIrmAIX2SPLgFaIpgpQkw86gQD0K_1wPh7YeW5etGVr/s1600/3.png" /></a></div>
<br />
<br />
Mogę tutaj anulować operację, powtórzyć robienie zdjęcia, lub zatwierdzić. Zatwierdzamy, a po powrocie z tej aktywności nasz ekran wygląda tak:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRI_8BWq4St60KeWl8-Y7TDgIbElyvgJuwPFfhkv-gjRh58nXeyYfxS5MHY1hky4r1AtKFuZ2BbjztgCRB5SMekKnq_VU2u15Qet9-CscRJYezm4Ho4JiIEcbICW4ab117T2j71_9-agf/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaRI_8BWq4St60KeWl8-Y7TDgIbElyvgJuwPFfhkv-gjRh58nXeyYfxS5MHY1hky4r1AtKFuZ2BbjztgCRB5SMekKnq_VU2u15Qet9-CscRJYezm4Ho4JiIEcbICW4ab117T2j71_9-agf/s1600/4.png" /></a></div>
<br />
<br />
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.<br />
<br />
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.<br />
<br />
<uses-permission android:name="android.permission.CAMERA" /><br />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><br />
<br />
<br />
Android udostępnia publicznie dostępny katalog (dostępny dla wszystkich aplikacji). Aby się do niego dobrać możemy zastosować np. taką konstrukcję:<br />
<br />
File katalog = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);<br />
<br />
Jak zawsze rozpoczynam pracę od przyklejenia komponentów:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivSZpN448r8PgIZz0J5xroaS3fUfl1W5wkFv42KhdUHf4aycAEW5YCJ5P6YVdcOPVSJKZ6-R4akGte466s7aPOOuJiDoQLlgXfpwAEyDKIChOsAeRk8rRD0nTKyQYOxL5xC51I-SV_ePVj/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEivSZpN448r8PgIZz0J5xroaS3fUfl1W5wkFv42KhdUHf4aycAEW5YCJ5P6YVdcOPVSJKZ6-R4akGte466s7aPOOuJiDoQLlgXfpwAEyDKIChOsAeRk8rRD0nTKyQYOxL5xC51I-SV_ePVj/s1600/5.png" /></a></div>
<br />
<br />
Dałem jeden guzik który uruchomi intencję, oraz jeden TextView na którym znajdzie się ścieżka do zrobionej fotografii.<br />
W aktywności, w metodzie onCreate podpinam referencję do komponentów i jako obsługę naciśnięcia przycisku wywołuję metodę obslugaGuzika().<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ2J8Yn2PwC0ZhYO_Gn7_8I-TVX_znEfULk_m0nByf7KyK5L5t08wqKp5ZtFIysY9tOhkyip5mqbYeqOtFefdgVdqTAk4yUz3E7xFnAxGQkC2vx6U8LjPr-SuiVG2jqL7Kw6XEIYSAM9Uc/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJ2J8Yn2PwC0ZhYO_Gn7_8I-TVX_znEfULk_m0nByf7KyK5L5t08wqKp5ZtFIysY9tOhkyip5mqbYeqOtFefdgVdqTAk4yUz3E7xFnAxGQkC2vx6U8LjPr-SuiVG2jqL7Kw6XEIYSAM9Uc/s1600/6.png" /></a></div>
<br />
<br />
Sama metoda „obslugaGuzika” jest przestawiona poniżej:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5lyTPHYxFgRZkMWemY4bnTx7GxZmEqeC6aeu-pM6E0egXC7HZ-8LI2izh0y4XDwPH-32b4I2XEqiIzwHOFeb4JFnjp_fQ1ayDLKRDe46hCzVuxRFymvegagfy6pramnl1dgWCfPBTTA6R/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj5lyTPHYxFgRZkMWemY4bnTx7GxZmEqeC6aeu-pM6E0egXC7HZ-8LI2izh0y4XDwPH-32b4I2XEqiIzwHOFeb4JFnjp_fQ1ayDLKRDe46hCzVuxRFymvegagfy6pramnl1dgWCfPBTTA6R/s1600/7.png" /></a></div>
<br />
<br />
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.<br />
Efekt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLQnH3kdb8hfK3_tpBMOyy7_gTi4LsSarAnPLbD98ti5NS6L7o-ajOmBIhee2ZpzlHw4QcPvJFTKEY5DAckMOgvp0jre9uiknKbY-LXpMKXP7xJsHcdpp9ZcTtoG2hpJuZwWJCXjRX791A/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLQnH3kdb8hfK3_tpBMOyy7_gTi4LsSarAnPLbD98ti5NS6L7o-ajOmBIhee2ZpzlHw4QcPvJFTKEY5DAckMOgvp0jre9uiknKbY-LXpMKXP7xJsHcdpp9ZcTtoG2hpJuZwWJCXjRX791A/s1600/8.png" /></a></div>
<br />
<br />
<br />
<br />
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.<br />
<br />
Stworzyłem nowy projekt i przykleiłem na ekranie button i imageView:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqSZxNL_oJw9iVO-1pZd84lRe9NnPJ9fCg_DflAWMkMfU54qKINdL-_K8fg94O0NHgCYYmHU8EgyP1BZ9dBrovtl2pUIASP3UeJllwntvZBs-80xhIviqzx_JIAF6MTcAI4Y67P7M5yxGK/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiqSZxNL_oJw9iVO-1pZd84lRe9NnPJ9fCg_DflAWMkMfU54qKINdL-_K8fg94O0NHgCYYmHU8EgyP1BZ9dBrovtl2pUIASP3UeJllwntvZBs-80xhIviqzx_JIAF6MTcAI4Y67P7M5yxGK/s1600/9.png" /></a></div>
<br />
<br />
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:<br />
<br />
<br />
<uses-permission android:name="android.permission.CAMERA"/><br />
<br />
<br />
Przechodzimy teraz do kodu. <br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwDVRCJWWgBZ5cG58VMF-FC8E1bqmbCP0ocAAEs5wB208j4RoTLXa1jiY5E1aYN-9pq6F89K9ZcQLSY-NBCjqKCF1nSNxXKktWxKB-bpAAlZ6VldPFYVcGEA0vtgKAJukfYwzydbfOAwpW/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwDVRCJWWgBZ5cG58VMF-FC8E1bqmbCP0ocAAEs5wB208j4RoTLXa1jiY5E1aYN-9pq6F89K9ZcQLSY-NBCjqKCF1nSNxXKktWxKB-bpAAlZ6VldPFYVcGEA0vtgKAJukfYwzydbfOAwpW/s1600/10.png" /></a></div>
<br />
<br />
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:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcMlj-UAUG_ThjSNrAtq_MTaTLLSDNYWkp3QH8PHRKAG9SW3WBFkeDVKyci8SwIgmQsoMa2y2pvliT4KNB9dLXKu8p2PFRV-6MqrpPH7WpW0XYD8DUjDTsMp652Pn7eD8WyumXE_hZb6SO/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcMlj-UAUG_ThjSNrAtq_MTaTLLSDNYWkp3QH8PHRKAG9SW3WBFkeDVKyci8SwIgmQsoMa2y2pvliT4KNB9dLXKu8p2PFRV-6MqrpPH7WpW0XYD8DUjDTsMp652Pn7eD8WyumXE_hZb6SO/s1600/11.png" /></a></div>
<br />
<br />
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:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOBcQf6H4IRM6Kf8ZBPmwaCB3Ckja69kjeYFlUKppg-qCxDZON_90xjWEJJzS5qB-IJzwhL3eQnsMZMV4ml0-CfDt1MJMlEYcxnNxGbaoDTo0rpB8TNFY_GHjjIKsFDORVDhX0bTgnDabb/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhOBcQf6H4IRM6Kf8ZBPmwaCB3Ckja69kjeYFlUKppg-qCxDZON_90xjWEJJzS5qB-IJzwhL3eQnsMZMV4ml0-CfDt1MJMlEYcxnNxGbaoDTo0rpB8TNFY_GHjjIKsFDORVDhX0bTgnDabb/s1600/12.png" /></a></div>
<br />
<br />
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:<br />
Implementujemy interfejs PictureCallback (linia 26)<br />
Podpięcie urządzenia pod obiekt klasy camera (linia 45)<br />
Wywołanie metody takePicture obiektu klasy Camera wtedy kiedy chcemy zrobić zdjęcie (linia 65)<br />
Implementacja automatycznie wywoływanej metody onPictureTaken i przetworzenie przekazanego nam przez argument zdjęcia (linia 94).<br />
<br />
Te elementy to nasz „engine”, reszta to poboczny osprzęt. <br />
<br />
Efekt po uruchomieniu i kliknięciu przycisku:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimYGu2E6CoriGPpSPXsRQpZOGiVfQr79MravAJbA17cK_tRWE-hozKcgxK41gzXdiIcH-EV7m5iG0u-1hfzWjG-KSOTHS0UBXzeZVdP8p7nQeTFGR80fXjeYzodnySrLhScbd3xC0X41Kq/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimYGu2E6CoriGPpSPXsRQpZOGiVfQr79MravAJbA17cK_tRWE-hozKcgxK41gzXdiIcH-EV7m5iG0u-1hfzWjG-KSOTHS0UBXzeZVdP8p7nQeTFGR80fXjeYzodnySrLhScbd3xC0X41Kq/s1600/13.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com20tag:blogger.com,1999:blog-5593586859352074500.post-22359100945497271172014-02-17T11:14:00.002-08:002014-02-18T08:58:11.029-08:00Odbieranie SMSów<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/odbieranie_smsow.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
<br />
Jeśli chcemy napisać aplikację która będzie reagowała na nadejście SMS, musimy ją zarejestrować jako taki odbiornik i w ten sposób zdeklarować że potrafi obsługiwać takie akcje. W tym celu,gdzieś pomiędzy tagami <Application> w pliku manifestu musimy wstawić taki fragment:<br />
<br />
<receiver android:name=".OdbiornikSMS" android:enabled="true"><br />
<intent-filter><br />
<action android:name="android.provider.Telephony.SMS_RECEIVED"/><br />
<category android:name="android.intent.category.DEFAULT"/><br />
</intent-filter><br />
</receiver><br />
<br />
Oczywiście w pierwszej linii podajemy nazwę swojej klasy która ma sesemesy odbierać. Wstawiając taki kawałek kodu do naszego pliku manifestu, deklarujemy że nasz program potrafi obsługiwać pewne operacje – np. w tym przypadku android.provider.Telephony.SMS_RECEIVED, ale równie dobrze może to być odbieranie nadchodzących połączeń czy obsługa wywołań otwarcia strony internetowej.<br />
Skoro deklarujemy, że jesteśmy w stanie obsługiwać odbieranie sms, to musimy jeszcze mieć do tego uprawnienia ;) Pamiętamy więc o dodaniu takiej linii do pliku manifestu:<br />
<br />
<uses-permission android:name="android.permission.RECEIVE_SMS"/><br />
<br />
<br />
Jeszcze kodzik który to wszystko obsłuży:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2bRMASEdhJzOzQq2jWWC1WyUQqEPyP-DwBs6-Fxxe2N4c2yZa_IyocZhy3A3eVk4yA3el7GwYkg-YE3JbpvmQUY_dj-m5RyNBgSW5pdTrrZpofvDc7Yudx0N2TxCQznWTD-uunkOue11/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjv2bRMASEdhJzOzQq2jWWC1WyUQqEPyP-DwBs6-Fxxe2N4c2yZa_IyocZhy3A3eVk4yA3el7GwYkg-YE3JbpvmQUY_dj-m5RyNBgSW5pdTrrZpofvDc7Yudx0N2TxCQznWTD-uunkOue11/s1600/1.png" /></a></div>
<br />
<br />
Musimy zaimplementować interfejs BroadcastReceiver i posiadać metodę onReceive w której określimy co ma się zdarzyć przy odbieraniu sms. Nie musisz się martwić że od teraz nasza aplikacja „zasłoni” domyślną obsługę odbierania smsów. Platforma Android w momencie odebrania SMS wywoła wszystkie aplikacje zarejestrowane jako odbiorniki smsów :)<br />
Taką funkcjonalność można fajnie wykorzystać do stworzenia systemu zarządzanego z dowolnego miejsca na ziemi poprzez SMS :) andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com7tag:blogger.com,1999:blog-5593586859352074500.post-42754448602151988932014-02-17T11:12:00.004-08:002014-02-18T08:57:43.981-08:00Wysyłanie wieloczęściowego SMSa<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/wysylanie_wieloczesciowego_smsa.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br /><br />
<br />
Wysyłanie wieloczęściowego SMSa jest bardzo podobne do wysyłania pojedynczego, z tą różnicą że smsa trzeba po drodze podzielić na kilka fragmentów, ale nawet do tego mamy dedykowaną gotową metodę. Zacznijmy od dodania niezbędnego uprawnienia:<br />
<br />
<uses-permission android:name="android.permission.SEND_SMS"/><br />
<br />
Potem przechodzimy do kodu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihznK28nhy2DjUR9zvchHvlopeH4Z2_7X6UzQelKXCQpM3-H7g9ezf6Briphw6Ym4qBOFRO3c9A4EykNSZS63m3EZLQnxtVfNlrHCsjYIcN-TT_EkZfeVYG-EIUm9c-2d06HwoRrF-qhXE/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihznK28nhy2DjUR9zvchHvlopeH4Z2_7X6UzQelKXCQpM3-H7g9ezf6Briphw6Ym4qBOFRO3c9A4EykNSZS63m3EZLQnxtVfNlrHCsjYIcN-TT_EkZfeVYG-EIUm9c-2d06HwoRrF-qhXE/s1600/1.png" /></a></div>
<br />
<br />
Obiekt klasy SmsManager jest elementem wysyłającym smsy. Zmienna odbiorca to numer telefonu na który chcemy wysłać wiadomość. Lista elementów fragmenty, zdefiniowana jako pusta to pojemnik do którego wrzucona zostanie wiadomość SMS podzielona na fragmenty. Zmienna wiadomosc służy do tymczasowego przechowania wiadomości o długości większej niż 160 znaków. W metodzie onCreate , w linii 28 odbieram instancję klasy SmsManager. W linii 29 wykorzystuję metodę divideMessage której podaję długą wiadomość, a ta zwraca nam ją już podzieloną na fragmenty do pojemnika „fragmenty”. Samo wysłanie wiadomości to wywołanie metody sendMultipartTextMessage, której podaję numer telefonu odbiorcy i wiadomość podzieloną na fragmenty. Po drodze (w linii 30) wyświetlam ilość elementów jakie wyjdą z naszej wiadomości. Wyszły mi cztery, co widać na poniższym obrazku:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjWv4yFfjvMHaJyw7nKKAFT10qyQTbe1bCGfbrBWCZ1Jk1dY5yV_Vb4c5iDmwwy3klwxEk-zvyHeaFU_qnAnV4wFAFavKIhHLp96150aFBwRkJOTPtCQA48coPlo9WOVtLI3-x7Cl8Ql37/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhjWv4yFfjvMHaJyw7nKKAFT10qyQTbe1bCGfbrBWCZ1Jk1dY5yV_Vb4c5iDmwwy3klwxEk-zvyHeaFU_qnAnV4wFAFavKIhHLp96150aFBwRkJOTPtCQA48coPlo9WOVtLI3-x7Cl8Ql37/s1600/2.png" /></a></div>
<br />
<br />
Wiadomość przychodzi jako całość. Pamiętamy oczywiście o włożeniu karty SIM do urządzenia z którego chcemy wysyłać smsy, ponieważ wbrew pozorom nie jest to metoda na darmowe wysyłanie smsów ;)andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com6tag:blogger.com,1999:blog-5593586859352074500.post-85218997665877903092014-02-17T11:10:00.006-08:002014-02-18T08:57:16.506-08:00Wysyłanie pojedynczego SMSa<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/wysylanie_pojedynczego_smsa.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
<br />
Wysyłanie SMSów w Androidzie jest bardziej niż proste. Do pliku manifestu dodajemy odpowiednie uprawnienie:<br />
<br />
<br />
<uses-permission android:name="android.permission.SEND_SMS"/><br />
<br />
<br />
A następnie przechodzimy do kodu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFBLv99R3ydQzZl65xdinmGYEmRXH6iuExOBgDElhBEJDlxPlFxz1u9VuI5TMqsve9h4g-Ib5m40jMVKgm47i3k-KqZFUP8o45Jgb-6CnjoQB7OD0KuW6g3MVw7XZI1LDf8mPzhwPKbZiy/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFBLv99R3ydQzZl65xdinmGYEmRXH6iuExOBgDElhBEJDlxPlFxz1u9VuI5TMqsve9h4g-Ib5m40jMVKgm47i3k-KqZFUP8o45Jgb-6CnjoQB7OD0KuW6g3MVw7XZI1LDf8mPzhwPKbZiy/s1600/1.png" /></a></div>
<br />
Obiekt klasy SmsManager to po prostu narzędzie służące do wysyłania SMS. Znaczenia pozostałych 2 zmiennych – odbiorca i wiadomosc nie trudno jest się domyślić. Przyjrzyjmy się teraz liniom 22 i 23. Metoda getDefault oddaje nam instancję klasy SmsManager. Metoda sendTextMessage służy do wysyłania SMSów składających się z nie więcej niż 160 znaków – tj. pojedynczy sms. Istnieje też metoda „sendMultipartTextMessage” która umożliwia wysłanie tekstu na który składa się więcej niż jeden SMS. Pierwszy parametr metody sendTextMessage to numer telefonu odbiorcy,trzeci to treść wiadomości. Musimy pamiętać, że urządzenie z którego wysyłamy wiadomości musi posiadać kartę SIM :)andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com10tag:blogger.com,1999:blog-5593586859352074500.post-67825955921203583002014-02-17T11:09:00.003-08:002014-02-18T08:56:45.046-08:00Czujnik zbliżeniowy<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/czujnik_zblizeniowy.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Obsługa czujnika zbliżeniowego jest bardzo podobna do omawianego wcześniej czujnika orientacji. Do ekranu przyklejam jeden TextView na którym będę wyświetlał wyniki:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeVJaRDVVAZXA2Z9Tso3-HF6CWibV61ps1pmoqLG0Xxbwaxe2lIIf-a5rOffdU-mYIb6tjX5M2En0xX7lT294n_-cT5dwhTPplhVhAwoKDcrstOYhsDkE06t6slRWLbpoe7wS9fq3p9j5N/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeVJaRDVVAZXA2Z9Tso3-HF6CWibV61ps1pmoqLG0Xxbwaxe2lIIf-a5rOffdU-mYIb6tjX5M2En0xX7lT294n_-cT5dwhTPplhVhAwoKDcrstOYhsDkE06t6slRWLbpoe7wS9fq3p9j5N/s1600/1.png" /></a></div>
<br />
<br />
<br />
Podobnie jak i przy czujniku orientacji implementuję interfejs SensorEventListener. Kod metody onCreate jest praktycznie taki sam jak i przy czujniku orientacji, z tą różnicą że przy rejestracji listenera jako parametr podajemy typ sensora Sensor.TYPE_PROXIMITY a nie TYPE_ORIENTATION. Podobnie jak i przy czujniku orientacji metoda onSensorChanged jest automatycznie wywoływana w momencie zmiany odczytów czujnika. W linii 33 wyświetlam na komponencie textView odczyt. Na urządzeniu Samsung ACE3 wyświelał tylko dwie wartości – 0, albo 5 jeśli zbliżyłem rękę na mniej niż ok 2cm.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_6g_M32QEKwUWVkMEfH-HmDEmGHfeYx2fbawZsraF6Q6S8UIxKnSgOvnwHx_-6A5KaXKZ5mjoHpmhOVXE5tMiAiEtWN_HisaErz6wTwqbVQZ9bwxQFD4QjNpVx-WF4owe2bsU9EsdXr4C/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_6g_M32QEKwUWVkMEfH-HmDEmGHfeYx2fbawZsraF6Q6S8UIxKnSgOvnwHx_-6A5KaXKZ5mjoHpmhOVXE5tMiAiEtWN_HisaErz6wTwqbVQZ9bwxQFD4QjNpVx-WF4owe2bsU9EsdXr4C/s1600/2.png" /></a></div>
<br />
<br />
W bardzo podobny sposób możemy wykorzystywać inne czujniki – np. czujnik temperatury (jeśli urządzenie takowy posiada). Wystarczyłoby w takim wypadku zamienić w linii 22 typ sensora z Sensor.TYPE_PROXIMITY na Sensor.TYPE_TEMPERATURE.andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com9tag:blogger.com,1999:blog-5593586859352074500.post-6803790088609648742014-02-17T11:07:00.006-08:002014-02-18T08:55:42.159-08:00Czujnik orientacji (poziomica + kompas )<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/czujnik_orientacji.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br /><br />
<br />
<br />
Android umożliwia korzystanie z wbudowanego w urządzenie czujnika orientacji. Dzięki temu czujnikowi jesteśmy w stanie określić pozycję telefonu – tj. pochylenie góra-dół i lewa-prawa. Można dzięki temu zaprogramować np. elektroniczną poziomicę. Ten sam czujnik orientacji pozwala też na określenie azymutu na który zwrócony jest telefon. W tym przykładzie wykorzystamy wszystkie te możliwości. <br />
Zaczynamy od przyklejenia na ekranie czterech elementów klasy TextView:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaKfYVo6J35kMaprRMeMNKCyFwTFKI8oYFhpFA5AEyUmCktdjSgQguziZh77J574MKBtFdzjR7c-qCy7bmSaDHBcWfoKAiYAjZNS88W5GZT6ZbNCR8Fe564LXbpD3TpwxsaxMFU9LB7ztb/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhaKfYVo6J35kMaprRMeMNKCyFwTFKI8oYFhpFA5AEyUmCktdjSgQguziZh77J574MKBtFdzjR7c-qCy7bmSaDHBcWfoKAiYAjZNS88W5GZT6ZbNCR8Fe564LXbpD3TpwxsaxMFU9LB7ztb/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Do przyklejonych elementów musimy podpiąć referencje aby z nich korzystać, tutaj niczego zaskakującego nie ma. Pojawia się za to implementacja interfejsu SensorEventListener. O co tu chodzi? Nasz czujnik będzie co chwila podawał nowe odczyty, aby wyświetlać te zmiany musimy zaimplementować ten interfejs. W związku z tym, będziemy musieli przesłonić metody onSensorChanged i onAccuracyChanged. O tym za momencik.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ4DV-yMQeB2TDyAPcvZbuB3YniroPv25H76-s5FOsKosWr5ISq_-x4rQJ5eL8Tu5T7YGXbprr0M_IEjOtvykgJS9Wl3XsyTVdwQObh3ASyodXGtz9_85PJmW2fWyKHla043V1u5TEZQqL/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgZ4DV-yMQeB2TDyAPcvZbuB3YniroPv25H76-s5FOsKosWr5ISq_-x4rQJ5eL8Tu5T7YGXbprr0M_IEjOtvykgJS9Wl3XsyTVdwQObh3ASyodXGtz9_85PJmW2fWyKHla043V1u5TEZQqL/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />
Zanim zaczniemy korzystać z naszego czujnika, musimy zarejestrować wybraną klasę (musi ona implementować interfejs SensorEventListener) jako odbiornik dla czujnika. Ja to zrobiłem w metodzie onCreate mojej aktywności, ponieważ ta jest uruchamiana w momencie startu programu. Jako odbiornik zarejestrowałem aktywność, ponieważ to w niej chcę reagować na zmiany odczytów (a zadbałem o to by ta aktywność implementowała interfejs SensorEventListener).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFFjTN_5xFYrebRSKbt_zP3DkPakOAtYDynvFpFjqHCclMmiGAlIFVIQZm_10IfPbv-Ic5J1eM-V1iBXCOBhwM2_7V7HPCmlSTQZPR-cqGBnBiOCHCWCOQDldu9IMeu26a6CwR6UEp4VPh/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFFjTN_5xFYrebRSKbt_zP3DkPakOAtYDynvFpFjqHCclMmiGAlIFVIQZm_10IfPbv-Ic5J1eM-V1iBXCOBhwM2_7V7HPCmlSTQZPR-cqGBnBiOCHCWCOQDldu9IMeu26a6CwR6UEp4VPh/s1600/3.png" /></a></div>
<br />
<br />
W związku z implementacją interfejsu SensorEventListener muszę zaimplementować też metody onAccuracyChanged i onSensorChanged. Pierwsza jest wywoływana kiedy dokładność czujnika ulega zmianie (tutaj raczej nam się to nie przyda), druga jest wywoływana automatycznie kiedy odczyty z czujników ulegną zmianie (ta już nas interesuje). Do tej metody przekazywane są odczyty z czujnika. Pod indeksem 0 (event.values[0]) znajdziemy azymut na jaki zwrócona jest góra telefonu, pod indeksem 1 (event.values[1]) wychylenie w płaszczyźnie góra-dół, pod indeksem 2 (event.values[2]) wychylenie lewa-prawa. Wartości wszystkich trzech wypisuję na pierwszym komponencie.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibnF5r60a1ZJRivAA-lMRP1IRd3n8j73fFXy6BmgoiWQ5DuRFjdpMMmtx6fetUjh5f1LZXZ2m0unovzn7ReLpnIVQ6TGUQ8JwXHkAGcBUinxq464VH-g6js4Io7C97TJXZ3X_tJUd6n7Zu/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEibnF5r60a1ZJRivAA-lMRP1IRd3n8j73fFXy6BmgoiWQ5DuRFjdpMMmtx6fetUjh5f1LZXZ2m0unovzn7ReLpnIVQ6TGUQ8JwXHkAGcBUinxq464VH-g6js4Io7C97TJXZ3X_tJUd6n7Zu/s1600/4.png" /></a></div>
<br />
<br />
Korzystając z tych odczytów wyświetlam na pozostałych komponentach stosowne komentarze. Mają one sprawić, że odczyty będą bardziej czytelne. Nie jest to niezbędny element, czysta estetyka. Całą metodę onSensorChanged widać na poniższej ilustracji.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSiuAw5ZfyEBvleQ6MSEa7CbMeMMDEXSfuguuJVSvFhJEP3g6POxHgitniV_G3AJa3z6lT-WJm8R_WshnioJd0GmpIGI2hnGkMqdB7o-ETRkJ7UHS9BP91EO-CcvrQfvTWWZkgczDieWVu/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSiuAw5ZfyEBvleQ6MSEa7CbMeMMDEXSfuguuJVSvFhJEP3g6POxHgitniV_G3AJa3z6lT-WJm8R_WshnioJd0GmpIGI2hnGkMqdB7o-ETRkJ7UHS9BP91EO-CcvrQfvTWWZkgczDieWVu/s1600/5.png" /></a></div>
<br />
<br />
Efekt działania (Samsung ACE3):<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4SS33VUn-G1G2qim3hPH0ukYDcgy9kCY9GPHP3Tz5cYXZtsWOhb9wH9jegEwkTDsS5t6KSIBBiEJjCrPgudO8NdCpnZZe9beG3szPMo7qsI1hdoLfpPomnmViSj_kbZnq-WxghsLNhnkC/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4SS33VUn-G1G2qim3hPH0ukYDcgy9kCY9GPHP3Tz5cYXZtsWOhb9wH9jegEwkTDsS5t6KSIBBiEJjCrPgudO8NdCpnZZe9beG3szPMo7qsI1hdoLfpPomnmViSj_kbZnq-WxghsLNhnkC/s1600/6.png" /></a></div>
<br />
<br />
Do programu warto byłoby dodać jeszcze obsługę sytuacji gdyby urządzenie nie posiadało czujnika, nie każdy telefon jest w niego wyposażony.andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com10tag:blogger.com,1999:blog-5593586859352074500.post-28616191160403412622014-02-17T11:03:00.005-08:002014-02-18T08:54:50.730-08:00Sprawdzanie jakie czujniki mamy dostępne<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/sprawdzanie_jakie_czujniki_mamy_dostepne.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Zanim zacznę korzystać z czujników jakiegoś urządzenia, chciałbym wiedzieć jakie w ogóle są dostępne. W pierwszej kolejności przyklejam na ekranie komponent typu TextView na którym wyświetlę wyniki:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0T4asI4LI3yRM3Frk3RrA3SL_rKV64V-MmMxYe3a0Ib_dw5TBRFfakjcO0MdKFrbf3tFMVdWIILTC5E3oIWj0N2nbnybVDOr0NMbzoIFFa4TDusBhTNbO7jvwSYVxO1NU8FEgDJYZH1Hb/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj0T4asI4LI3yRM3Frk3RrA3SL_rKV64V-MmMxYe3a0Ib_dw5TBRFfakjcO0MdKFrbf3tFMVdWIILTC5E3oIWj0N2nbnybVDOr0NMbzoIFFa4TDusBhTNbO7jvwSYVxO1NU8FEgDJYZH1Hb/s1600/1.png" /></a></div>
<br />
W metodzie onCreate aktywności głównej dodaję parę linii kodu. Kod który ma realizować zadanie znajduje się właśnie w tej metodzie, ponieważ chcę aby wyniki wyświetliły się od razu po uruchomieniu programu. Linie 21,22 to podpięcie referencji do komponentu TextView i wyczyszczenie jego zawartości (napisu). Klasa SensorManager do której obiektu odwołuję się w linii 25 (sama deklaracja obiektu jest w linii 14) daje nam dostęp do czujników wbudowanych w urządzenie. Pobranie instancji tej klasy następuje poprzez wywołanie metody getSystemService z parametrem SENSOR_SERVICE. Aby odczytać nazwy czujników które są dostępne w urządzeniu, wykorzystuję metodę getSensorList klasy SensorManager ( linia 26 ). Metoda ta zwraca dostępne (czyli znajdujące się na danym urządzeniu) czujniki wskazanego typu. Tutaj podaję jako typ Sensor.TYPE_ALL, dzięki czemu w efekcie dostaję czujniki wszystkich typów. Mógłbym w tym miejscu wstawić np. Sensor.TYPE_GYROSCOPE i dostałbym listę wszystkich dostępnych w urządzeniu żyroskopów. Linie 27-29 to już iteracja po zwróconych obiektach i dodawanie kolejnych linii z nazwami kolejnych czujników do kompontentu tekstowego.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpOfUmX46Jp021Uqin8z-Hw0uwDlpthqX-E0sZaSWVek-vwSXqxVDzBt2UN26K-mRamxEEcNWBQ3-KzTgQA3fSMaOdpNyIo3fEBmlGO-FhO-NtLQSnH2HaIRyYp77iR3OkcrrrISPH44Se/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpOfUmX46Jp021Uqin8z-Hw0uwDlpthqX-E0sZaSWVek-vwSXqxVDzBt2UN26K-mRamxEEcNWBQ3-KzTgQA3fSMaOdpNyIo3fEBmlGO-FhO-NtLQSnH2HaIRyYp77iR3OkcrrrISPH44Se/s1600/2.png" /></a></div>
<br />
<br />
Efekt na Samsung ACE3:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOduwYci14yg4d6rSEf-izjYSCOMwzkCRd4w8I1DA_MLWeLpLdhRdqd4fl7XdxaJm80yiMiGLGqaDDxsPPimPV_QWO79p1klHdhK_TOXCe8q-4MlZchzqyQYXmSnlP_Ao9bZcMyGTgMPOQ/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjOduwYci14yg4d6rSEf-izjYSCOMwzkCRd4w8I1DA_MLWeLpLdhRdqd4fl7XdxaJm80yiMiGLGqaDDxsPPimPV_QWO79p1klHdhK_TOXCe8q-4MlZchzqyQYXmSnlP_Ao9bZcMyGTgMPOQ/s1600/3.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com8tag:blogger.com,1999:blog-5593586859352074500.post-15302778163200564402014-02-17T10:02:00.003-08:002014-02-18T08:54:05.672-08:00Używanie map OpenStreetMaps<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/uzywanie_openstreetmaps.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Jedną z alternatyw dla stosowania w swoich aplikacjach map od Google jest wykorzystaniem map zod OpenStreetMaps. Są bezpłatne i działają całkiem sprawnie. <br />
Zaczynamy od dodania do projektu niezbędnych blibliotek tj. osmdroid-android-4.0.jar i slf4j-android-1.5.8.jar (lub ich nowszych wersji jeśli takie się pojawią):<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmBJd10qxpjELxIFjqFutWhbTyamkTDmadIewe7P8VgCOMSuvvzkphh0rTrImZnF9DtSGYwP1VL9CQ6c3Fe_tDIIMYo44YG6OM_cP-2l6dIOmYWq-Lp_WEGRtL5X4Cn9klm64rIpEbN92R/s1600/0.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjmBJd10qxpjELxIFjqFutWhbTyamkTDmadIewe7P8VgCOMSuvvzkphh0rTrImZnF9DtSGYwP1VL9CQ6c3Fe_tDIIMYo44YG6OM_cP-2l6dIOmYWq-Lp_WEGRtL5X4Cn9klm64rIpEbN92R/s1600/0.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Musimy też dodać te biblioteki do Build Path projektu. Robimy to wchodząc do właściwości projektu, następnie przechodząc do „Java Build Path” i wybierając „Add Jars”:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigCA_IwiQH6u_0W2ZpaJJbFPLlFLofiQnWFr_Q_HTsqlnfDV6j00bGwe1tlOxnEEWnInEwMaWB4rQhyphenhypheny08sEq20YnNHXWOXXqr0Bay8Lseh_2QBRAUGLncUSNvgbolIELzi4QYZuNylMDB/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigCA_IwiQH6u_0W2ZpaJJbFPLlFLofiQnWFr_Q_HTsqlnfDV6j00bGwe1tlOxnEEWnInEwMaWB4rQhyphenhypheny08sEq20YnNHXWOXXqr0Bay8Lseh_2QBRAUGLncUSNvgbolIELzi4QYZuNylMDB/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Wybieramy te dwie nowo dodane biblioteki i zatwierdzamy. Czas wstawić obiekt mapy do naszego layoutu. Dodajemy widoczny na poniższej ilustracji element org.osmdroid.views.MapView:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhJbjelwQrtSlMK9f6VHzUnZM7HJae97RUqXcRNYP5BmPR7zj7Q-CxBAD6EbwwtkW9fN85xX5RB0WCEu1KzXJNZoCIYal_JX0U9X82RbSKZ1K-aTELFWLhM79IKrpcdbe_tL_wYAA_4_LS/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhJbjelwQrtSlMK9f6VHzUnZM7HJae97RUqXcRNYP5BmPR7zj7Q-CxBAD6EbwwtkW9fN85xX5RB0WCEu1KzXJNZoCIYal_JX0U9X82RbSKZ1K-aTELFWLhM79IKrpcdbe_tL_wYAA_4_LS/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Ponieważ aplikacja będzie musiała pobierać dane (mapy) z internetu, musimy zadbać o odpowiednie pozwolenia. Do pliku manifestu dodajemy więc poniższe linie:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<uses-permission android:name="android.permission.INTERNET"/><br />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/><br />
<br />
tuż za tagiem </application> <br />
To są wystarczające uprawnienia niezbędne do działania map OpenStreetMaps. My w naszym przykładzie wykorzystamy naszą realną pozycję z GPS i wyświetlimy fragment mapy z okolicą naszej pozycji. Z tego powodu trzeba będzie dodać jeszcze dodatkowe uprawnienia:<br />
<br />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><br />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><br />
<br />
W przypadku gdybyśmy nie korzystali z pozycji GPS, tych ostatnich uprawnień nie dodajemy. Ogólnie w suemie całość powinna wyglądać mniej więcej tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9ayqSCELsfHpH43tfihOLjn3HC8i3Xvrahe2kTsbW7DNhZAUQqzXSSxo8N4B5OO0G0DaZIrOOxPn8jI1nen2iHCphHZ1BPbnvULgpwuk1azB-i0iQilc2nf5_k4Aa1jz19UprphJJKXrk/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi9ayqSCELsfHpH43tfihOLjn3HC8i3Xvrahe2kTsbW7DNhZAUQqzXSSxo8N4B5OO0G0DaZIrOOxPn8jI1nen2iHCphHZ1BPbnvULgpwuk1azB-i0iQilc2nf5_k4Aa1jz19UprphJJKXrk/s1600/3.png" /></a></div>
<br />
Przechodzimy teraz do aktywności którą wykorzystamy do zaprezentowania mapy. Dodaję kilka pól do klasy. Pola w liniach 17-20 są związane z wykorzystaniem GPS, więc jeśli nie zamierzamy z niego korzystać, nie definiujemy ich. Potrzebne nam będą tylko obiekty z linii 23,24:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLRDsy-9M2R0rWRFNnDUnbYBGR8aT0R5BcZbO3AnfSWPAYsvqz1e8vmVjHaiUq0nv6_gQnfN-lfVlZgr-iFuhlsXBzHYbiaBeJlpRzCjrwlUv0o1ddC7IyK7MXFcp8WHXd9i2xvIkOsUzF/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLRDsy-9M2R0rWRFNnDUnbYBGR8aT0R5BcZbO3AnfSWPAYsvqz1e8vmVjHaiUq0nv6_gQnfN-lfVlZgr-iFuhlsXBzHYbiaBeJlpRzCjrwlUv0o1ddC7IyK7MXFcp8WHXd9i2xvIkOsUzF/s1600/4.png" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkxz2h1AIdutixSihyyF9AEo04iK0D4mzmQCA7yIJRm2_PJR05pEnzJUm851kxBfqmbFExyt9rj3-DpxzSSnjbwhKh1aexDpOBGrFabQeRcIvQwK3TznKbDgS27KUOwcBiISrM9QIu-iZ/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijkxz2h1AIdutixSihyyF9AEo04iK0D4mzmQCA7yIJRm2_PJR05pEnzJUm851kxBfqmbFExyt9rj3-DpxzSSnjbwhKh1aexDpOBGrFabQeRcIvQwK3TznKbDgS27KUOwcBiISrM9QIu-iZ/s1600/5.png" /></a></div>
<br />
Kod z linii 42-45 jest związany z pobraniem pozycji GPS, omawiałem w jednym z poprzednich rozdziałów dokładnie sposób działania takiego kodu i nie będę się tutaj tym zajmował. Nas interesują zasadniczo linie 35-40 i 46-50. Linia 35 to element nie związany z samą mapą jako taką, służy do podpięcia referencji do komponentu mapy. W linii 36 wskazujemy źródło z którego pobieramy mapy. Linie 37 i 38 określają zachowanie mapy. Metoda setBuildInZoomControls (linia 37) ustawia, czy ma być możliwość przybliżania i oddalania na mapie (jeśli podamy true, będą dostępne przyciski + i – na mapie). Metoda setMultiTouchControls (linia 38) ustawia możliwość stosowania gestów dotykowych na ekranie – jak np. zbliżanie i oddalanie widoku dwoma palcami. W linii 40 określamy domyślne przybliżenie mapy. W linii 46 tworzymy obiekt klasy GeoPoint reprezentujący nasze położenie. W linii 49 centrujemy mapę na punkcie wskazanym przez obiekt klasy GeoPoint ustawiony w linii 46. Wywołanie metody invalidate w linii 50 to odświeżenie widoku.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsfJ_d-ojtTJJ1Ypw4vqtTUxo-WHMwd7fuijODETDiZyR3JKLis2XAoLORCxa1QGt9hLsIxm_Wtki2eDuVXhKisMSmDd28MbsyTOmHuki_bvxjAq281V27CnRNZINO3RDcyOr39ljxfGvz/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsfJ_d-ojtTJJ1Ypw4vqtTUxo-WHMwd7fuijODETDiZyR3JKLis2XAoLORCxa1QGt9hLsIxm_Wtki2eDuVXhKisMSmDd28MbsyTOmHuki_bvxjAq281V27CnRNZINO3RDcyOr39ljxfGvz/s1600/6.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com19tag:blogger.com,1999:blog-5593586859352074500.post-37132824491680675682014-02-17T09:57:00.005-08:002014-02-18T08:53:04.157-08:00GPS - sprawdzanie lokalizacji<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/gps.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Jeśli nasz telefon / urządzenie z którego korzystamy posiada czujniki GPS,<br />
możemy wykorzystać je do sprawdzania naszej pozycji programowo. Możemy również skorzystać z darmowych map projektu OpenStreetMap (http://openstreetmap.org) i np. stworzyć własną nawigację.<br />
Zaczniemy od utworzenia nowego projektu i wyedytowania pliku AndroidManifest.xml tego projektu. Musimy dodać prośbę o uprawnienia do korzystania z czujników GPS. Bez takiej autoryzacji ze strony użytkownika, nasz program nie będzie działał. Dodajemy linie:<br />
<br />
<br />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/><br />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/><br />
<br />
Tuż pod zamknięciem elementu <application>. Podglądzik:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwkNFr0AW07_ePBnf4h1Nchknz8T0NEnXz4cG_zYQkGbu9Dvzw4fsT31_jVagCowHBWkfpkMGXwQm5vnyxnxf_EJbHhyAPIeaDwul0re9fppd-uIX55Aovg6uu6U1OVR8SrqJsXspQXetO/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiwkNFr0AW07_ePBnf4h1Nchknz8T0NEnXz4cG_zYQkGbu9Dvzw4fsT31_jVagCowHBWkfpkMGXwQm5vnyxnxf_EJbHhyAPIeaDwul0re9fppd-uIX55Aovg6uu6U1OVR8SrqJsXspQXetO/s1600/1.png" /></a></div>
<br />
Mamy już możliwość korzystania z czujników GPS. Czas przejść do właściwej wersji kodu. Zaczniemy jak zawsze od wersji najprostszej tj. po uruchomieniu programu ma się na ekranie wyświetlić nasze położenie tzn. długość i szerokość geograficzna. <br />
Przechodzimy teraz do naszej głównej aktywności. Przyklejam na ekranie trzy elementy klasy TextView. Na długość i szerokość geograficzną, oraz na nazwę wybranego dostawcy (wyjaśni się to nieco później). <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQj62ZnI9nsRrGnjkhIvu7cX_FBDhjkxqJaJ2Z1tgItXTE6EQqhzdAtO9MjwyCaUoZ_6YxI3uO0AXUxGzAiaxX5zXu4SJoNk6hdhMfzz5GbfZXNis5WwIEjdUEEHNQe6kw2rYBu-oig5jt/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQj62ZnI9nsRrGnjkhIvu7cX_FBDhjkxqJaJ2Z1tgItXTE6EQqhzdAtO9MjwyCaUoZ_6YxI3uO0AXUxGzAiaxX5zXu4SJoNk6hdhMfzz5GbfZXNis5WwIEjdUEEHNQe6kw2rYBu-oig5jt/s1600/2.png" /></a></div>
<br />
<br />
Od razu w metodzie onCreate podpinam do nich uchwyty. Będę się przecież do tych elementów odwoływał, więc będę też potrzebował elementów reprezentująćych te komponenty. <br />
Do określania lokalizacji będzie nam służył obiekt klasy LocationManager. Definiuję go podobnie jak komponenty klasy TextView – jako pole klasy a nie jako zmienna prywatna w metodzie onCreate. Robię tak, bo być może będę się do tego obiektu owoływał z innych metod na dalszych etapach tworzenia programu. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtVFGw4t4pzpaKT9ujX0NditU7w6lJ6o1DJSMkhMoFK2P-8bGYcqqKmwWo93Hpuy-x1oTMt9-NikaDMOiTJ8gXWvbYQw6f25T205Idp7BJooPMK5Zjrmvvon99xrCZfSJXFOYwbjYtB2Q-/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtVFGw4t4pzpaKT9ujX0NditU7w6lJ6o1DJSMkhMoFK2P-8bGYcqqKmwWo93Hpuy-x1oTMt9-NikaDMOiTJ8gXWvbYQw6f25T205Idp7BJooPMK5Zjrmvvon99xrCZfSJXFOYwbjYtB2Q-/s1600/3.png" /></a></div>
<br />
<br />
Obiekt kr klasy Criteria (linia 29) służy do wyszukiwania najlepszego dostawcy informacji o położeniu GPS. Mogę np. poprzez te kryteria określić że interesują mnie wyłącznie bezpłatni dostawcy ( metoda setCostAllowed klasy Criteria), czy poziom zapotrzebowania energetycznego wymaganego dla danego sposobu określania lokalizacji (setPowerRequirement). Chcąc zastosować któryś z wymienionych warunków, po utworzeniu obiektu klasy Criteria, uruchamiamy dla tego obiektu wybrane metody. Sam obiekt klasy Criteria jako element warunkujący wybór dostawcy stosujemy później przy pobieraniu nazwy najlepszego dostawcy. W linii 30 inicjalizuję obiekt przy uzyciu usługi zwracanej przez metodę getSystemService (metoda dziedziczona po klasie Activity). Z użyciem tej metody możemy podpinać się do najprzeróżniejszych usług systemowych : np. korzystać z różnych czujników (temperatury, ciśnienia etc), korzystać z usług lokalizacyjnych, pobierać dane z internetu, drukować, korzystać z portu USB i wiele innych. Ponieważ będziemy korzystać z usług lokalizacyjnych, jako parametr metody getSystemService podaję LOCATION_SERVICE. Linia 31 to pobranie nazwy najlepszego dostawcy informacji o położeniu (może to być np. GPS, położenie określone wg triangulacji, lub sieć WIFI). Zwrócony może być tylko dostawca dla którego wywołująca aktywność ma niezbędne pozwolenia (chodzi o wpisy w AndroidManifest.xml). Jeśli kilku dostawców spełnia założone kryteria, to zwrócona zostanie nazwa tego który charakteryzuje się najwyższą dokładnością położenia. Przykładowo, jeśli możemy określić nasze położenie na podstawie GPS, lub na podstawie triangulacji, przy czym GPS określi to z dokładnością do 10 metrów a triangulacja z dokładnością do 1 kilometra, to zostanie zwrócona nazwa dostawcy gps. Pierwszy parametr metody getBestProvider to nasze kryteria, drugi określa czy zwracani mają być tylko aktywni dostawcy ( w tym przypadku oczywiście tylko tacy nas interesują).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRJtzGpleEiRqJg223PkO2Mf5MXwbrY84Jorka4G7oRPiZe6uvAAcnrGnlXwKnb128WawoairmxGPXqmHCRbszcGVOOMi-cxg8gVGMMDXkBSM5ZEIDt4ZZc9PEui3qXA_kw1TDLWI0rDCy/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRJtzGpleEiRqJg223PkO2Mf5MXwbrY84Jorka4G7oRPiZe6uvAAcnrGnlXwKnb128WawoairmxGPXqmHCRbszcGVOOMi-cxg8gVGMMDXkBSM5ZEIDt4ZZc9PEui3qXA_kw1TDLWI0rDCy/s1600/4.png" /></a></div>
<br />
<br />
Obiekt klasy Location będzie reprezentował naszą lokalizację w przestrzeni, będziemy z niego później pobierać np. naszą długość i szerokość geograficzną. Najpierw jednak musimy określić nasze położenie :) Robimy to w linii 32 przy użyciu metody getLastKnownLocation obiektu klasy LocationManager. Jako parametr podajemy nazwę wybranego dostawcy informacji o położeniu. Linie 34-36 to po prostu wyświetlenie uzyskanych danych. Warte uwagi mogą tutaj być metody getLongitude i getLatitude zwracające długość i szerokość geograficzną. Zwracają liczby typu double, z dosyć dużą dokładnością. Będąc na obrzeżach Warszawy dostałem swoje położenie z dokładnością do 7 miejsca po przecinku stopnia geograficznego. Do tego prostego programu warto byłoby ewentualnie dorobić obsługę sytuacji w której nie byłoby w danym położeniu żadnego dostawcy, lub nie można byłoby określić położenia. Nie będziemy się tym tutaj jednak zajmować ponieważ obsługa nulla zwracanego z metody (tutaj z metod getBestProvider i getLastKnownLocation) to podstawy Javy. Chcąc testować rzeczy związane z GPS, najlepiej robić to na realnym urządzeniu a nie żadnych emulatorach. Niby na emulatorach jest możliwość ustawienia fikcyjnego położenia GPS, ale też nie zawsze to działa. U mnie po uruchomieniu programu na telefonie wyświetliło się:<br />
<br />
najlepszy dostawca: network<br />
szerokość geograficzna: 52.2996591<br />
długość geograficzna: 20.9930961<br />
<br />
Program działa, jednak jeśli zmienimy położenie, nasz program tego nie odnotuje. Warto byłoby wzbogacić program o funkcjonalność która by uwzględniała zmiany naszego położenia. Zacznę od dodania do ekranu elementu na którym będzie pojawiała się historia lokalizacji. Przykleiłem komponent klasy SmallText.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmqdfpo2ZOIogFNk10zRYRN-1RsiOZq2jjhT68sjWV9FJorLjULD-4Z6FaDdwEgGgv2ZzqtDCbVPRiy0hzo2ve4ZJI0f5i80tcAE3HaoL_wbxqKZsHNbS1k4h19Xh1eyikUlG3G1zE3T7b/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgmqdfpo2ZOIogFNk10zRYRN-1RsiOZq2jjhT68sjWV9FJorLjULD-4Z6FaDdwEgGgv2ZzqtDCbVPRiy0hzo2ve4ZJI0f5i80tcAE3HaoL_wbxqKZsHNbS1k4h19Xh1eyikUlG3G1zE3T7b/s1600/5.png" /></a></div>
<br />
<br />
Żeby historia troszkę się odróżniała, zmieniłem tekstu w tym nowym komponencie poprzez edycję pliku layoutu i dopisani linijki : android:textColor=”#300FD”<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKrBESMpMWVSaYeLQnkAodwqODb7Ho8yN-vwYhnbcdrd8TuIsH8aRwUgNxm1-oN_0ODKaFvO5BEBp1S6WgGfAHu15becyGeP_1LSK1dsQDo_ePBUhS2fT-grNmc2d8OotkJFyQ400UUI1Q/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgKrBESMpMWVSaYeLQnkAodwqODb7Ho8yN-vwYhnbcdrd8TuIsH8aRwUgNxm1-oN_0ODKaFvO5BEBp1S6WgGfAHu15becyGeP_1LSK1dsQDo_ePBUhS2fT-grNmc2d8OotkJFyQ400UUI1Q/s1600/6.png" /></a></div>
<br />
Kodowi źródłowemu naszej aktywności będziemy przyglądać się poczynając od góry. W definicji dopisałem „implements LocationListener”, dzięki czemu będę miał dostępną metodę onLocationChanged która jest wywoływana za każdym razem kiedy zostanie wykryta nowa lokalizacja. Ponieważ jest to interfejs, będę musiał zaimplementować cztery wymagane przez niego metody. Tym się zajmiemy za chwilę.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw1O_psYkyRl3MlXf84txpQTepve_6rHvmJH0JUkA52F1SbSps9kCO96uk2plVjbPQZOuFJNf5L-TgHWJjbZ1Rrh7-gfplYnyfBP-yJMO6RbjBaUbGiGJ6E3-qGkWT4AtFnhGPu7-pNdoE/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgw1O_psYkyRl3MlXf84txpQTepve_6rHvmJH0JUkA52F1SbSps9kCO96uk2plVjbPQZOuFJNf5L-TgHWJjbZ1Rrh7-gfplYnyfBP-yJMO6RbjBaUbGiGJ6E3-qGkWT4AtFnhGPu7-pNdoE/s1600/7.png" /></a></div>
<br />
W linii 18 widzimy nowe pole – t4. To jest obiekt reprezentujący komponent do wyświetlania historii. W liniach 25-28 widzimy nową metodę „odswiez”. Ponieważ będę musiał odświeżać swoje położenie przynajmniej dwukrotnie tj. przy uruchomieniu aplikacji i przy zmianie lokalizacji, postanowiłem kod odpowiedzialny za odświeżanie oddelegować do osobnej metody. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7jQuZ6dOYrAG4NLXMcDI9lcQmO7ViFMTCh4DQPsl-zcH5-p6bFgLmlvuVfUXHwyCLQu0iXLCizxaBmP5bb7_iGbKI9x31ggQHWcTcsldkFe15NAe3f0tjktmKy_xQ0ftf8E2UBEftIJLO/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg7jQuZ6dOYrAG4NLXMcDI9lcQmO7ViFMTCh4DQPsl-zcH5-p6bFgLmlvuVfUXHwyCLQu0iXLCizxaBmP5bb7_iGbKI9x31ggQHWcTcsldkFe15NAe3f0tjktmKy_xQ0ftf8E2UBEftIJLO/s1600/8.png" /></a></div>
<br />
<br />
W linii 37 widzimy podpięcie do obiektu t4 referencji do naszego nowego komponentu (tego napisu który będzie robił za historię). Nic nadzwyczajnego, ale musimy o tym pamiętać. W linii 41 zamiast wywoływać osobno szukanie najlepszego dostawcy i pobieranie położenia mamy wywołanie metody która właśnie to robi i aktualizuje nasze obiekty. Linia 42 zawiera ustawienie własności odświeżania. Konfigurujemy tutaj co jaki czas i przy jakiej zmianie położenia system ma odświeżać lokalizację (czyli jak często ma być automatycznie wywoływana metoda onLocationChanged którą będziemy za chwilę implementować - w liniach 50-57). Pierwszy parametr to nazwa dostawcy z którego korzystamy, drugi to czas w milisekundach co ile ma być odświeżana lokalizacja, trzeci to co jaki dystans (wyrażony w metrach), a czwarty to obiekt implementujący interfejs LocationListener (tutaj akurat dotyczy to obiektu w którym się znajdujemy). Linie 43-46 to ustawienie domyślnych napisów, czyli tego co ma się pojawić zaraz po uruchomieniu programu. Tutaj tak jak wcześniej wyświetlamy dostawcę i współrzędne. W linii 46 ustawiam pierwszą i na razie jedyną linię historii.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje3uSF6SbrXapkRFcKkuomPvAjB7uf1eot2u11sKK7yXoHD_ctQDXkn1z4POSATRQ3CkikyGIkwBZ2fo-O5nCv5519VbL7ZVR6F4sUqUpOO026f_Qctyn4lnvUuzG3kSRQ31oDZPg_PdXy/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEje3uSF6SbrXapkRFcKkuomPvAjB7uf1eot2u11sKK7yXoHD_ctQDXkn1z4POSATRQ3CkikyGIkwBZ2fo-O5nCv5519VbL7ZVR6F4sUqUpOO026f_Qctyn4lnvUuzG3kSRQ31oDZPg_PdXy/s1600/9.png" /></a></div>
<br />
Poniżej mamy cztery metody które musiałem zaimplementować z racji implementacji interfejsu LocationListener. Trzy tymczasowo zostawiam w spokoju. Interesuje mnie teraz tylko metoda onLocationChanged. Jest ona wywoływana co interwał czasowy lub dystans określone (linia 42) w metodzie requestLocationUpdates. W ramach mojej implementacji metody onLocationChanged odświeżam położenie wywołując metodę odswiez, a następnie w liniach 52-54 wyświetlam na komponentach aktualne położenie. W linii 55 dodaję do historii kolejną linię.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihQQ1CjJ9H8uMn9dbwMcCjxdpn2FpOr5n9sA1zoUIEdLEf2-JkoukotqqhqGOq64q4ms5SXPJRICt7SCDxEcr0dfJGJ8axth0Rce7A9V38Aqmgf4P2KgGIO70wmWXny4svVnsVDQm60sDa/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihQQ1CjJ9H8uMn9dbwMcCjxdpn2FpOr5n9sA1zoUIEdLEf2-JkoukotqqhqGOq64q4ms5SXPJRICt7SCDxEcr0dfJGJ8axth0Rce7A9V38Aqmgf4P2KgGIO70wmWXny4svVnsVDQm60sDa/s1600/10.png" /></a></div>
<br />
<br />
Byłem ciekaw na ile sprawnie t o działa, więc wgrałem program na telefon i poszedłem sprawdzić czy nie ma mnie za rogiem. Oto wyniki:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAAUro7J1GJyQ242u3nqDOcwJX8Rb8o0td4wQPG3-MUrHDsaFa_ri-cm7pLHm1s8snX00QQrf2y_NuezjzGQVvQnV50-2hfdi1M5mQWIzI7VCWs7Vw0GCY1lDQmc7zbkchKle-nXMWmo7K/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAAUro7J1GJyQ242u3nqDOcwJX8Rb8o0td4wQPG3-MUrHDsaFa_ri-cm7pLHm1s8snX00QQrf2y_NuezjzGQVvQnV50-2hfdi1M5mQWIzI7VCWs7Vw0GCY1lDQmc7zbkchKle-nXMWmo7K/s1600/11.png" /></a></div>
<br />
<br />
<br />
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com18tag:blogger.com,1999:blog-5593586859352074500.post-78468176238224001192014-02-17T09:51:00.003-08:002014-02-18T08:52:32.484-08:00Sprawdzanie ilości wolnego miejsca na karcie SD<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/sprawdzanie_ilosci_miejsca_na_karcie_sd.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Jeśli chcemy sprawdzić ilość wolnego miejsca na karcie SD lub w katalogu , możemy skorzystać z klasy StatFs. Niestety nie działa to dla katalogu root. <br />
W pierwszej kolejności przyklejam na ekranie element typu TextView. Będzie na nim wyświetlona ilość miejsca na karcie.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKM0M6z0cnSaujD-Tk_7nNt5gJOFjoFZLCTGlLVdtxAZg9mhzb0jIZm6nOsnA0_SbfGsYlcGPjsfj3w0bx3j-OpxcGGDX3EwZqEdBhxWohFIOtpkTwmlKrXJS1TYZrRukGZnhzz-kkUmDe/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKM0M6z0cnSaujD-Tk_7nNt5gJOFjoFZLCTGlLVdtxAZg9mhzb0jIZm6nOsnA0_SbfGsYlcGPjsfj3w0bx3j-OpxcGGDX3EwZqEdBhxWohFIOtpkTwmlKrXJS1TYZrRukGZnhzz-kkUmDe/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Teraz trzeba dodać nieco kodu który to obsłuży:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk6JycrvbAuql9S9iuIr2dzf_63rWHGFi1AT5FFmBqDOCNHGaVjpLiVMtQFw9DIZWsf9cae-c78hknk6g3x9xCnSuQ4j7TdM55rCbnPHsihUdHL9jjb6P4jVRW9k44NDRuqboEXA1XiU_K/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk6JycrvbAuql9S9iuIr2dzf_63rWHGFi1AT5FFmBqDOCNHGaVjpLiVMtQFw9DIZWsf9cae-c78hknk6g3x9xCnSuQ4j7TdM55rCbnPHsihUdHL9jjb6P4jVRW9k44NDRuqboEXA1XiU_K/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Zasadniczo to co najważniejsze mieści się w liniach 16 i 17. W 16 z użyciem konstruktora klasy StatFs wskazuję katalog dla którego będzie sprawdzana ilość wolnego miejsca. W linii 17 wykorzystuję metody getBlockSize i getBlockCount do sprawdzenia ilości miejsca na karcie wyrażonej w bajtach. Ponieważ bardziej czytelne jest wyświetlenie tego w megabajtach, dzielę dwukrotnie uzyskaną wartość przez 1024 i zaokrąglam w linii 18. Efekt działania programu:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzkczBGXarAJERJkuclJE7Ss17OyIXqQiiIY2Id-pFv15SyJm1dheLQZPAapKnD0jXY0FcDJo_2rfPfspBU4hufdjsJbPh9T12EqMD_LeQUgpeDgWqsW7grrrt3-TP0pr1Jy3RUuuIn-LC/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjzkczBGXarAJERJkuclJE7Ss17OyIXqQiiIY2Id-pFv15SyJm1dheLQZPAapKnD0jXY0FcDJo_2rfPfspBU4hufdjsJbPh9T12EqMD_LeQUgpeDgWqsW7grrrt3-TP0pr1Jy3RUuuIn-LC/s1600/3.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com6tag:blogger.com,1999:blog-5593586859352074500.post-22799402033769746702014-02-17T09:49:00.003-08:002014-02-18T08:51:28.069-08:00Sprawdzanie zawartości katalogu i własności plików<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/sprawdzanie_zawartosci_katalogu_i_wlasciwosci_plikow.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br /><br />
<br />
Rozpoczniemy od sprawdzania zawartości katalogu. Wyniki wyświetlę na komponencie ListView.<br />
W pierwszej kolejności dodaję więc taki komponent do mojej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfFh1wz85b5N6YHPuvNnzU9fJs3nG9noH40BGrfepoqCnxhGPNA5OBjYzrPWO0LqhslgXS6SOnQ-BoJB29HdmpL-Aco0e0yo8pVIajykRtRthBIJJPNC1UotseJQmAogSTtr1E7uO_lSbe/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfFh1wz85b5N6YHPuvNnzU9fJs3nG9noH40BGrfepoqCnxhGPNA5OBjYzrPWO0LqhslgXS6SOnQ-BoJB29HdmpL-Aco0e0yo8pVIajykRtRthBIJJPNC1UotseJQmAogSTtr1E7uO_lSbe/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Do tego komponentu trzeba będzie dodać layout dla pojedynczych elementów listy (jest to niezbędne na późniejszych etapach), na razie tworzę pusty plik XML w katalogu layout.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIK9-sqBzKlRy9y6g9_cK_ymojS4Xf9YxGZ5_2Rd9EBhjvFW324BT21f-9IqMEZHf0ydWwr67ULkA5lDDVyQKocgFx38yqjdEnaXnNkaD3op6Izt-ACz4VwPDjCRyHUYS0SV9txm_io0Ry/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIK9-sqBzKlRy9y6g9_cK_ymojS4Xf9YxGZ5_2Rd9EBhjvFW324BT21f-9IqMEZHf0ydWwr67ULkA5lDDVyQKocgFx38yqjdEnaXnNkaD3op6Izt-ACz4VwPDjCRyHUYS0SV9txm_io0Ry/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Jego zawartość przedstawia się następująco:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggcfNS-F8Ua1_DrsEaGnNAISHYYAeJPgjZnADUES8LRL0jCkF92Nu3BibvfxMxWSr42kSKJ3iMyIlW6O26gcFc1ZCfru_nAndz7XpQbgLQe8iNbJyRNkHJaa-TU7vBBv6r6E0IeGRy4eH0/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggcfNS-F8Ua1_DrsEaGnNAISHYYAeJPgjZnADUES8LRL0jCkF92Nu3BibvfxMxWSr42kSKJ3iMyIlW6O26gcFc1ZCfru_nAndz7XpQbgLQe8iNbJyRNkHJaa-TU7vBBv6r6E0IeGRy4eH0/s1600/3.png" /></a></div>
<br />
<br />
Nic specjalnego. Plik opisuje po prostu w jaki sposób ma wyglądać pojedynczy element listy. Tutaj będzie to po prostu napis, ale równie dobrze możemy tutaj zastosować choćby TableLayout i ułożyć sobie bardziej skomplikowane struktury (np. obrazek i obok tekst).<br />
Przyszła pora na kod:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPRgqVnqIM46vBsQ7yr3GQRDaCJeKRAtPbGV42_Tj0jKtknIc6cG5rgETSyMMAHIC2QYJXpZ4AGpdjFo-jqe6lulO8x5p5ZiDAvzK0f8lL4LHM5woeH9l7KKuAiVmTZLudmurKCcJWcyuq/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhPRgqVnqIM46vBsQ7yr3GQRDaCJeKRAtPbGV42_Tj0jKtknIc6cG5rgETSyMMAHIC2QYJXpZ4AGpdjFo-jqe6lulO8x5p5ZiDAvzK0f8lL4LHM5woeH9l7KKuAiVmTZLudmurKCcJWcyuq/s1600/4.png" /></a></div>
<br />
Najważniejsze elementy znajdują się w liniach 23-28. W linii 23 do listy elementów typu String przypisuję listę plików z wskazanego katalogu. Klasa File to zwykła javowa File, nie żadna Androidowa interpretacja. W Javie każdy katalog też jest plikiem, stąd taki może nieco dziwny zapis. Katalog którego zawartość pobieram to „.” , czyli katalog root systemu. Równie dobrze może to być dowolny inny katalog, podajemy go jako parametr konstruktora klasy File. W liniii 24 tworzę obiekt klasy ArrayList. Będę musiał do adaptera (czyli elementu dzięki któremu uzupełniam zawartość komponentu klasy ListView) podać listę właśnie takiego typu. W linii 26 robię „konwersję” z dotychczas używanej listy stringów na obiekt klasy ArrayList. Linia 27 to inicjalizacja wcześniej wspomnianego adaptera. Mamy trzy parametry, pierwszy to kontekst, drugi do wskaźnik do zawartości pliku XML charakteryzującego wygląd pojedynczego elementu listy, trzeci to lista typu ArrayList zawierająca dane do wyświetlenia.<br />
<br />
<br />
Efekt:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaFgbfURIp_JDJ623xcZFDqORjGnsOk2x8NwSRqPaVdWJlgGjNqBjyQTV9gfYl-pr3M5_CCMpHffdmJzNCA-VSs6vg2wxrYu6AfZaWFQHlLTX6KnXnB4V682qFt-77g1jOZPbPr5UWrmon/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjaFgbfURIp_JDJ623xcZFDqORjGnsOk2x8NwSRqPaVdWJlgGjNqBjyQTV9gfYl-pr3M5_CCMpHffdmJzNCA-VSs6vg2wxrYu6AfZaWFQHlLTX6KnXnB4V682qFt-77g1jOZPbPr5UWrmon/s1600/5.png" /></a></div>
<br />
<br />
Zmieniłem katalog którego zawartość wyświetlam na „/sdcard” , czyli zawartość karty SD. Wynik wygląda tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6p__-eD0_cgi2wfyIJXL2ME_x5N3NCUocjDLDnlHemybxmAqi04EZjLnMAzMc8P8fZdT0Spzc90tJdIKjfcxqAOC5E6l1kNSPKF9gspJENlOqykTv03dtLnZYynW1YN0wdSdhHmj9B4BR/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh6p__-eD0_cgi2wfyIJXL2ME_x5N3NCUocjDLDnlHemybxmAqi04EZjLnMAzMc8P8fZdT0Spzc90tJdIKjfcxqAOC5E6l1kNSPKF9gspJENlOqykTv03dtLnZYynW1YN0wdSdhHmj9B4BR/s1600/6.png" /></a></div>
<br />
Dodajemy teraz wyświetlanie własności plików i katalogów.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgseje-WvEBcW9s6eYCTzOqOAa4_30LcIrn4Yh_G_QMee89g6jUiPYCq3kibguUv5bPNqNt3ZhzxoDNWeRLvQ9vV28g8oLM0PNcC5HUF3N3ZJviZl7e_cBfV4MXkAHu6A-icOw30HRjZA_h/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgseje-WvEBcW9s6eYCTzOqOAa4_30LcIrn4Yh_G_QMee89g6jUiPYCq3kibguUv5bPNqNt3ZhzxoDNWeRLvQ9vV28g8oLM0PNcC5HUF3N3ZJviZl7e_cBfV4MXkAHu6A-icOw30HRjZA_h/s1600/7.png" /></a></div>
<br />
Wprowadziłem kilka zmian w kodzie. W linii 27 zmieniłem troszkę wywołanie konstruktora klasy File, tak by korzystał ze zmiennej typu String o nazwie katalog, a nie podanej bezpośrednio ścieżki. Zrobiłem tak, ponieważ nieco dalej znowu korzystam ze ścieżki do pliku i nie chcę powielać tego samego kodu. Od linii 29 do 37 iteruję po elementach listy stringów zawierających nazwy plików i katalogów. W zależności od własności pliku/katalogu do jego nazwy doklejam różne informacje. Dalsza część kodu pozostaje bez zmian. Linia 31 to doklejenie do nazwy litery R, jeśli plik/katalog możemy odczytywać. Linia 32 to dodanie do nazwy litery W jeśli mamy możliwość pisania do pliku. Linie 33-35 służą dodaniu daty do nazwy pliku/katalogu. Korzystam tutaj z obiektu klasy java.util.Date, ponieważ wartość zwracana przez metodę lastModified() wyrażona jest jako int. W linii 36 powiększam wartość iteratora którego używam do poruszania się po liście. <br />
W pliku XML elementy_listy_glownej.xml opisującym wygląd pojedynczego elementu listy również dokonałem małej zmiany (czysta kosmetyka). Zmniejszyłem wielkość czcionki z 15 na 11, ponieważ większa ilość informacji powodowałaby zawijanie linii, a tak mamy wszystkie informacje dotyczące pliku/katalogu w jednej linijcie :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-UCWokKzlLTwqYASl5egyQwXRVg1ABdcRttTlIDSebtuIR3r_KiYGydaPrdgWK-nB-ZAHZzryRg5W0jVWxcVmY6t5g372F65P0QWZV2J6IBLMvkjmeQD01dRAaTEuyThkGtzlzXKQPS-z/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-UCWokKzlLTwqYASl5egyQwXRVg1ABdcRttTlIDSebtuIR3r_KiYGydaPrdgWK-nB-ZAHZzryRg5W0jVWxcVmY6t5g372F65P0QWZV2J6IBLMvkjmeQD01dRAaTEuyThkGtzlzXKQPS-z/s1600/8.png" /></a></div>
<br />
Efekt:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSRpnLZlXahj2dy7SDWSWoYavqi7V1LUSWkg8jpJe-3jjy4sqjgIQ11GocjZY5h8OqN9lS_l3uGHtETEoWoIlCwwfwiV_6yS24gaeAza7EWk7mbnJTJr0iPKkGFMzulVS94xz31QL4s7S/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEheSRpnLZlXahj2dy7SDWSWoYavqi7V1LUSWkg8jpJe-3jjy4sqjgIQ11GocjZY5h8OqN9lS_l3uGHtETEoWoIlCwwfwiV_6yS24gaeAza7EWk7mbnJTJr0iPKkGFMzulVS94xz31QL4s7S/s1600/9.png" /></a></div>
<br />
<br />
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com10tag:blogger.com,1999:blog-5593586859352074500.post-41927909976800872942014-02-17T09:44:00.004-08:002014-02-18T08:50:34.509-08:00Baza SQLite w androidzie<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/baza_sqlite_w_androidzie.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
Android zapewnia wbudowaną obsługę baz SQLite. Nie potrzebujemy żadnych dodatkowych bibliotek, wszystko co niezbędne jest dla Androida natywne. SQLite jest łatwą w obsłudze lekką bazą danych i wykorzystywana jest nie tylko w systemie Android. Również w zwykłej Javie czy JEE możemy tę bazę wykorzystać. Jest jednak kilka różnic w obsłudze. Po pierwsze w zwykłej Javie/JEE potrzebujemy dodawać biblioteki SQLite, po drugie z samą bazą łączymy się z użyciem JDBC czy jakiegoś ORMa np. Hibernate. Tutaj cała baza zawiera się w jednym pliku który na dodatek jest generowany automatycznie :)<br />
W niniejszym przykładzie stworzymy prostą bazę kontaktów. W pierwszej kolejności muszę stworzyć klasę dziedziczącą po SQLiteOpenHelper i przesłonić metody onCreate i onUpgrade, oraz stworzyć konstruktor sparametryzowany. Klasa ta będzie służyła do tworznia bazy danych i niezbędnych tabel.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzG73NQb2u5kDrimsDT1W3xxdPE5TAY-CcGMTB5R5aqud3Ek0pSzHphF0WQfpWNtQbv5-fzY6HtOWSpc7b9sA_HYIBqdCTvQ8fC9zad-DLkEOuEowgTYnIGR7nOs8uH6Hn7f4j5WSygxnn/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgzG73NQb2u5kDrimsDT1W3xxdPE5TAY-CcGMTB5R5aqud3Ek0pSzHphF0WQfpWNtQbv5-fzY6HtOWSpc7b9sA_HYIBqdCTvQ8fC9zad-DLkEOuEowgTYnIGR7nOs8uH6Hn7f4j5WSygxnn/s1600/1.png" /></a></div>
Konstruktor jako taki przyjmuje tylko jeden parametr – kontekst, który zresztą przekazuje następnie do konstruktora klasy po której dziedziczy. „Kontakty.db” to nazwa pliku bazy danych który zostanie utworzony. Jedynka w konstruktorze to wersja bazy danych. Możemy tę wartość zwiększać przy kolejnych zmianach w strukturze bazy.<br />
Podczas pierwszego skorzystania z obiektu naszej klasy ZarzadcaBazy, SQLiteOpenHelper zauważy brak pliku bazy (tutaj kontakty.db) i go utworzy a następnie wywoła metodę onCreate. W mojej metodzie onCreate, posługując się metodą execSQL obiektu klasy SQLiteDatabase tworzę tabelę w której będę przechowywał dane. Nic nadzwyczajnego – zwykły SQL. Metoda onUpgrade zostanie wywołana jeśli będziemy się odnosić do przestarzałej wersji bazy danych (silnik bazy będzie to wiedział na podstawie numeru wersji którą podajemy jako parametr konstruktora). Możemy tutaj np. zaimplementować wywołanie jakichś poleceń rozbudowujących czy zmieniających struktury bazy.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiTDE_CfRy2xn45n90X5Qd23N_MLdhDA93_1LSH5o3AxfTFzRhULmFQvBletuLdIYF1J6ld-mzICLyXJJl4cl1h0_X4hypnTXa2xfb4_8j4352IDcArFe-nX1PZxBuzqgb7uHrPmXiKH0M/s1600/2.1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgiTDE_CfRy2xn45n90X5Qd23N_MLdhDA93_1LSH5o3AxfTFzRhULmFQvBletuLdIYF1J6ld-mzICLyXJJl4cl1h0_X4hypnTXa2xfb4_8j4352IDcArFe-nX1PZxBuzqgb7uHrPmXiKH0M/s1600/2.1.png" /></a></div>
<br />
Teraz dodamy funkcjonalność odpowiedzialną za dodawanie, modyfikowanie, kasowanie i wyświetlanie danych. Na razie jest bardzo ubogo :) Kolejne dwie metody również dodaję do klasy ZarządcaBazy. Oczywiście schemat ten jest bardzo uprowadzony, raczej powinniśmy stosować oddzielne klasy DAO do zadań tego typu. Taka uproszczona forma ułatwi zrozumienie samej zasady działania, potem każdy może sobie to przerobić wg własnego uznania. Dodałem metodę dodajKontakt która będzie służyła do dodawania nowych kontaktów. Metoda ta przyjmuje 3 parametry które stanową dane dodawane później do tabeli tj imię, nazwisko, telefon.<br />
W linii 34 uzyskuję uchwyt do bazy. Korzystam tutaj z metody getWritableDatabase, ponieważ będę wprowadzał zmiany w bazie. W sytuacji gdybym chciał jedynie odczytywać dane, wywołałbym metodę getReadableDatabase. Linie 36-38 służą podstawieniu wartości do poszczególnych kolumn. Pierwszy parametr metody put to nazwa kolumny do której wstawiamy dane, drugi to wartość która ma do niej trafić. Nie muszę uzupełniać wszystkich. Swoją drogą przykładowo kolumny nr nie uzupełniam wcale. To jest kolumna typu „autoincrement”, a więc wartości w niej będą generowane automagicznie. W linii 16 wywołuję metodę insertOrThrow której przekazuję jako parametry nazwę tabeli do której wstawiane są dane, oraz listę wartości klasy ContentValues którą uzupełniałem wcześniej. Metoda ta służy do wstawienia wiersza do tabeli. W razie problemów wyrzuci wyjątek typu SQLExcepion, ale możemy go wyłapać obejmując to wywołanie blokiem try catch.<br />
Do klasy ZarzadcaBazy dodalem tez metodę dajWszystkie. Sluzy ona pobieraniu danych z bazy.<br />
W linii 43 zdeklarowałem kolumny jakie chcę odczytać, oraz ich kolejność. Nie muszę odczytywać wszystkich kolumn, ani też nie muszę wyciągać ich w takiej kolejności w jakiej są w tabeli. W linii 44 pobieram uchwyt do bazy. Tym razem wywołuję jednak metodę getReadableDatabase, ponieważ ie będę w bazie nic modyfikował, a jedynie czytał. Zasadniczo pobranie danych jest realizowane metodą query w linii 45. Podaję tutaj nazwę tabeli z której będę czytał dane, oraz jako parametr listę kolumn które będę pobierał. Pozostałe parametry (które są tutaj nullami) służą do stsowania warunków WHERE, Having, grupowania, sortowania etc.<br />
Przyszedł czas na dorobienie warstwy prezentacji. Przyklejam więc na swojej głównej aktywności komponent klasy TextView:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo5pmrbahVMeeWFnwBsPendShW1pVpSBZaSz8TIIJ7nVXRTWJVzRlFuf3MyZP-iCsCNqm8wtgeXC_MT4BihQPj2wYZDRQSOaubXtQo4xrTquEoG3vocfD_LsH4nBXy0A-LEYdwLxqai_Ps/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgo5pmrbahVMeeWFnwBsPendShW1pVpSBZaSz8TIIJ7nVXRTWJVzRlFuf3MyZP-iCsCNqm8wtgeXC_MT4BihQPj2wYZDRQSOaubXtQo4xrTquEoG3vocfD_LsH4nBXy0A-LEYdwLxqai_Ps/s1600/4.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
W ramach tego przykładu, do tabeli telefony dodam z użyciem dotychczas stworzonych funkcji 3 nowe kontakty, a następnie je wyświetlę. W metodzie onCreate aktywności głównej zasadniczy najważniejszy dla nas element mieści się w liniach 18-28. Linia 18 to stworzenie obiektu zarządcy bazy danych – a więc tej klasy która jest odpowiedzialna za łączenie się z bazą i operacje na niej. Konstruktor tej klasy wymaga podania konktekstu. Tutaj jako kontekt wskazuję „this”, a więc bieżącą aktywność. Z użyciem metody „dodajKontakt” zawartej w owej klasie, dodaję trzy nowe kontakty. W dalszej kolejności chcę je pobrać z bazy i wyświetlić na ekranie. Korzystam z metody dajWszystkie, która jednak zwraca obiekt klasy Cursor. Przetwarzanie takiego kursora nie jest najwygodniejsze więc za chwilę zajmiemy się przerobieniem wszystkiego w taki sposób by metoda dajWszystkie zwracała listę obiektów np. klasy Kontakt. Krok po kroku. Na razie mamy spartańskie warunki. <br />
W pętli while (linia 23) widzimy metodę moveToNext(), wykorzystanie tej metody pozwala nam na przesuwanie się po wyniku zapytania po linii w dół tak długo jak długo są jeszcze jakieś dane do przeczytania. W liniach 24-27 do definiowanych w pętli zmiennych przypisuję zawartość kolumn z kolejnych wierszy wg pozycji tej kolumny od lewej – stąd wywołania typu getInt(0), getString(3) – chodzi o numer kolumny od lewej strony wg kolejności jaką wymieniliśmy w metodzie dajWszystkie klasy ZarzadcaBazy. Zauważ że posługuję się też różnymi metodami w zależności od spodziewanego typu danych zwracanych z zapytania – mam na myśli metody getString i getInt. Pilnuj tego, ponieważ jeśli spróbujesz zrobić getInt lub np. getLong a w wyniku dostaniesz ciąg tekstowy , program wyrzuci wyjątek. W linii 28 zwyczajnie konkatenuję uzyskane informacje i wyświetlam je w kompononecie klasy TextView który przykleiłem tam przed momentem. Aby nie posklejało mi się to w jeden długi pozawijany ciąg – przed każdym kolejnym kontaktem doklejam „\n” czyli znak nowej linii.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzm8e5SCl-PKvFG-2RYnxbNRkGnXZ9zTc6PSPSgmiyxIPidqSfRJ_0fjLLaiAi9QPTIlxcyr_E05mYCshnlogJBu8etzwP5z-5-1CmdD7KoFKhDFTuXm2rhTRdWTVh2tGPdUOQNZ5Qn1BI/s1600/3.1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzm8e5SCl-PKvFG-2RYnxbNRkGnXZ9zTc6PSPSgmiyxIPidqSfRJ_0fjLLaiAi9QPTIlxcyr_E05mYCshnlogJBu8etzwP5z-5-1CmdD7KoFKhDFTuXm2rhTRdWTVh2tGPdUOQNZ5Qn1BI/s1600/3.1.png" /></a></div>
<br />
Efekt :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9CMYeatOSpf9YAiMNfz0_TLwIAnJ6nrBVOc65MUDb2xhjU1-Bx9uyMpq0RrIibNieEsUuhyphenhyphenXAwGg5xQRrdoem8EsFyWToWxWRioJPZefgFUcdmpxJqeZ9I07ZJO8slqkOMecOLJzZkSH/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI9CMYeatOSpf9YAiMNfz0_TLwIAnJ6nrBVOc65MUDb2xhjU1-Bx9uyMpq0RrIibNieEsUuhyphenhyphenXAwGg5xQRrdoem8EsFyWToWxWRioJPZefgFUcdmpxJqeZ9I07ZJO8slqkOMecOLJzZkSH/s1600/5.png" /></a></div>
<br />
<br />
Pokażę teraz ciekawą właściwość na bazie pewnego przykładu. Zawartość tej bazy danych będzie trwała pomiędzy kolejnymi uruchomieniami programu. Jeśli więc teraz uruchomię jeszcze raz ten program, a co za tym idzie po raz kolejny dodam trzy te same nowe kontakty, powinienem teraz zobaczyć zdublowane rekordy. Jedyne co powinno je odróżniać to identyfikatory rekordów tj. zawartość kolumny nr będącej kluczem głównym tabeli „telefony” a mającej własność „autoincrement”. Dokonuję więc drobnej kosmetycznej zmiany w kodzie:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqXXrit3g9aH9OFDeh3uhAic70JORv12EKVa_XCl4EdGCsbog7yctMn_0GCIoK5idSOIwViGvwg9uRH9Mjrjh1z6T5IHy3qoJY60azB1zD6C-XCVQGpDTPOxwjOHyxXOI5HSR0GdmQ1txe/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqXXrit3g9aH9OFDeh3uhAic70JORv12EKVa_XCl4EdGCsbog7yctMn_0GCIoK5idSOIwViGvwg9uRH9Mjrjh1z6T5IHy3qoJY60azB1zD6C-XCVQGpDTPOxwjOHyxXOI5HSR0GdmQ1txe/s1600/6.png" /></a></div>
<br />
W linii 28 dodaję jedynie wyświetlanie kolumny nr Żadnych innych zmian nie nanoszę. Efekt jest więc taki jak przewidywaliśmy:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcdo4Kwk7YLmD5PF0g_GRnAduWG-XSA1FAt_MNAqepHoy48jCeYls-OLtq7G2xw4LYIKRjjtO451h0A_vXi_YDRk_87L-g2wl_JY1ZpJRuRqBxkg95iVE4iVATaIcqmzF7lhJ6tU_Xs2di/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcdo4Kwk7YLmD5PF0g_GRnAduWG-XSA1FAt_MNAqepHoy48jCeYls-OLtq7G2xw4LYIKRjjtO451h0A_vXi_YDRk_87L-g2wl_JY1ZpJRuRqBxkg95iVE4iVATaIcqmzF7lhJ6tU_Xs2di/s1600/7.png" /></a></div>
<br />
Aby program był w jakikolwiek sposób użyteczny, oraz aby kod był przejrzysty, trzeba będzie dokonać kilku kolejnych zmian:<br />
Pozbyć się tego paskudnego „TextView” na samym początku listy.<br />
Dodać możliwość kasowania kontaktów<br />
Dodać możliwość aktualizowania kontaktów <br />
Stworzyć klasę „Kontakt” i posługiwać się obiektami tej klasy a nie listą elementów typu String.<br />
Dorobić metody pozwalające wyciągać dane kontakty wg wskazanych kryteriów (np. numeru identyfikacyjnego, numeru telefonu czy nazwiska).<br />
Dodać jakieś okienka umożliwiające wygodne dodawanianie, akatualizację i kasowanie kontaktów przez użytkownika. <br />
Odseparować kod odpowiedzialny za odczyt, zapis, aktualizację i kasowanie kontaktów do osobnej klasy.<br />
Rozwiązanie problemu nr 1. sprowadza się tylko do wyczyszczenia wyświetlanego w komponencie klasy TextView tekstu przed jego uzupełnianiem w pętli. Widzimy to w linii nr 22 :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPpoCzFZw6xNxve9rALuJGMYseOO1nUZH_EmEt7bLMd0dUjKbsAXJQvY5-aeOZJP51m9GvAqRBUOitsCsam5QQ6v3BFgY3Gaarlf2Aw2jqhpEBuqfm3it3CMYvdPzIrY9VonVeHkP3Myd4/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPpoCzFZw6xNxve9rALuJGMYseOO1nUZH_EmEt7bLMd0dUjKbsAXJQvY5-aeOZJP51m9GvAqRBUOitsCsam5QQ6v3BFgY3Gaarlf2Aw2jqhpEBuqfm3it3CMYvdPzIrY9VonVeHkP3Myd4/s1600/8.png" /></a></div>
<br />
Przy okazji widzimy że nasze 3 kontakty zostały dodane po raz kolejny ;) Z tym też musimy coś zrobić. In plus że mamy różne numery identyfikacyjne.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggI4ANZQgQ_n-aHWq65o6ubWbFgDSHfO0lMFMo5xNUbadILLlu2sqlsAAwOirNyZwT-OZkvdjrV7lqqfh7CUW44J68Tb_II4VwUiXpqGw9tMZ7ASmwR92nR5K-Q6EziXBVU_hG10VKbQsQ/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggI4ANZQgQ_n-aHWq65o6ubWbFgDSHfO0lMFMo5xNUbadILLlu2sqlsAAwOirNyZwT-OZkvdjrV7lqqfh7CUW44J68Tb_II4VwUiXpqGw9tMZ7ASmwR92nR5K-Q6EziXBVU_hG10VKbQsQ/s1600/9.png" /></a></div>
<br />
Dodam więc prostą metodę służącą do kasowania zbędnych rekordów. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeNCtz7Ov4slibzoMucs73BAU45OZtx8631BtfTYADvGXrViiKQzoWNYGT0AOMiPMLnTtnY_zFef0HWCz2zszNfIUZfil_3a3sxxsECS2iPuBH-DrrewylIu-cYMAXU3ScKYfHLqnhNkfB/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjeNCtz7Ov4slibzoMucs73BAU45OZtx8631BtfTYADvGXrViiKQzoWNYGT0AOMiPMLnTtnY_zFef0HWCz2zszNfIUZfil_3a3sxxsECS2iPuBH-DrrewylIu-cYMAXU3ScKYfHLqnhNkfB/s1600/10.png" /></a></div>
<br />
<br />
Oczywiście bazę podpinamy w trybie do zapisu, co widzimy w linii 50. W linii 51 definiuję listę elementów argumentów dla warunków where. Musi to być lista argumentów klasy String. Myślę że najlepiej będzie to wyjaśnić na przykładzie. Przyjrzyjmy się linii 52. Pierwszy warunek to nazwa tabeli z której chcemy kasować wiersze. Drugi to warunki które mają trafić po klauzuli where. Samego where już tutaj nie wymieniamy. Zamiast wartości liczbowych czy tekstowych które miałyby się pojawić w warunkach, stawiamy znak zapytania. Gdybyśmy mięli więcej niż jeden parametr, to ten ciąg tekstowy mógłby wyglądać np. tak: „department_id=? and manager_id=? and salary>?”. System oczekiwałby w takiej sytuacji podania mu trzech wartości które trafią pod nasze znaki zapytania. Listę tychże właśnie wartości podaję w postaci kolekcji String zdefiniowanej w linii 51 poprzez trzeci parametr metody delete. W moim przypadku lista składa się z całego jednego elementu który na dodatek w sposób mało elegancki, aczkolwiek popularny rzutuję na typ String. Czyli podsumowując:<br />
Argument nr 1 to nazwa tabeli z której kasujemy<br />
Argument 2 to warunki dla klauzuli where<br />
Argument 3 to lista wartości które mają trafić do parametrów warunków where określonych jako argument nr 2 metody delete :)<br />
Przetestujmy teraz działanie naszej nowej metody. <br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil2tvyXtt7xs_z0QQUKtJLtlX9FEnPC1kdne0Fqsw9YYGUFPCRzSJCgGqQjKOtBQitocQKlXdwZIOKmsVOOo0N-1aaaLwXJxr-NwqWiaLl6xTk0whIq5YwBVRxr2AR_Aag06ROBZVBTkS7/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEil2tvyXtt7xs_z0QQUKtJLtlX9FEnPC1kdne0Fqsw9YYGUFPCRzSJCgGqQjKOtBQitocQKlXdwZIOKmsVOOo0N-1aaaLwXJxr-NwqWiaLl6xTk0whIq5YwBVRxr2AR_Aag06ROBZVBTkS7/s1600/11.png" /></a></div>
<br />
Troszkę przerobiłem kod naszej głównej aktywności. Linie od 19 do 21 odpowiedzialne za dodawanie nwych wierszy po prostu wykomentowałem, za to 2 liniach 25-27 dodałem kasowanie wszystkich kontaktów o identyfikatorach od 4 do 9. Wyświetlanie kontaktów pozostało takie jakie było na początku. Aktualny stan :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhVzUKyxwp9juE_rQCFAognztoRsAB2hwXFdqtPAcPcnFvqdCjl2ovl1ZClLalPCNZkJj0MyJ6VK9Q3HUGZz9TlB0LtjBtoZCsnk31R1VrhMHBA8XgW_6NiQAlnT-iMq9UXu-lIw6khb_d/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhVzUKyxwp9juE_rQCFAognztoRsAB2hwXFdqtPAcPcnFvqdCjl2ovl1ZClLalPCNZkJj0MyJ6VK9Q3HUGZz9TlB0LtjBtoZCsnk31R1VrhMHBA8XgW_6NiQAlnT-iMq9UXu-lIw6khb_d/s1600/12.png" /></a></div>
<br />
Przydałaby się teraz metoda pozwalająca na modyfikowanie wpisów w bazie.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2sab2USZ08k6ZE4PkeU3rYFGZgQXmJg3L2kc7yW_sUdB0qCiDhKfhogl0jwy2lN__6YYRD0TTe-jFAZ_sRDTX-u-xR6Yhr005pzC_1FDagxGPot_2T6OQJggzHzuwsgA4PwMwrTNJ1DN8/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2sab2USZ08k6ZE4PkeU3rYFGZgQXmJg3L2kc7yW_sUdB0qCiDhKfhogl0jwy2lN__6YYRD0TTe-jFAZ_sRDTX-u-xR6Yhr005pzC_1FDagxGPot_2T6OQJggzHzuwsgA4PwMwrTNJ1DN8/s1600/13.png" /></a></div>
<br />
Troszkę przemieściłem metody w klasie „ZarzadcaBazy”, ponieważ nowo dodana metoda „aktualizujKontakt” ma parę elementów wspólnych z wcześniej napisanymi metodami „kasujKontakt” i „dodajKontakt”. W linii 49 określiłem jakie parametry przyjmuje metoda. Pierwszy to identyfikator wiersza (unikalny numer z kolumny nr) wg którego będę wybierał wiersz do aktualizacji. Pozostałe to wartości do aktualizacji. Przyjąłem że nie będę rozgraniczał aktualizji poszczególnych kolumn , robił osobnych metod ani skomplikowanych bloków warunkowych żeby aktualizować tylko te faktycznie zmienione kolumny. Po najmniejszej linii oporu. Zakładam że po prostu zczytam wiersz, zmienię jedno czy więcej pól i przekażę do tej metody tak by zaktualizowała wszystkie kolumny, niezależnie od tego czy faktycznie coś się w nich zmieniło czy nie.<br />
Podobnie jak działo się to przy dodawaniu nowego wiersza, tak i tutaj w liniach 51-54 podaję listę wartości które mają trafić do poszczególnych kolumn. Są to wartości które ulegają zmianie – te które podaję przez parametry metody. Tak jak i wcześniej metoda put przyjmuje jako argumenty nazwę kolumny w której aktualizujemy wartość, oraz wartość dla tej kolumny. Podobnie jak w przypadku metody kasujKontakt, tak i tutaj w linii 55 wartości które zostaną podane jako argumenty warunku where. W linii 56 to już wywołanie metody update która przyjmje jako parametry kolejno: nazwę aktualizowanej tabeli,zaktualizowane wartości kolumn, warunek where, wartość lub wartości które mają trafić do warunku where w miejsce znaków zapytania. <br />
Przerabiam teraz główną aktywność tak, aby przed wyświetleniem wywołać metodę aktualizującą i zmienić numer telefonu Dziadka Mroza:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGMC_rEjt7KQeJiRVjYCxEQ4B9vk7cy1vfkqyPGTn-yl_lPWrVBmpGkkr-fMmItLH7GrEltRuyQMhOihjyRykNFf9qS-BXbuacILo2ECYQaMDNBBvKGGGHgdGl_-yr4pQMICV6LNjKHl-y/s1600/14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGMC_rEjt7KQeJiRVjYCxEQ4B9vk7cy1vfkqyPGTn-yl_lPWrVBmpGkkr-fMmItLH7GrEltRuyQMhOihjyRykNFf9qS-BXbuacILo2ECYQaMDNBBvKGGGHgdGl_-yr4pQMICV6LNjKHl-y/s1600/14.png" /></a></div>
<br />
<br />
Efekt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPm3M-6S9jaNjSRaX77w-oxRBVryQKfIZPL0RDEauubRxY7DUb3JP6ZK4M2iulrOhG6AYOdWbqrFhrKIG6RcB5SWNlBIdphVGAiSwxxlT95l-4mhFbHhtCQmdeyX4xNn_DzU1NxOZydDzE/s1600/15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPm3M-6S9jaNjSRaX77w-oxRBVryQKfIZPL0RDEauubRxY7DUb3JP6ZK4M2iulrOhG6AYOdWbqrFhrKIG6RcB5SWNlBIdphVGAiSwxxlT95l-4mhFbHhtCQmdeyX4xNn_DzU1NxOZydDzE/s1600/15.png" /></a></div>
<br />
<br />
Oczywiście programistyczni puryści podniosą zarzut, że jakto tak z palca podawać wszystkie wartości włącznei z tymi nie zmienianymi, że te dane powinny być pobrane z bazy , zwrócone przez jakąś metodę w postaci obiektu, a następnie po zmianie wartości wybranego pola przekazane jako obiekt do metody aktualizującej w bazie. I jak najbardziej będą mięli rację :) Z tym, że aby to osiągnąć potrzebujemy najpierw przerobić to na troszkę bardziej obiektową formę, oraz stworzyć metodę zwracającą wybrany wiersz wg identyfikatora :). Zacznijmy więc od zdefiniowania klasy która będzie reprezentować pojedynczy wiersz. Starałem się tak definiować żeby pola w klasie odpowiadały nazwom kolumn w tabeli:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXXUsoZe6HkqN0kCw-U7wkZnC7TttdKf6efvnSeJOV-0F6jZFIzKcIUx7cYDoXCunl4T4s8SDv8ozbnU08QMpvaG_6riMeL0LD1va6QoWJH4Tg89Bbbz7k7Sm97pKrwYuIJsBdcHfBcn4u/s1600/16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXXUsoZe6HkqN0kCw-U7wkZnC7TttdKf6efvnSeJOV-0F6jZFIzKcIUx7cYDoXCunl4T4s8SDv8ozbnU08QMpvaG_6riMeL0LD1va6QoWJH4Tg89Bbbz7k7Sm97pKrwYuIJsBdcHfBcn4u/s1600/16.png" /></a></div>
<br />
<br />
Jest taka dobra praktyka programistyczna, by pola w klasach typu POJO nie były publiczne, a prywatne, natomiast dostęp do nich uzyskiwać poprzez tak zwane gettery i settery. Dzięki temu mamy większą kontrolę nad zawartością tych pól. W środowisku Eclipse jest nawet specjalna funkcja do automatycznego generowania tego typu metod. Wybieramy z menu dostępnego pod prawym przyciskiem myszki:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-iYU5sSdZ3S0VTyk1wsOQdhVYzsBPkDTDm9UP8rOxvEwL1C7W0qnd6Hptjnj29JlaKojgXQZMR1WKrSoKg3lhhYWKaxYLrEWzpmq15R50NY8mgy1RNiaepu8AZkA_Mzzlar8y0GHZHBVc/s1600/17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-iYU5sSdZ3S0VTyk1wsOQdhVYzsBPkDTDm9UP8rOxvEwL1C7W0qnd6Hptjnj29JlaKojgXQZMR1WKrSoKg3lhhYWKaxYLrEWzpmq15R50NY8mgy1RNiaepu8AZkA_Mzzlar8y0GHZHBVc/s1600/17.png" /></a></div>
<br />
<br />
Gdy pojawi się okno, zaznaczamy wszystkie opcje:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtpeIW8Vby360F3lKU7c176dekJN6QGoh1SM9KSuoNCM47gv4NXDQu4mm-GM-OK7rDCILDdhAg6EDNcux3gZSjZsPMIb9Ggm-tA3maUq1UzV-3YcdscDmGFvFD-TY0jXKgQoYTV6nfqTtP/s1600/18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjtpeIW8Vby360F3lKU7c176dekJN6QGoh1SM9KSuoNCM47gv4NXDQu4mm-GM-OK7rDCILDdhAg6EDNcux3gZSjZsPMIb9Ggm-tA3maUq1UzV-3YcdscDmGFvFD-TY0jXKgQoYTV6nfqTtP/s1600/18.png" /></a></div>
<br />
i klikamy dostępny na dole przycisk „OK”. W tej chwili nasza klasa powinna wyglądać mniej więcej tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn17bHM8OSB4pe2Bhei2N0dGA1r41CNMK2ZbIy40a61cgZb1yVNWd59564VvMMe4-4Cc3h4mrWsQDudFzfz3YwNH3wOOUq8ajhDBSnsF2J3l34gRIZM5nKWOIVCnLmFvfPyxHXdlVtXv-I/s1600/19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhn17bHM8OSB4pe2Bhei2N0dGA1r41CNMK2ZbIy40a61cgZb1yVNWd59564VvMMe4-4Cc3h4mrWsQDudFzfz3YwNH3wOOUq8ajhDBSnsF2J3l34gRIZM5nKWOIVCnLmFvfPyxHXdlVtXv-I/s1600/19.png" /></a></div>
<br />
<br />
Stworzę teraz w naszym ZarzadcyBazy metodę odpowiedzialną za pobieranie z bazy i oddawanie wiersza w postaci obiektu nowo stworzonej klasy Kontakt. Dodaję do klasy ZarzadcaBazy metodę dajKontakt. W linii 67 tworzę na razie pusty obiekt klasy kontakt, który po uzupełnieniu zostanie z metody zwrócony. W linii 68, tak jak i w każdym dotychczasowym przypadku podpinam zaczep do bazy. W linii 69 definiuję kolumny które będę czytał. Widzimy że analogiczny fragment pojawia się po raz kolejny, trzeba więc będzie go odseparować w przyszłości. Linia 70 to tak jak i wcześniej podstawienie wartości która trafi pod znak „?” w warunku where. Linia 71 to zasadnicze wykonanie zapytania. Argumenty odpowiedzialne za grupowanie, warunek having, sortowanie i limitowanie ilości zwracanych wierszy (wg wymienionej kolejności) pozostawiam nullowe, ponieważ żadnej z tych operacji nie będę wykonywał. Jeśli w wyniku zapytania zostanie zwrócony przynajmniej jeden wiersz (co sprawdzam w linii 72), przechodzę do czytania danych z pól wiersza i uzupełniania obiektu. W linii 73 przechodzę do pierwszego (i w tym przypadku jedynego) wiersza. Linie 74-77 to znane już z wcześniejszych przypadków czytanie danych, tym razem jednak nie przypisuję ich do zmiennych, a przy użyciu setterów uzupełniam obiekt klasy kontakt. W linii 79 uzupełniony już (miejmy nadzieję – mogliśmy przecież poszukiwać kontaktu po numerze który w tabeli nie występuje) obiekt klasy kontakt zwracam z metody. Cała metoda:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAueahmz3KyBaq667sb-TnWPgVk8G0cjULmg6nF4HM2cWY2gpIEmk3mhhvLez1uSshSyhpKZcfT2QDCTvWkwHg879s9OqTT3YpMseVrqr3Q1a2UvtXA3_A0OmOped3EIndubyvJUbrwMhd/s1600/20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhAueahmz3KyBaq667sb-TnWPgVk8G0cjULmg6nF4HM2cWY2gpIEmk3mhhvLez1uSshSyhpKZcfT2QDCTvWkwHg879s9OqTT3YpMseVrqr3Q1a2UvtXA3_A0OmOped3EIndubyvJUbrwMhd/s1600/20.png" /></a></div>
<br />
<br />
Aby przetestować nową metodę przerabiam ponownie główną aktywność:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwdyjniOFqBPpELVjbAFjC5F3mSTt1qdMUGXOopA8yfe2sSurhvvuHDPAomlDQ-JGMYllmEOot1cdqP5hkXDWKc44Bd1p74sBN58FT9s34lCAP1OxPqQOo6vcLe3cV9bTgx19ZZMciq10K/s1600/21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwdyjniOFqBPpELVjbAFjC5F3mSTt1qdMUGXOopA8yfe2sSurhvvuHDPAomlDQ-JGMYllmEOot1cdqP5hkXDWKc44Bd1p74sBN58FT9s34lCAP1OxPqQOo6vcLe3cV9bTgx19ZZMciq10K/s1600/21.png" /></a></div>
<br />
<br />
Efekt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMn5dNWUUpxMNkD0kgcCkrXoaAw3lI6zUQkUDxeVDkqC71mRHa9ETA6lBo_kcBqhGmQhYac94Il9HrX3PFT23Ayyprxgk7gFfbqxrttZWAuCO_49iVFy34kkYn1Q-Yq9fmWqYYcWsaiLgP/s1600/23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMn5dNWUUpxMNkD0kgcCkrXoaAw3lI6zUQkUDxeVDkqC71mRHa9ETA6lBo_kcBqhGmQhYac94Il9HrX3PFT23Ayyprxgk7gFfbqxrttZWAuCO_49iVFy34kkYn1Q-Yq9fmWqYYcWsaiLgP/s1600/23.png" /></a></div>
<br />
<br />
Przerobię teraz pozostałe metody z klasy ZarzadcaBazy, tak by całość była jako tako obiektowa :)<br />
Zacząłem od zmiany metody dodajKontakt. Zamiast przyjmować wartości pojedynczych pól jako osobne argumenty metody, przekazuję po prostu jeden obiekt klasy Kontakt, który zostaje dodany do bazy. Jeszcze jedna drobna różnica jest widaczna w liniach 36-39. Zamiast korzystać z osobnych zmiennych reprezentujących wartości do poszczególnych pól, po prostu wydobywam niezbędne dane z obiektu, z użyciem zdefiniowanych w klasie Kontakt getterów.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiylH_ROtoT1wVmj3_keY_F0S-n59zUaFwNnQIAnHFZV7HbtlRK3muIWHQV29QCTfcUgLaM8n29JGQeAaDRXupw5mhcYl2r8q_WP1t7DDdwrEtizsZjdL1ntUK6R9fp1r172C2P5ysD3IXF/s1600/24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiylH_ROtoT1wVmj3_keY_F0S-n59zUaFwNnQIAnHFZV7HbtlRK3muIWHQV29QCTfcUgLaM8n29JGQeAaDRXupw5mhcYl2r8q_WP1t7DDdwrEtizsZjdL1ntUK6R9fp1r172C2P5ysD3IXF/s1600/24.png" /></a></div>
<br />
<br />
<br />
W podobny sposób przerobiłem metodę aktualizującą wiersze:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_FVyuwbf0wQDnyWkpqcUE0R6eNHx1F1pxEga6ss4qSo2q2RERJj1Z0-WwO47DJwl4Tt9_QuaUaqkMBCWRAVYmKr4iS2NGZgSa-lQUX0DfZX-YUGkoYozaRXjs7VjN_vDPOjZh4pNYxLYC/s1600/25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_FVyuwbf0wQDnyWkpqcUE0R6eNHx1F1pxEga6ss4qSo2q2RERJj1Z0-WwO47DJwl4Tt9_QuaUaqkMBCWRAVYmKr4iS2NGZgSa-lQUX0DfZX-YUGkoYozaRXjs7VjN_vDPOjZh4pNYxLYC/s1600/25.png" /></a></div>
<br />
Bardzo nie podoba mi się również dotychczasowe rozwiązanie pobierania wszystkich kontaktów:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZkIrBlsDsAqBwEiMkvUu0V0NyeBbEcAYZieg4D5fkJNjur4BHLpWO8CrWTSSgHY5JHhHIPPYaORNO0Xi8000Hc0PNvDTGgXxL_mqrMkh72uU0VDzFEQXYmHIE4MTU7tgzTp5TMaz6NSHL/s1600/26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhZkIrBlsDsAqBwEiMkvUu0V0NyeBbEcAYZieg4D5fkJNjur4BHLpWO8CrWTSSgHY5JHhHIPPYaORNO0Xi8000Hc0PNvDTGgXxL_mqrMkh72uU0VDzFEQXYmHIE4MTU7tgzTp5TMaz6NSHL/s1600/26.png" /></a></div>
<br />
Metoda zwraca obiekt klasy Cursor, którego przetwarzanie nie należy do najwygodniejszych. Tą metodę przerobię też tak, by zwracała listę (klasy LinkedList) obiektów.<br />
Po paru poprawkach metoda dajWszystkie działa tak jak sobie tego życzyliśmy. Sposób pobierania danych jest taki sam, jedynie dodałem w liniach 67-73 przetwarzanie kursora i ładowanie danych do przygotowanej wcześniej (w linii 63) listy kontaktów.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_-1I50KDAZJffYBqCIdCacCKARsVRzdzymfcpI2gT84PDaQEySdhIrs5UZDhXbshfMX3dMQ_4YptMXIjkQlhzKmE8-ArEoGP9_gAZwTEpZbqfvbQ_7wpPxPVxqN26nLTuzhmkiDEHysRe/s1600/27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_-1I50KDAZJffYBqCIdCacCKARsVRzdzymfcpI2gT84PDaQEySdhIrs5UZDhXbshfMX3dMQ_4YptMXIjkQlhzKmE8-ArEoGP9_gAZwTEpZbqfvbQ_7wpPxPVxqN26nLTuzhmkiDEHysRe/s1600/27.png" /></a></div>
<br />
Przetwarzanie takiej listy jest później dużo wygodniejsze. Poniżej wykorzystanie nowej wersji metody dajWszystkie w głównej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHA-NqW4BuBMYc6zZXdf10cM9ZhnMtfaj4zQoa39hTszzTCTZXXdhlF8_Tc-rFkHSRTOfbv8FBiHJ6ILktyKqgOYtsLEzwTL6aa0CoQid7wEwfAc4R21TytU1D7vnqRLh6WvxujRZ4h68I/s1600/28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHA-NqW4BuBMYc6zZXdf10cM9ZhnMtfaj4zQoa39hTszzTCTZXXdhlF8_Tc-rFkHSRTOfbv8FBiHJ6ILktyKqgOYtsLEzwTL6aa0CoQid7wEwfAc4R21TytU1D7vnqRLh6WvxujRZ4h68I/s1600/28.png" /></a></div>
<br />
<br />
Przy okazji pojawia się nam tutaj nowa metoda: d klasy Log. Wywołuje się ją w taki sposób:<br />
Log.d(„tag”,”text”); a efekt wygląda tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBwD3Sqvvd73DqfTcLUvI8adikuZgXI5J3tfDyAtfJLORNi30EHaJu-0LXWAvuzpnJUvs42Osr6lpVMFQHfDLnOWt9lqsnNQMN2oLTxyOR5K0gsk_WH0VId2vdYXsxAHs6U_vB9fhXkBhj/s1600/29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjBwD3Sqvvd73DqfTcLUvI8adikuZgXI5J3tfDyAtfJLORNi30EHaJu-0LXWAvuzpnJUvs42Osr6lpVMFQHfDLnOWt9lqsnNQMN2oLTxyOR5K0gsk_WH0VId2vdYXsxAHs6U_vB9fhXkBhj/s1600/29.png" /></a></div>
Sprawdza się to zwłaszcza w zastępstwie System.out.println którego może nieco brakować programistom Javy ;)<br />
<br />
Taki jeszcze drobny, acz istotny element. W tych przykładach tego nie wykorzystamy, ale może nastąpić konieczność pogrupowania, zastosowania klauzuli having, sortowania, czy limitowania ilości wierszy w zapytaniu. Trzeba odpowiednio podać parametry w wywołaniu metody query. Najlepiej będzie pokazać na przykładzie. Gdybym zechciał np. odebrać wynik takiego zapytania:<br />
<br />
SELECT AVG(SALARY) , DEPARTMENT_ID FROM EMPLOYEES GROUP BY DEPARTMENT_ID ORDER BY DEPARTMENT_ID DESC;<br />
<br />
To wywołanie metody query musiałoby wyglądać tak:<br />
SQLiteDatabase db = getReadableDatabase();<br />
String[] kolumny={"avg(salary) as srednia","department_id"}; <br />
Cursor kursor = db.query("telefony",kolumny,null,null,”department_id”,null,<br />
”department_id desc”);<br />
<br />
Parametry wg kolejności :<br />
1. Nazwa tabeli z której czytamy<br />
2. Kolumny które czytamy<br />
3. Warunki where<br />
4. Wartości do warunków where<br />
5. Kolumna/kolumny po których grupuję<br />
6. Warunek do klauzuli having<br />
7. Sposób sortowania<br />
<br />
Tutaj już każdy może uznać czy chce taką postać wykorzystywać czy nie. Moim zdaniem to średnio wygodne i mało intuicyjne. Poza tym, co jeśli chciałbym stosować złączenia między tabelami, podzapytania etc? Zamiast takiej nieco abstrakcyjnej formy wywoływania zapytań z użyciem metody query, możemy wykorzystać metodę rawQuery. Do naszego zarządcy bazy dopisałem metodę wyciągającą kontakty o nazwisku które przekazuję jako parametr.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXazUCuFtcsLVIJkxJJiOpUSFrpFWhN2tVuVZxtJb-1eJJLP_X6Hoj7LqkJaWDLYBHXGm8X9W0Uauqx9CYxSkY8bTD0EmjGcPGcPmw3zTSO38ug_lY5Alc-beqhgFtK7-1uBuPrHS0RYRA/s1600/30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiXazUCuFtcsLVIJkxJJiOpUSFrpFWhN2tVuVZxtJb-1eJJLP_X6Hoj7LqkJaWDLYBHXGm8X9W0Uauqx9CYxSkY8bTD0EmjGcPGcPmw3zTSO38ug_lY5Alc-beqhgFtK7-1uBuPrHS0RYRA/s1600/30.png" /></a></div>
<br />
W przypadku metody rawQuery, nie używamy żadnych dodatkowych parametrów do określania grupowania, czy sortowania. Wszyskie tego typu rzeczy określamy w samym zapytaniu SQL bez żadnych udziwnień. Jak widać w liniach 98-100 potrzebny parametr zwyczajnie dołączyłem na zasadzie sklejania tekstu, warunek WHERE i sortowanie określone jest w samym SQL. Zdecydowanie preferuję tę formę, jako bardziej przyjazną :) W liniach 101-105 wstawiłem też zakomentowaną alternatywną postać tego samego wywołania. W tym przypadku zamiast sklejać zapytanie, posłużyłem się znakami zapytania i podstawiam wartości które pod te znaki zapytania mają trafić w trakcie wykonania z listy podanej jako drugi parametr metody rawQuery (tutaj lista jest po prostu jednym elementem typu String). Samo pobieranie danych z takiego kursora przebiega dokładnie tak jak wcześniej. Ponieważ wartości z kolejnych kolumn pobieram podając jej numer zaczynac liczyć od 0 od lewej strony, nie muszę nawet robić żadnych aliasów dla kolumn które wynikają np. z działania funkcji.andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com41tag:blogger.com,1999:blog-5593586859352074500.post-8598008335141342202014-02-17T09:26:00.003-08:002014-02-17T09:26:42.715-08:00Wielowątkowość w AndroidzieJak bardzo potrafi irytować zawieszanie się aplikacji wie każdy z nas. Teraz wyobraź sobie, że Twoja aplikacja na Androida ma do wykonania jakieś czasochłonne czynności, wystarczy że będzie musiała pobrać jakieś dane czy obraz z internetu. Jeśli zrobisz to w ramach głównej wątku programu, UI przestanie odpowiadać, a użytkownik Twojego programu będzie życzył Ci właśnie tego, czego zapewne Ty życzysz czasem programistom Internet Explorera :) Aby się o tym przekonać, wystarczy przykleić na ekranie przycisk, a jako jego reakcję oprogramować zdarzenie oczekiwania. Przyklej guzik:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDpOMxoqG5RDERsXfz_Nvmp2zYL-c0SpIYyuXPTh0UoJcwbQd3bXuKbf0bcEkTdQe_3mkvOjGpyH2UrvznOcoOY_vXV43BkzmiNj5jQbdX7k75f7_9fLo5J8gMVD1y-bTxhxwTWI5AIVw3/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDpOMxoqG5RDERsXfz_Nvmp2zYL-c0SpIYyuXPTh0UoJcwbQd3bXuKbf0bcEkTdQe_3mkvOjGpyH2UrvznOcoOY_vXV43BkzmiNj5jQbdX7k75f7_9fLo5J8gMVD1y-bTxhxwTWI5AIVw3/s1600/1.png" /></a></div>
<br />
a jako obsługę jego naciśnięcia wpisz taki kod:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Ac42DwxICpXhcX9WNJChsdsKyH1B9l5aTTBiAovoEvcx3im3BE3qyULAphpsq9kdhPcya0Qi6rfD0BAhfW7PoyXgdRm65D66EaE5WehIwrSOGN-ZsMO2mRFCfciONKf3J_UgsAzy6gSj/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_Ac42DwxICpXhcX9WNJChsdsKyH1B9l5aTTBiAovoEvcx3im3BE3qyULAphpsq9kdhPcya0Qi6rfD0BAhfW7PoyXgdRm65D66EaE5WehIwrSOGN-ZsMO2mRFCfciONKf3J_UgsAzy6gSj/s1600/2.png" /></a></div>
<br />
<br />
W zasadzie interesują nas tylko linie 24-32. Reszta to otoczka. Jak widzimy, w chwili naciśnięcia przycisku wywoływana jest instrukcja Thread.sleep(10000). To sprawi że aplikacja zaczeka 10 sekund. Spróbuj to uruchomić na dowolnym urządzeniu i np. wywołać menu pogramu (tym dedykowanym guzikiem do menu). Zobaczysz że nic się nie stanie, aplikacja po prostu się zawiesiła.<br />Trzeba więc wydzielić osobny wątek i puścić go w tle do realizowania czasochłonnych zadań. Można zrobić to „tradycyjnie” po javowemu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizPLMPmjDk8xO8XqS-Nr09Ioh3kaP6Bgelo0rchQumPW-hom0ZjGiRn2JETe32CN5JchjrbqYNuUXF2aDPvJTtjF8W8hwPKrVSi3fVRK8V6BXjZHhZ4m6bH6MugH5UCoHW9y3Hf-dFRbVM/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEizPLMPmjDk8xO8XqS-Nr09Ioh3kaP6Bgelo0rchQumPW-hom0ZjGiRn2JETe32CN5JchjrbqYNuUXF2aDPvJTtjF8W8hwPKrVSi3fVRK8V6BXjZHhZ4m6bH6MugH5UCoHW9y3Hf-dFRbVM/s1600/4.png" /></a></div>
<br />
Możesz uruchomić tak przerobiony program, a przekonasz się że wątek działający w tle nie blokuje innych elementów interfejsu. Jest oczywiście pewne „ale”. Jeśli spróbujesz sięgnąć z poziomu kodu do jakichkolwiek komponentów wizualnych z poziomu takiego wątku, dostaniesz taki wyjątek:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuwBehDRJfUCSAtW5Z4u9iw2Gfr1xqq042KK5DENUlQ0zVnZFGHCwdzNneoH6BsFV_6LBUdIilx4p5sHjYPBacza4oWVN8WnGYC4zqYKUwxR1hqd4gA_bOjXYRimzqc-JugXdqltyph1PN/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuwBehDRJfUCSAtW5Z4u9iw2Gfr1xqq042KK5DENUlQ0zVnZFGHCwdzNneoH6BsFV_6LBUdIilx4p5sHjYPBacza4oWVN8WnGYC4zqYKUwxR1hqd4gA_bOjXYRimzqc-JugXdqltyph1PN/s1600/5.png" /></a></div>
<br />
Myślę że komunikat tego wyjątku jest zrozumiały. Na potrzeby Androida powstał zupełnie osobny mechanizm wielowątkowości. Wykorzystamy tutaj klasę AsyncTask która jak sama nazwa wskazuje, służy do wykonywania zadań asynchronicznie. Wykorzystamy też jeden ciekawy gadżet – ProgressBar. Jest dostępny na palecie Form Widgets.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16yQinDfJBoF_F2U9B-VjMSJx4xP4zJGeYFhHakifi8ck6VaQ19JtvZUD2tJXc6v1YcRgT2cxL0pXyDPynY98xus1lEHSnsIAuhvTrAVD2XlYQIHAUDTLBOuqb2hOW3E-g0Nb5P3VRUan/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh16yQinDfJBoF_F2U9B-VjMSJx4xP4zJGeYFhHakifi8ck6VaQ19JtvZUD2tJXc6v1YcRgT2cxL0pXyDPynY98xus1lEHSnsIAuhvTrAVD2XlYQIHAUDTLBOuqb2hOW3E-g0Nb5P3VRUan/s1600/6.png" /></a></div>
<br /><br />
Chcemy żeby użytownik widział, że program coś wykonuje, a nie po prostu zwisł. Taki ProgressBar domyślnie jest widoczny i wykonuje się animacja. My zrobimy tak, żeby na począŧku nie był widoczny, pokażemy go gdy puścimy wątek i schowamy gdy go zakończymy.<br />
Wykorzystanie wcześniej wspomnianej klast AsyncTask polega na stworzeniu klasy która po niej dziedziczy i zaimplementowaniu paru metod. Do klasy naszej aktywności dodaję wewnętrzną klasę OsobnyWątek dziedziczącą po AsyncTask. Nic nie stoi na przeszkodzie by była to w ogóle osobna klasa. Pojawiło się tutaj też coś nowego – element <Void,Void,Void> oraz dziwna parametryzacja metod doInBackground i onPostExecute. Związane jest to z pojęciem klas generycznych, ogólnych. Tym zagadnieniem nie będziemy się tutaj zajmować, nie jest związane bezpośrednio z tematem wielowątkowości w Androidzie. Obiekty klasy OsobnyWatek będą stanowiły osobne wątki w ramach aplikacji. Zauważ że mamy tutaj trzy metody przesłaniające takie metody z klasy po której dziedziczymy. Metoda onPreExecute jest automatycznie wywoływana przy starcie wątku. Wyświetlam tutaj nasz ProgressBar. Kółko staje się widoczne i zaczyna „pracować”. Daję też komunikat na konsolę. W tej metodzie, oraz w metodzie onPostExecute możemy bez obaw odwoływać się do komponentów graficznych w naszej aktywności. Nie robimy tego za to w metodzie doInBackground. To co ma się dziać w ramach wątku – te długotrwałe czynności piszemy właśnie w metodzie doInBackground. Tutaj wyrzucam na konsolę informację, oraz wstawiam 10 sekundowe oczekiwanie. Metoda onPostExecute jest wywoływana automatycznie w chwili zakończenia wątku. Wyświetlam tutaj informację o zakończeniu i chowam ProgressBar.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMsev9Ij4NGeRAb520sHnR9yL-EQhXpAqkuKmcgvntYAqGSO9N0SeWGjGfX84phlPOC-4DiI2Fn5akNGvpWu-H3NxpBIwVaReyuRsjEFLFc1sSsrjXaTfOJPZXPwngYiLHc9DPF_fkoseN/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhMsev9Ij4NGeRAb520sHnR9yL-EQhXpAqkuKmcgvntYAqGSO9N0SeWGjGfX84phlPOC-4DiI2Fn5akNGvpWu-H3NxpBIwVaReyuRsjEFLFc1sSsrjXaTfOJPZXPwngYiLHc9DPF_fkoseN/s1600/7.png" /></a></div>
<br />
<br />
Zobaczmy teraz co się dzieje w metodzie onCreate:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMlS_WT6orNAA3JMsaRYK2_oA38Axc55mg_vFjLBZ1aii0TUt2ihcCfkNEGZXwEdR0IDSmhYfZcuvqbh0ycTzRM-ftXc6VAJMBfJ-2rO1pj06SixYaZRJZyPW8gdg59KLAWFxBcqg1tIIS/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMlS_WT6orNAA3JMsaRYK2_oA38Axc55mg_vFjLBZ1aii0TUt2ihcCfkNEGZXwEdR0IDSmhYfZcuvqbh0ycTzRM-ftXc6VAJMBfJ-2rO1pj06SixYaZRJZyPW8gdg59KLAWFxBcqg1tIIS/s1600/8.png" /></a></div>
<br />
<br />
W linii 55 na początek chowam ProgressBar. Będzie on wyświetlany tylko w trakcie działania wątku w tle. Jako reakcję na naciśnięcie przycisku uruchamiam nasz wątek (linia 60).<br />Program po uruchomieniu i kliknięciu przycisku wygląda tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfFCUzbZlO5DIkFA8wbYD_I6Fmu8SaTeVVepg32vgn4qI69HDOESyPz0BORtdK9CFisiuwCJMh7nHtYcs56WAfJW4H8W9OWoG44EmaIzSFyID0yGG5Qi61rA8TqDZMWj-Y3ROpLD9zuScT/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjfFCUzbZlO5DIkFA8wbYD_I6Fmu8SaTeVVepg32vgn4qI69HDOESyPz0BORtdK9CFisiuwCJMh7nHtYcs56WAfJW4H8W9OWoG44EmaIzSFyID0yGG5Qi61rA8TqDZMWj-Y3ROpLD9zuScT/s1600/9.png" height="271" width="320" /></a></div>
<br /><br />
Zawartość konsoli LogCata po zakończeniu całego procesu:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi568TvPU2aKlSqAS1CmFZCaCAU6R9UYKeTjy8apbIeSCkUsL_VokN7Fp02meHlYvBYEK8kM1sPrh3cg8Iz68qnIB3g_xd49vMslvfJ-wWQqzAgQPpaVy271cc4f0Vo6OUUyzvH7a5VhCgG/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi568TvPU2aKlSqAS1CmFZCaCAU6R9UYKeTjy8apbIeSCkUsL_VokN7Fp02meHlYvBYEK8kM1sPrh3cg8Iz68qnIB3g_xd49vMslvfJ-wWQqzAgQPpaVy271cc4f0Vo6OUUyzvH7a5VhCgG/s1600/10.png" /></a></div>
<br />andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com12tag:blogger.com,1999:blog-5593586859352074500.post-18654132715244637512014-02-17T09:13:00.001-08:002014-02-18T08:49:51.480-08:00Wywoływanie aktywności (ekranów) , elementy graficzne Button, EditText, TextView - podsumowanie podstaw<br />
<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/komponenty_i_wywolywanie_aktywnosci.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
<br />
-W tej lekcji zajmiemy się wywoływaniem nowych aktywności (czyli przechodzeniem pomiędzy ekranami), a także elementami Button, EditText oraz TextView. Stworzymy przy okazji prosty program do obliczania pól figur geometrycznych.<br />
Zaczynamy od stworzenia projektu. Z menu wybieramy „Android Application Project”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtotceO31tFoSsI3dZLsldnZ6X33WETqB7_GQH2Phz6sayPXbPLKiF8gFVfoKWZ6xqEXxCbWsa8f_Yd2DsF9oSPbn7vWfbODLa73QV2iVX9fXQLSXsReX3W8q1Aaw15ODE7wTflhrC3tnM/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtotceO31tFoSsI3dZLsldnZ6X33WETqB7_GQH2Phz6sayPXbPLKiF8gFVfoKWZ6xqEXxCbWsa8f_Yd2DsF9oSPbn7vWfbODLa73QV2iVX9fXQLSXsReX3W8q1Aaw15ODE7wTflhrC3tnM/s1600/1.png" height="302" width="320" /></a></div>
<br />
<br />
Dalej nadajemy naszej aplikacji nazwę:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT6ZPku_4bj8hPau04bcE198WREzGdvzGNwZmckvP-K7aRFAJ0ZfL6_a7hLk9u-E8HQfLu1_VumZ4YQLZytOo2_O36crQCNv7ioWkPcHDSFbFJjQejwY_AwxYh6drIS8c7tfqgHCpdIOGl/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhT6ZPku_4bj8hPau04bcE198WREzGdvzGNwZmckvP-K7aRFAJ0ZfL6_a7hLk9u-E8HQfLu1_VumZ4YQLZytOo2_O36crQCNv7ioWkPcHDSFbFJjQejwY_AwxYh6drIS8c7tfqgHCpdIOGl/s1600/2.png" /></a></div>
<br />
Przy tworzeniu projektu, tworzymy również od razu pierwszą aktywność. Będzie to po prostu pierwszy ekran jaki się pojawi po uruchomieniu aplikacji. Decydujemy o jego pokroju - zostawiamy domyślne „Blank Activity”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWWN_Zkk78utWGdqI28pJqcQujk8YoMOEp22L4q7cCL8vY9Vnl7kgeRKeH5N8u21fIEkIkMiVRdIcqkX1uvj8O7jcc-hyDl314Hf8DtapNu6vmQtLNRc3M0jRekz_5eWsxuG0CZ6CC7cyj/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhWWN_Zkk78utWGdqI28pJqcQujk8YoMOEp22L4q7cCL8vY9Vnl7kgeRKeH5N8u21fIEkIkMiVRdIcqkX1uvj8O7jcc-hyDl314Hf8DtapNu6vmQtLNRc3M0jRekz_5eWsxuG0CZ6CC7cyj/s1600/3.png" /></a></div>
<br />
Podajemy nazwę klasy będącej pierwszą aktywnością (ekranem) w polu „Activity Name”. Do naszej klasy będzie też potrzebny plik XML w którym to określimy jak mają być rozmieszczone elementy w danej aktywności (guziki, okienka etc). Nazwę tego pliku określamy w polu „Layout Name”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsGyUDZxyK2E_dsbXyaNPXG9tuUztx80ehM6D-MlvGvfL2x5XHLqkI3WmbRj29g9yuDuxU0_YGPsIH3KBgdEzuokvxFhijnsviGqtT92CE7t2TEbRFWBVVcSldjdyc_zGg9-Kq0pyxGPC3/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhsGyUDZxyK2E_dsbXyaNPXG9tuUztx80ehM6D-MlvGvfL2x5XHLqkI3WmbRj29g9yuDuxU0_YGPsIH3KBgdEzuokvxFhijnsviGqtT92CE7t2TEbRFWBVVcSldjdyc_zGg9-Kq0pyxGPC3/s1600/4.png" /></a></div>
<br />
Gdy projekt zostanie utworzony, zostanie otwarta klasa głównej aktywności. Będzie zawierać dwie metody. Pierwsza „onCreate” to metoda która jest uruchamiana w momencie startu aktywności. Druga „onCreateOptionsMenu” to metoda uruchamiana kiedy ktoś wybierze przycisk menu. Tę drugą usuwamy, nie jest nam na razie potrzebna.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl3z1gC3FWuXxHN1GNMD9j0m1i-UtncIfsX1gailL-v06zurWTW4LLi9wkw4Nj2aqOfQ1Pitz1cZOB3mcqUU416H6uXXkLak6Wnc2AAdqYdP9lFyNMyQ-rFqy_lliQYYyyXPrMDSiGsvfz/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjl3z1gC3FWuXxHN1GNMD9j0m1i-UtncIfsX1gailL-v06zurWTW4LLi9wkw4Nj2aqOfQ1Pitz1cZOB3mcqUU416H6uXXkLak6Wnc2AAdqYdP9lFyNMyQ-rFqy_lliQYYyyXPrMDSiGsvfz/s1600/5.png" /></a></div>
<br />
Nasza klasa powinna wyglądać teraz mniej więcej tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ykQDF2FDy88ExHvKmm_YKNInCRwFRZMAh9NUniUpXEC9oyy_bLkd5OslDAtTN6GhUTi2bD9Qn_-Pj6hmQdjdQWLCEcpkqLjfv8_Xjz2XnjENviqXyg7ayK5rjTiZH0F_eZZUstjiD_kB/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_ykQDF2FDy88ExHvKmm_YKNInCRwFRZMAh9NUniUpXEC9oyy_bLkd5OslDAtTN6GhUTi2bD9Qn_-Pj6hmQdjdQWLCEcpkqLjfv8_Xjz2XnjENviqXyg7ayK5rjTiZH0F_eZZUstjiD_kB/s1600/6.png" /></a></div>
<br />
<br />
Zadbamy teraz o wygląd pierwszego ekranu. Przechodzimy do pliku layoutu. Powinniśmy zobaczyć taki ekran. Po prawej mamy naszą aktywność, po lewej paletę komponentów. Gdyby zamiast aktywności widoczny był kod XML, wybierz z zakładek na dole „Graphical Layout”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDw8BFF93dWI86EXD16f30qxcjw5mI6hQxyGuTFQMEM8vWuAdv-yaMYe4CdjsC4ALiHHZ49nrbXDbvR3MPV4IcmSHpgga9XriH50-4KFLE2PhSbkYTHuCTzuNZEfjrixD6ySybDzIfM3da/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgDw8BFF93dWI86EXD16f30qxcjw5mI6hQxyGuTFQMEM8vWuAdv-yaMYe4CdjsC4ALiHHZ49nrbXDbvR3MPV4IcmSHpgga9XriH50-4KFLE2PhSbkYTHuCTzuNZEfjrixD6ySybDzIfM3da/s1600/7.png" /></a></div>
<br />
<br />
Zawczasu przygotowałem sobie kilka obrazków które będą mi potrzebne w aplikacji (narysowałem w Paincie :p ). Będziemy ich używać w naszej aplikacji. Zrobimy sobie listę figur pojawiającą się na „Dzień dobry” wraz z obrazkami. Po kliknięciu na nazwę figury program przejdzie do formularza służącego obliczaniu pola danej figury.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglaj3pX14J6HH9NthBPYKNjcNPHloYHZa6Xc2YCplZ_uspMyL167V45P70Bg7z9rkD9Fr5A2JzyusMNwbemRGkN0iFHhJIfXHTOhr7VB6j_gYYXJ0c0N_9YGqXC597QQmITDIyjPAHv9y3/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglaj3pX14J6HH9NthBPYKNjcNPHloYHZa6Xc2YCplZ_uspMyL167V45P70Bg7z9rkD9Fr5A2JzyusMNwbemRGkN0iFHhJIfXHTOhr7VB6j_gYYXJ0c0N_9YGqXC597QQmITDIyjPAHv9y3/s1600/8.png" /></a></div>
<br />
<br />
Muszę je teraz umieścić w projecie. Zaznaczam je w katalogu, robię CTRL+C, w projekcie wchodzę w podkatalog res, następnie drawable-hdpi (lub inny drawable-....) i robię CRTL+V:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5TZmUJLe89zc4DJcmfa6ZPWzUeUA_YSXe0fcnKgyZcf1kWa_9luQpc3NDHMFUsttucRJSRK3XbSVXBtvXsJMgWFLB1ubY88uhgzUSRVIAdyFQHcmdH1h-NQxh9MQyvxyS_DMYKg3lPNBV/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5TZmUJLe89zc4DJcmfa6ZPWzUeUA_YSXe0fcnKgyZcf1kWa_9luQpc3NDHMFUsttucRJSRK3XbSVXBtvXsJMgWFLB1ubY88uhgzUSRVIAdyFQHcmdH1h-NQxh9MQyvxyS_DMYKg3lPNBV/s1600/9.png" /></a></div>
<br />
<br />
Przechodzę teraz do edycji pliku XML określającego layout naszej głównej aktywności. Będziemy edytować ręcznie, więc tym razem nie wybieramy „Graphical Layout” a „activity_main.xml” (lub jak tam nazwałeś swój plik :) ). <br />
Będzie tam już trochę tekstu, ale nie jest nam potrzebny więc go w całości kasujemy. Teraz doprowadzamy zawartość pliku do takiej mniej więcej formy:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlqQ515pA9d4aV8OX5Trr0m5KezAH8M6Ahbvr7ie8T6rJlEIktQIfib3hpwHfLXYoqZXDjtor6RqMqkYn_jLugj0rasvYQsVKeR54hyBy-8AwDf0jON76lIO21-FTBD7rqfHDetKKazepO/s1600/10.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhlqQ515pA9d4aV8OX5Trr0m5KezAH8M6Ahbvr7ie8T6rJlEIktQIfib3hpwHfLXYoqZXDjtor6RqMqkYn_jLugj0rasvYQsVKeR54hyBy-8AwDf0jON76lIO21-FTBD7rqfHDetKKazepO/s1600/10.png" /></a></div>
<br />
<br />
„Table Layout” to tabelaryczny układ elementów na ekranie aktywności , coś jak w HTML. Kolejne „TableRow” to po prostu kolejne wiersze w tabeli. Elementy zawarte w elemencie „TableRow” będą ze sobą sąsiadować w ramach danego poziomu w tabeli. Mamy tutaj dwa elementy: ImageView, oraz TextView. Pierwszy służy do wyświetlania obrazków, drugi do wyświetlania (ale nie wprowadzania) tekstu. W obu mamy parametr android:id. Nadajemy nim unikalną nazwę elementowi w ramach danej aktywności. Wartość tego parametru musi się zaczynać od „@+id/” po których następuje nasz identyfikator. Ten identyfikator jest niezbędny do rozróżniania elementów i jednoznacznego wskazywania jednego z nich. W obu też występują parametry layout_width i layout_height. Określają one szerokość i wysokość elementu. Jednostka DP to piksele niezależne od rozdzielczości ekranu. W elemencie ImageView jest jeszcze parametr android:src. Poprzez niego podajemy jaki obrazek ma zostać wyświetlony. Jeśli chodzi o obrazek zawarty w projecie (a nie znajdujący się np. na zdalnym serwerze czy karcie pamięci) to zaczynamy wartość od „@drawable/” po którym następuje nazwa obrazka wrzuconego w jednym z poprzednich kroków do katalogu drawable-hdpi. Nie podajemy rozszerzenia (tutaj .png) w nazwie pliku. <br />
W elemencie TextView mamy dodatkowo element android:text przy użyciu którego ustawiamy tekst jaki ma być wyświetlany na elemencie. Związany z nim jest jeszcze element android:textSize, który określa wielkość czcionki. Użyłem tutaj też parametrów android:layout_marginLeft, oraz android:layout_marginTop. Określają one margines lewy , oraz górny.<br />
Wracamy do widoku projektowania graficznego. Nasza aplikacja powinna w tej chwili wyglądać mniej więcej tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm2QtHauu-E5hlhkD-DQQV_DZ2qYXnYBApBZpQgjbZBz0ZwKkNLVDmyu53ifZXNomZvUPreHzLcfoeVG2VZcLCkTH34wjQIE6TIsgA9Yr5YysdD1udCQrjhTKSj8inQEwCuON-5mI3_GO5/s1600/11.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgm2QtHauu-E5hlhkD-DQQV_DZ2qYXnYBApBZpQgjbZBz0ZwKkNLVDmyu53ifZXNomZvUPreHzLcfoeVG2VZcLCkTH34wjQIE6TIsgA9Yr5YysdD1udCQrjhTKSj8inQEwCuON-5mI3_GO5/s1600/11.png" /></a></div>
<br />
Dodałem analogicznie kolejne wiersze tabeli z kolejnymi elementami. Zmieniłem też wielkość czcionki na 20dp ponieważ niektóre opisy nie mieściły się w jednej linii:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxM4JCnv0P_1wMZmC44g6WDG4ThTWaknYM7hS93sjEuRZauj6ze7oRPFwpGlmLx83ZNLly0UQUZVwAnO-G3gn9lglrD_q6WzIf0FRrTZxGIDKfIpkSrQPES_D6niQdyW1QdWA4rZdYdyO/s1600/12.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiRxM4JCnv0P_1wMZmC44g6WDG4ThTWaknYM7hS93sjEuRZauj6ze7oRPFwpGlmLx83ZNLly0UQUZVwAnO-G3gn9lglrD_q6WzIf0FRrTZxGIDKfIpkSrQPES_D6niQdyW1QdWA4rZdYdyO/s1600/12.png" /></a></div>
<br />
Po tym zabiegu program wygląda tak:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfooSMm4cA70B37l4e53TEkG7rOjW-RRbMqsLatw2pP7avP_Qy9KNVZdQGXQoOnDHwij-ddOdzmk8F3V161xLHp8HOuLx7e3cpBHnCScGRcaWWeqkYooLFGdPMJxQB94QQN0UK78uXjskC/s1600/13.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhfooSMm4cA70B37l4e53TEkG7rOjW-RRbMqsLatw2pP7avP_Qy9KNVZdQGXQoOnDHwij-ddOdzmk8F3V161xLHp8HOuLx7e3cpBHnCScGRcaWWeqkYooLFGdPMJxQB94QQN0UK78uXjskC/s1600/13.png" /></a></div>
<br />
Przyszła pora na dodanie nowej aktywności – tj. ekranu na który aplikacja powinna przejść po kliknięciu na napis „pole kwadratu”. Klikam prawym przyciskiem myszy na pakiecie w którym znajduje się moja pierwsza aktywność i wybieram NEW--> OTHER:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtuZfUw1Njyj_wG4_hNDeeg3HlgpWW0aNgY08CJ7ygUskcSbmkJ1PTJVkE38kuZiLTd9MkUQTvDSJkk-VUveTs0Dv2mPmrVxKxl-WeWbWyPxH1r6z8lKMDszXXjvvxt4H6CgVbYff48YYd/s1600/14.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtuZfUw1Njyj_wG4_hNDeeg3HlgpWW0aNgY08CJ7ygUskcSbmkJ1PTJVkE38kuZiLTd9MkUQTvDSJkk-VUveTs0Dv2mPmrVxKxl-WeWbWyPxH1r6z8lKMDszXXjvvxt4H6CgVbYff48YYd/s1600/14.png" /></a></div>
<br />
<br />
Dalej „Android Activity”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtFIr3tTGN8iBoRcxsWmfUSFcXFpFAv8jXbzEOSwUpWsRG-zYeciPqgRUQQvieqBFm9yylmJl4AouG_dC3QvF1RAqidKLuuPQMSq9tWq4YCKTxofpiuVM_9Z-te9dpydU0XpRnfT8KgY_d/s1600/15.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgtFIr3tTGN8iBoRcxsWmfUSFcXFpFAv8jXbzEOSwUpWsRG-zYeciPqgRUQQvieqBFm9yylmJl4AouG_dC3QvF1RAqidKLuuPQMSq9tWq4YCKTxofpiuVM_9Z-te9dpydU0XpRnfT8KgY_d/s1600/15.png" /></a></div>
<br />
<br />
Podobnie jak i wcześniej nadaje klasie aktywności i plikowi layoutu nazwy:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaFP5n8ynws7WMJPNsUqwBgGUzk2P_BPVfelnjW8uKY53uH95xHPG_mFQt_YI_cgMhRKa53JxokBoS1nsWEbjMbDHX8mTPM7llDxjw2JvX3RD1X8Ef701PGI7K_RJ5H2p18rjQLdJmmGfz/s1600/16.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiaFP5n8ynws7WMJPNsUqwBgGUzk2P_BPVfelnjW8uKY53uH95xHPG_mFQt_YI_cgMhRKa53JxokBoS1nsWEbjMbDHX8mTPM7llDxjw2JvX3RD1X8Ef701PGI7K_RJ5H2p18rjQLdJmmGfz/s1600/16.png" /></a></div>
<br />
Klasa nowej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-pVwfFRHlmvrOr8SunfFzPce6Ax1xhHPRqP1p5aVU1GipuuCFqAOG2ZS1iQys7ZdHf3HLMv_hKNuxicujhr4Ypp0JCiq8dscgYKNbav4LF2NrWrj_n-gd3ICX_zFQVEUosnklZRQZPVGN/s1600/17.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-pVwfFRHlmvrOr8SunfFzPce6Ax1xhHPRqP1p5aVU1GipuuCFqAOG2ZS1iQys7ZdHf3HLMv_hKNuxicujhr4Ypp0JCiq8dscgYKNbav4LF2NrWrj_n-gd3ICX_zFQVEUosnklZRQZPVGN/s1600/17.png" /></a></div>
<br />
<br />
Zauważ że w metodzie „onCreate” jest wywołanie metody setContentView, która służy do wiązania aktywności z opisem wyglądu zawartym w pliku XML. Każda aktywność domyślnie będzie miała swój plik xml i po stworzeniu będzie wywołanie jej własnego pliku, ale możesz do zmienić wskazując w parametrze inny layout. Tutaj mamy R.layout.activity_pole_kwadratu, ponieważ taki właśnie plik został utworzony podczas generowania nowej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFKDJu2lJW-H_gzpAG3_wlJkm0RHNlSItIOaSvs08wzNfVX5XAmBeTBjihyphenhyphen5yx2CaAjHZCJ0NECkx44-hneAuJRNVk7Zc9ew4qa_Orbi0B9e1k12uAYvDkk4vgy5iFi-0XuGstzoMvXGa2/s1600/18.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFKDJu2lJW-H_gzpAG3_wlJkm0RHNlSItIOaSvs08wzNfVX5XAmBeTBjihyphenhyphen5yx2CaAjHZCJ0NECkx44-hneAuJRNVk7Zc9ew4qa_Orbi0B9e1k12uAYvDkk4vgy5iFi-0XuGstzoMvXGa2/s1600/18.png" height="242" width="320" /></a></div>
<br />
<br />
Domyślnie zawartość tego pliku wygląda tak:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBY52AnwI81vzlDobGubEdzaoYCGaywsODLGeszm08RC-QXZ4ViUtuKMbscWkAtrvugTPEdNQ9nL8tJOgDjHC5HnFuKZWz6VMZ8dFIClKoyv7YpHL_O8bxWs4gWvkQ6p96sanrP6ubn9WI/s1600/19.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBY52AnwI81vzlDobGubEdzaoYCGaywsODLGeszm08RC-QXZ4ViUtuKMbscWkAtrvugTPEdNQ9nL8tJOgDjHC5HnFuKZWz6VMZ8dFIClKoyv7YpHL_O8bxWs4gWvkQ6p96sanrP6ubn9WI/s1600/19.png" /></a></div>
<br />
Na ten moment ta aktywność będzie po prostu wyświetlała napis „Hello World!”. My w tej aktywności dodamy parę elementów, będzie to formularz do obliczania pola kwadratu. Przechodzimy do graficznej formy edycji layoutu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjerwbAAtNlxl8_Y4eGMUdmIWuA2rT-aTGssq4XhsTQ6qentGM0ZquqlDWLAbdKnU_ucxZxFxSiJ9KI5iofdI3ku27dmq4X32P8XHZ5HX_MoAso6AOmP-FZnLGtatN4bkWrJl6QrUSh8xZa/s1600/20.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjerwbAAtNlxl8_Y4eGMUdmIWuA2rT-aTGssq4XhsTQ6qentGM0ZquqlDWLAbdKnU_ucxZxFxSiJ9KI5iofdI3ku27dmq4X32P8XHZ5HX_MoAso6AOmP-FZnLGtatN4bkWrJl6QrUSh8xZa/s1600/20.png" /></a></div>
Napis który już tam się znajduje nieco zmodyfikujemy. Klikamy nań prawym przyciskiem myszy i wybieramy „Edit Text”:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiReOJGSl0ZeLVZAzsr8dFT7NUs9wv6xoJg4v3Kpam6_b_WDQhugMSyUxw52c0GA8LBt84var2_QScjOsfIaX-nr2JtLYsfKZfD-HhNH9kSC2uUuKQj8BqnOMtq5rAfK4vSjLEUv8cZfiR/s1600/21.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiReOJGSl0ZeLVZAzsr8dFT7NUs9wv6xoJg4v3Kpam6_b_WDQhugMSyUxw52c0GA8LBt84var2_QScjOsfIaX-nr2JtLYsfKZfD-HhNH9kSC2uUuKQj8BqnOMtq5rAfK4vSjLEUv8cZfiR/s1600/21.png" /></a></div>
<br />
<br />
Wcześniej tekst wpisywaliśmy niejako „bezpośrednio”, teraz przyjmiemy nieco inną konwencję. W projekcie obecny jest plik strings.xml:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLodHjbPnfkcN1yv2ld-pEnmmdoO6EAWMiiZCEYklWIUOsr60Pk_tlpmcYgefqsZP0eUEsCE1eGQvtLf8C0yPBP4VV2F1PwlV5_ZI3EKPhsWlwgTFjqUYo1hz7_59RNwpljZwjB3xq3sbS/s1600/22.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgLodHjbPnfkcN1yv2ld-pEnmmdoO6EAWMiiZCEYklWIUOsr60Pk_tlpmcYgefqsZP0eUEsCE1eGQvtLf8C0yPBP4VV2F1PwlV5_ZI3EKPhsWlwgTFjqUYo1hz7_59RNwpljZwjB3xq3sbS/s1600/22.png" /></a></div>
Znajdują się w nim identyfikatory, oraz przypisane do nich teksty. Chcąc wyświetlić jakiś tekst na elemencie, będziemy się odwoływać poprzez identyfikator do tekstu zawartego w tym pliku.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPBXcf8fGUkBCia2mzlkXFHmqj3HimXnSHXXSMnA0rZ9Q_kt3EdMsOupNqhRp98_zdWFo_7A_5SHPv1UrBLaPKxMo6LYY-jXnkUxci265U3L7xZNkHnbAOZhCP4oADKjl2ZC8qEmCwfW-z/s1600/23.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPBXcf8fGUkBCia2mzlkXFHmqj3HimXnSHXXSMnA0rZ9Q_kt3EdMsOupNqhRp98_zdWFo_7A_5SHPv1UrBLaPKxMo6LYY-jXnkUxci265U3L7xZNkHnbAOZhCP4oADKjl2ZC8qEmCwfW-z/s1600/23.png" /></a></div>
Po co nam to wszystko? Dzięki temu mamy wszystkie informacje tektstowe w jednym miejscu, dużo łatwiej jest je dzięki temu zmieniać. Ponadto znacznie łatwiej będzie później robić różne wersje językowe naszego programu, bo wystarczy potem tylko podmienić ten jeden plik. Ustawimy teraz napis na TextView, jednocześnie dodając go do pliku strings.xml:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8CRScVl2EEWfv6qDZAyedn8O9ie3pzrdl1KFnIt8ty85xOAlNUaaEGrL35gEzZSwnLX-liDTOPsMYI9T5NWkFJE8zpdxsnakyszNp6drGvfpNytK6GAUGuD2tto3pjrmG1xKdT5dnqKl5/s1600/24.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh8CRScVl2EEWfv6qDZAyedn8O9ie3pzrdl1KFnIt8ty85xOAlNUaaEGrL35gEzZSwnLX-liDTOPsMYI9T5NWkFJE8zpdxsnakyszNp6drGvfpNytK6GAUGuD2tto3pjrmG1xKdT5dnqKl5/s1600/24.png" height="156" width="320" /></a></div>
<br />
Wybieramy edycję i przechodzimy do takiego ekranu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB-iQpbrhV1WT5ziZ9jq6zoCoQdrZhSICZVL_LRqTmOjvOhG_KAPJcndyQjKfkkJovB5TRUnNZOFuuqplWYCQX-aSfj1c4m_BWOD8wAKwOBIzy9HGxB_uMXnvyyrEc6XR-NFTGXk5j6ihc/s1600/25.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiB-iQpbrhV1WT5ziZ9jq6zoCoQdrZhSICZVL_LRqTmOjvOhG_KAPJcndyQjKfkkJovB5TRUnNZOFuuqplWYCQX-aSfj1c4m_BWOD8wAKwOBIzy9HGxB_uMXnvyyrEc6XR-NFTGXk5j6ihc/s1600/25.png" /></a></div>
<br />
Klikamy przycisk „New String”. Powinniśmy teraz zobaczyć taki ekran:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2d5FQq3zr0SqqyfVNTIT9PnQJAgrgvX1S72ZIfzvxhNmQnhl9p8gAwUNoaaIeZ_NfxNx6jRO_P0pUF5AS0gfxh6MznDLnovM4Kx92ow1EljpPCQjGUN8fclNiVE1o_JAYKa7vnq3BtdBH/s1600/26.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2d5FQq3zr0SqqyfVNTIT9PnQJAgrgvX1S72ZIfzvxhNmQnhl9p8gAwUNoaaIeZ_NfxNx6jRO_P0pUF5AS0gfxh6MznDLnovM4Kx92ow1EljpPCQjGUN8fclNiVE1o_JAYKa7vnq3BtdBH/s1600/26.png" /></a></div>
<br />
W polu „String” wpisujemy tekst który ma zostać dodany do pliku strings.xml. Oraz wyświetlony na komponencie. W polu „New R.string” podajemy identyfikator tekstu, pod który zostanie wpisany tekst do pliku.<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4RkD9zqJnMcVwR-HsqFCnZozUIy6-oJUuecIwSp-hIpdg3xdeIMphV9T4w0qlBZcLadeJevaRKc0Oxi9OFB5kULDJoS2H6P42djOv1rjIwPgilP-XglpynSG8rBpyFlc9I3u5mKtjoWdZ/s1600/27.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4RkD9zqJnMcVwR-HsqFCnZozUIy6-oJUuecIwSp-hIpdg3xdeIMphV9T4w0qlBZcLadeJevaRKc0Oxi9OFB5kULDJoS2H6P42djOv1rjIwPgilP-XglpynSG8rBpyFlc9I3u5mKtjoWdZ/s1600/27.png" /></a></div>
<br />
<br />
Zatwierdzamy. Zobaczymy taki ekran:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihAIDH_9DR8rheATA3F5TCjG8Ncv2eEW0mGAlMWPICMM3IERaWIVjMc8lqhmqs_S8u1IH7PbLnbVPLnzSv6eA9jgKq9bxT9dOpUXoL-GhCy08vUqi45DOLQPCIET0j2uFfG1Uu8H00jjBV/s1600/28.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihAIDH_9DR8rheATA3F5TCjG8Ncv2eEW0mGAlMWPICMM3IERaWIVjMc8lqhmqs_S8u1IH7PbLnbVPLnzSv6eA9jgKq9bxT9dOpUXoL-GhCy08vUqi45DOLQPCIET0j2uFfG1Uu8H00jjBV/s1600/28.png" /></a></div>
<br />
Pojawi się nowy wpis w pliku strings.xml, a napis na komponencie się zmieni:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_41-jAaOXR78deJmP66tPur8uhMPP9rH2xRqTAcrQtw_coNX9PpCR-StX-S92XyK_paTI5y9xbm_mLT3U7ZAS_qBWI29gccGEZxuT_6ROLdFeB-LUQaT4KFrmwN1hntNxlNRTZTmef3Uf/s1600/29.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_41-jAaOXR78deJmP66tPur8uhMPP9rH2xRqTAcrQtw_coNX9PpCR-StX-S92XyK_paTI5y9xbm_mLT3U7ZAS_qBWI29gccGEZxuT_6ROLdFeB-LUQaT4KFrmwN1hntNxlNRTZTmef3Uf/s1600/29.png" /></a></div>
Dobrze. Mamy teraz ekran początkowy z listą elementów do wyboru, oraz zalążek nowego ekranu który docelowo będzie służył obliczaniu pola kwadratu. Trzeba teraz te elementy połączyć. Po kliknięciu napisu „pole kwadratu” na naszej aplikacji powinna pojawić się aktywność do obliczania pola kwadratu. Przyszedł czas na dodanie reakcji na kliknięcie napisu.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy_Xlo6_P7nFTMpavyLfGPaMhrwy5xO-d8zgDtivuO9KyDGMDqWUECdlK3zWKs7ODqeew796CmVRtASGCVnjFt0D__A53TBFOmU_Imfg2LQSTBarwJ6kfL7P1bCsRPTu2JqNAl37SUiH-B/s1600/30.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiy_Xlo6_P7nFTMpavyLfGPaMhrwy5xO-d8zgDtivuO9KyDGMDqWUECdlK3zWKs7ODqeew796CmVRtASGCVnjFt0D__A53TBFOmU_Imfg2LQSTBarwJ6kfL7P1bCsRPTu2JqNAl37SUiH-B/s1600/30.png" /></a></div>
<br />
Zmieniłem nieco zawartość klasy mojej głównej aktywności. Dodałem zmienną t1 która będzie reprezentowała komponent TextView z napisem „Pole kwadratu”. W linii 19 do tej zmiennej przypinam realny komponent. Robię to przy użyciu wbudowanej metody findViewById. Ma ją każda klasa dziedzicząca po Activity. Ta metoda służy do zwracania referencji komponentu z aktywności. Do zmiennej t1 zostaje przypisany element o identyfikatorze textView1 (czyli nasz napis „pole kwadratu” z ekranu głównego:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJUolPIvaKk74iUQqW7P9pW3gAcun1NVdyE-iHBOug_X9NyU6wcRuWkMYkPj-dhYZrsRlAdSVktFut3aiBwYKYHpqKemS2-IKWf41z3ey8Vje1WPn6riLHFefZYBZBLADG0vu-WnwTHXdv/s1600/31.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiJUolPIvaKk74iUQqW7P9pW3gAcun1NVdyE-iHBOug_X9NyU6wcRuWkMYkPj-dhYZrsRlAdSVktFut3aiBwYKYHpqKemS2-IKWf41z3ey8Vje1WPn6riLHFefZYBZBLADG0vu-WnwTHXdv/s1600/31.png" /></a></div>
<br />
<br />
Obsługę zdarzenia kliknięcia realizujemy poprzez podstawienie listenera dla obiektu t1. Listener to taki proces nasłuchu który czeka na jakieś zdarzenie. Tworzymy nasz listener jako obiekt klasy OnClickListener i jednocześnie defuniujemy dla niego reakcję na kliknięcie (metoda onClick). Tymczasowo reakcja na kliknięcie komponentu ma polegać na zmianie napisu „Pole kwadratu na napis „AŁA!!!!”. Stworzony listener ustawiamy dla obiektu t1 poprzez metodę tego obiektu setOnClickListener.<br />
<br />
Uruchamiam projekt i sprawdzam działanie. Reakcja na kliknięcie jest taka jaka być powinna. Nasz program jest nadwrażliwą beksą :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu1SnVY_JZzYl4mzu6qmgy0PW8BRt6gasFJwO1nZMe7YVbMv9TEvDxQ7nY8fG9aVvo-u6HaYNPTJebKYS5qvxXg9SVtRQTvayxmuewB3046qkL0CgRf57xrxuX4ao56Cd6rnBLJ93lakwL/s1600/32.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgu1SnVY_JZzYl4mzu6qmgy0PW8BRt6gasFJwO1nZMe7YVbMv9TEvDxQ7nY8fG9aVvo-u6HaYNPTJebKYS5qvxXg9SVtRQTvayxmuewB3046qkL0CgRf57xrxuX4ao56Cd6rnBLJ93lakwL/s1600/32.png" /></a></div>
<br />
Skoro już działa obsługa kliknięcia, to teraz jako reakcję wepniemy uruchomienie innej aktywności w miejsce zmiany tekstu.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv_AQ-mV9IMzhzLRSENNNb9MozHKi8Sj_aThcSATCjGAbqHhH4mbRoAufCLSBXMoqlzUe1z3KF5NpI82rJnhq5C1vHRyRR3uEDJAjk7WSDYBsdcyVJwUNao_hndSTprNc2wkmqEKxEqAxc/s1600/33.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhv_AQ-mV9IMzhzLRSENNNb9MozHKi8Sj_aThcSATCjGAbqHhH4mbRoAufCLSBXMoqlzUe1z3KF5NpI82rJnhq5C1vHRyRR3uEDJAjk7WSDYBsdcyVJwUNao_hndSTprNc2wkmqEKxEqAxc/s1600/33.png" /></a></div>
Wykomentowałem zmianę tekstu. Dodałem obiekt klasy Context do którego w linii 28 przypisuję kontekst aplikacji. W linii 29 tworzę nową intencję. Intencje to komunikaty łączące ze sobą różne komponenty.W tym przypadku nasza intencja będzie służyła wywołaniu innej aktywności. W tej samej linii podaję też informację, aktywność jakiej klasy ma zostać wywołana. W linii 30 odpalam tę aktywność.<br />
Sprawdzam działanie całości. Tym razem po kliknięciu napisu „Pole kwadratu” nie zmienia się tekst na komponencie, a zostaje wyświetlony ten ekran:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2tgzUyFa_AULw5GqsiL3xmLwQ4bYRbHk_RPkTwSu5h6dYbq_seFnc8GkYwZrl03Br0_yJaPZRNiHBvW0tIfD7Jc-Fzu4s0YqReCu5L04HOAAHRmROOO7cN4UVBMwzATeo_LIJuOrIptKT/s1600/34.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2tgzUyFa_AULw5GqsiL3xmLwQ4bYRbHk_RPkTwSu5h6dYbq_seFnc8GkYwZrl03Br0_yJaPZRNiHBvW0tIfD7Jc-Fzu4s0YqReCu5L04HOAAHRmROOO7cN4UVBMwzATeo_LIJuOrIptKT/s1600/34.png" /></a></div>
<br />
<br />
Skoro wywołania działają, trzeba przerobić tę aktywność tak, aby faktycznie służyła obliczaniu pola kwadratu. Przechodzę więc do pliku layoutu tej aktywności:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPnZOBZ7UdceD0-dChyphenhyphenk2nZsE43NAOzzPlt1BBBtVig77Mkhm1AEpCpp_PAuX12XqiJFDs6whFWJZItwSlqBBzyRr1KgDpkKflGZXYF8edLlUQPX7gk5sBdZmrvCLPmUm-t_9kRwjjf2em/s1600/35.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiPnZOBZ7UdceD0-dChyphenhyphenk2nZsE43NAOzzPlt1BBBtVig77Mkhm1AEpCpp_PAuX12XqiJFDs6whFWJZItwSlqBBzyRr1KgDpkKflGZXYF8edLlUQPX7gk5sBdZmrvCLPmUm-t_9kRwjjf2em/s1600/35.png" /></a></div>
<br />
by następnie w trybie projektowania graficznego poprzyklejać do niej niezbędne komponenty.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF2vLvhPb4EkHdOgxNnP1_NNpf8QANJCTp-YF8kGmE_Z19udOnF_YWPlAKDY7eTMMgTmjv3CYxiE0seStXq0mNtgy-mHH9maoT-gqB8qsLmIFd9kas7_F8go2OrPF3IMwRtr4o_-7ovSJM/s1600/36.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiF2vLvhPb4EkHdOgxNnP1_NNpf8QANJCTp-YF8kGmE_Z19udOnF_YWPlAKDY7eTMMgTmjv3CYxiE0seStXq0mNtgy-mHH9maoT-gqB8qsLmIFd9kas7_F8go2OrPF3IMwRtr4o_-7ovSJM/s1600/36.png" /></a></div>
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWfhrErELjIM74yoLIdArKXmRbrYV4oH2vY7dsLRCXJC4ci7P0SUOVrzk1eGoe1378W8_8b14dlkyaLkUh0B38WeVUwSQHccfC8U_jS6axPuUOVmxIWykBGTfFpRzPGXiZqrQVihebZ1VD/s1600/37.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWfhrErELjIM74yoLIdArKXmRbrYV4oH2vY7dsLRCXJC4ci7P0SUOVrzk1eGoe1378W8_8b14dlkyaLkUh0B38WeVUwSQHccfC8U_jS6axPuUOVmxIWykBGTfFpRzPGXiZqrQVihebZ1VD/s1600/37.png" /></a></div>
<br />
Pojawiły się tu nowe rzeczy. Komponent klasy Button, komponent klasy LargeText, oraz komponent klasy EditText. Button to po prostu przycisk. LargeText to element wyświetlający tekst, ale o powiększonej czcionce. EditText to komponent w którym możemy wyświetlić jakiś tekst, ale użytkownik programu będzie mógł gozmienić.<br />
<br />
Po rozmieszczeniu elementów przechodzimy do edycji klasy aktywności :<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvJ-YopkeJzLC75_e4Iws0lqlx5y9IRuIYZHQ1NGViP9-1TFJiUYL94vbtq6SCnLj6KLSmX67lkFuMmoNtEIZ-v1HH_YJr4cu0QSBml3Q5qA2iAoi1Wd6Jgp_uv6CZ08XnfvUDj8RM_1FA/s1600/38.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhvJ-YopkeJzLC75_e4Iws0lqlx5y9IRuIYZHQ1NGViP9-1TFJiUYL94vbtq6SCnLj6KLSmX67lkFuMmoNtEIZ-v1HH_YJr4cu0QSBml3Q5qA2iAoi1Wd6Jgp_uv6CZ08XnfvUDj8RM_1FA/s1600/38.png" /></a></div>
<br />
W analogiczny sposób jak dla kliknięcia napisu „Pole kwadratu” oprogramowałem tutaj kliknięcie przycisku z napisem „Oblicz”. Zdefiniowałem w liniach 14-16 obiekty które będą reprezentować używane przeze mnie komponenty. W liniach 23-25 przypisuję do tych obiektów referencje do komponentów. W liniach 27-36 mam oprogramowaną reakcję na kliknięcie przycisku. Działa to analogicznie jak wcześniejszy przykład. Z nowości jest tutaj tylko rodzaj reakcji. W liniach 31-33 jest opisane co dokładnie ma się zdarzyć. Generalnie ustawiam tekst na komponencie wynik (to ten komponent klasy LargeText który ma domyślnie ustawione trzy myślniki. Ustawiany tekst to kwadrat wartości podanej poprzez obiekt bok – czyli obiekt klasy EditText, do którego użytkownik wprowadzi długość boku kwadratu. Po drodze pojawiają się też rzutowania z tekstu na liczbę i odwrotnie :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBS60hlsRY70Lv3o1WSFOMwHiBlXfcbQTpatXrx6itsUpdd7W19et5mpEXYLSmCLV0-OfV4hHzqbWgXEfiA8-MNCqd-yrIxwqRF9vtP9x2X2qjtOmZ20GZAqyTE0e8EIQOQMTuJVX0hEE0/s1600/39.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhBS60hlsRY70Lv3o1WSFOMwHiBlXfcbQTpatXrx6itsUpdd7W19et5mpEXYLSmCLV0-OfV4hHzqbWgXEfiA8-MNCqd-yrIxwqRF9vtP9x2X2qjtOmZ20GZAqyTE0e8EIQOQMTuJVX0hEE0/s1600/39.png" /></a></div>
<br />
<br />
Po wprowadzeniu długości boku i kliknięciu przycisku „Oblicz” mamy taki efekt:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxrgFFotADNg4XEKba4IzrKPiqTjrRleOWDJZKUQ2tAeQb_exj73R5S7WQhmDMMRu5iMwAhpD9946pgiM2NS4WDz7XLmgKemmI75PgRGTiC5mBy56yCmDVkhwzypzILdpJuqYBnPjxMk1Z/s1600/40.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxrgFFotADNg4XEKba4IzrKPiqTjrRleOWDJZKUQ2tAeQb_exj73R5S7WQhmDMMRu5iMwAhpD9946pgiM2NS4WDz7XLmgKemmI75PgRGTiC5mBy56yCmDVkhwzypzILdpJuqYBnPjxMk1Z/s1600/40.png" height="261" width="320" /></a></div>
<br />
<br />
W charakterze ćwiczenia, dokończ niniejszy projekt, może przyda się młodszemu rodzeństwu :)andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com13tag:blogger.com,1999:blog-5593586859352074500.post-44414622945591930542014-02-17T07:42:00.002-08:002014-02-18T06:16:45.216-08:00Otwieranie przeglądarki WWW z aplikacji<span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">Kod źródłowy do tej lekcji znajduje się</span></span></span><a href="http://jsystems.pl/storage/kurs_android/otwieranie_przegladarki_www_z_aplikacji.zip" target="_blank"><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;"> </span></span><span style="background-color: yellow;"><span style="background-color: #38761d;">tutaj</span></span></span></a><span style="background-color: #38761d;"><span style="background-color: yellow;"><span style="background-color: #38761d;">. 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.</span></span></span><br />
<br />
W tym przykładzie pokażę w jaki sposób możemy otworzyć zewnętrzną (domyślną) przeglądarkę z poziomu aplikacji. Zaczynam jak zawsze od najprostszego przykładu. Stworzyłem nowy projekt i przykleiłem guzik z napisem „Gugiel!”. Po naciśnięciu przycisku powinna zostać uruchomiona przeglądarka z uruchomionym adresem http://google.pl<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNE-0Hg12rRn54H2DTc5VZJtw9SQfw_giA-GWKGoC455keAlVuYAKVvTD_iGQ2now4ma24ne_IXzudBAXROzbclssGtBb9PUff019Xu3y7w-CJmSXKBjXfbVtP4Ui4uyfU3y_0McdrgbSg/s1600/1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiNE-0Hg12rRn54H2DTc5VZJtw9SQfw_giA-GWKGoC455keAlVuYAKVvTD_iGQ2now4ma24ne_IXzudBAXROzbclssGtBb9PUff019Xu3y7w-CJmSXKBjXfbVtP4Ui4uyfU3y_0McdrgbSg/s1600/1.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Przechodzimy teraz do kodu aktywności. Linia 18 to podpięcie referencji do guzika. Muszę to zrobić by móc siędo niego odwoływać. Linie 19-27 to podpięcie listenera pod kliknięcie guzika Właściwa akcja dzieje się w linijkach 22-24 i w zasadzie tylko do nich się ogranicza. Adres który ma zostać otwarty będę musiał przekazać jako obiekt klasy Uri, tworzę więc go w oparciu o adres http://google.pl. Tu może być np. strona Twojej firmy, albo link do wyszukiwarki z przekazanymi przez pasek parametrami. Aby uruchomić przeglądarkę musimy stworzyć podając adres uri i wywołać nową intencję – co zawiera się w liniach 23,24.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbLXP38K9swo7EpHPD_TOmHPLCJfJtvx0h-pcXDV_HZaoFSf1iFINVPIA96G3S_23FrnFk8OTu1MN1zUTnRp0DhHS07c6Hprs0lyhV9L6yezUZCBG5FCXWjxaztM7xIJxreHZRVfwGoDjh/s1600/2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjbLXP38K9swo7EpHPD_TOmHPLCJfJtvx0h-pcXDV_HZaoFSf1iFINVPIA96G3S_23FrnFk8OTu1MN1zUTnRp0DhHS07c6Hprs0lyhV9L6yezUZCBG5FCXWjxaztM7xIJxreHZRVfwGoDjh/s1600/2.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Ekran po uruchomieniu:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB5dAo7WVx9ZM6sntAOfyU805l0hdO8F9gkAxu2962jW3uBFR55KmBhi9R7KSYJysffCDRt8qtVbk3xlRJnX__TxklLzJR3axxS3m4wbafBONm_y18hZs3_7a1E0jw8kxm4ZucJwxv8Dgy/s1600/3.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhB5dAo7WVx9ZM6sntAOfyU805l0hdO8F9gkAxu2962jW3uBFR55KmBhi9R7KSYJysffCDRt8qtVbk3xlRJnX__TxklLzJR3axxS3m4wbafBONm_y18hZs3_7a1E0jw8kxm4ZucJwxv8Dgy/s1600/3.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Po naciśnięciu przycisku uruchomiła mi się domyślna przeglądarka z uruchomionym wskazanym adresem:</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIWKyAhEbZ4oIYTBn8Sw5QE89JzPSdgujHEn46-dK-Z4q8j9jq54-5W1DGe1ufWId7euu9jVf7hfqlM2dotC0FyA5qFJozGoStoSO9GqYIL4ogncLzq24uynn-b-iW0LKHOnqak8YA6EX2/s1600/4.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIWKyAhEbZ4oIYTBn8Sw5QE89JzPSdgujHEn46-dK-Z4q8j9jq54-5W1DGe1ufWId7euu9jVf7hfqlM2dotC0FyA5qFJozGoStoSO9GqYIL4ogncLzq24uynn-b-iW0LKHOnqak8YA6EX2/s1600/4.png" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Troszkę przerobimy teraz naszą aplikację. Użytkownik sam wpisze adres strony która ma zostać wywołana. Dodaję do widoku element EditText i zmieniam tekst na guziku:</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSjKbsmTl74htM6Qmqym4O_iQEkorYQWUVBW7SJy2ADZi6sL-VCdDiZ5pT4jh51vNsG_FZuFECmmees-s2gRmS6P8qKx-jdPnvV28I7ERDGm_DlLtk-3uukDq9ppUwB5U3RG159uTborqO/s1600/5.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgSjKbsmTl74htM6Qmqym4O_iQEkorYQWUVBW7SJy2ADZi6sL-VCdDiZ5pT4jh51vNsG_FZuFECmmees-s2gRmS6P8qKx-jdPnvV28I7ERDGm_DlLtk-3uukDq9ppUwB5U3RG159uTborqO/s1600/5.png" /></a></div>
<br />
Musi za tym iść mała przeróbka w kodzie. Interesują nas linie 23 i 24. W linii 23 dodałem uchwyt do okienka edycyjnego. W linii 24 konkatenuję adres url na podstawie początku http:// (bo o tym pewnie każdy użytkownik zapomni, a bez tego nie zadziała) oraz tekstu wpisanego w oknie edycyjnym. Pozostała część programu pozostaje bez zmian.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxC6ajYXWis4agXMKqMDCxwLSVfxkIX9CtEWh8oxdSb2fBiIxBab-Lb5p3SnMh8D34vTv8Snm6NbDtEghm8-mdqnSg1wQuCzVasAUzLe-rdoizoUAF-5qzvkKsIk8uoWSxDrAr2JaKtIpY/s1600/6.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjxC6ajYXWis4agXMKqMDCxwLSVfxkIX9CtEWh8oxdSb2fBiIxBab-Lb5p3SnMh8D34vTv8Snm6NbDtEghm8-mdqnSg1wQuCzVasAUzLe-rdoizoUAF-5qzvkKsIk8uoWSxDrAr2JaKtIpY/s1600/6.png" /></a></div>
<br />
Uruchomiłem program i wpisałem adres strony internetowej:<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSrIkBOKvPG_y1H0L6XPRXJ06WWGbOSoCYPq0TTjn9UKeqkbW0F3YhD5_qV7GDw7L1UJUDT9jFxqmrprhNs-lnCc8NM4nh0FXZMkfbeMZDluBa5FwPa0U_pkXyAkAR6_8OjnT-BHxmNDZp/s1600/7.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjSrIkBOKvPG_y1H0L6XPRXJ06WWGbOSoCYPq0TTjn9UKeqkbW0F3YhD5_qV7GDw7L1UJUDT9jFxqmrprhNs-lnCc8NM4nh0FXZMkfbeMZDluBa5FwPa0U_pkXyAkAR6_8OjnT-BHxmNDZp/s1600/7.png" /></a></div>
<br />
Po uruchomieniu:<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGEmnz0mZR4KsbDNcBUg1dKWbJkgcOETrNRiM05BBhRh1xUzHhQqAPf8Fnv_81owt5UCjhHzykbmDwEtsvzlgzaNiPo4H23prwnKHBEpY10Me7MdTU1OYFo752c2zD8tdcEdB0IHm8Znrz/s1600/8.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiGEmnz0mZR4KsbDNcBUg1dKWbJkgcOETrNRiM05BBhRh1xUzHhQqAPf8Fnv_81owt5UCjhHzykbmDwEtsvzlgzaNiPo4H23prwnKHBEpY10Me7MdTU1OYFo752c2zD8tdcEdB0IHm8Znrz/s1600/8.png" /></a></div>
<br />
<br />
Jeszcze jedna ciekawostka, aczkolwiek bardziej w charakterze „bajeru”. Jeśli odnajdziemy w pliku określającym layout element odpowiedzialny za nasz EditText i dopiszemy android:inputType=”textUri”, to po kliknięciu na okienko do wprowadzania tekstu wyskoczy nam klawiatura ze specjalnymi guzikami do wprowadzania adresów URL np. „www” :)<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihOuzZLPYqxW4ZLfILznf87L5iWX7Ibv8jUkBtUCZ_Z89EdvhcV7Yd7r1ww136sgQSPhmc9Cftf8oTxu1InB-XcFSlW-qFOYcvRm1pcoUWwvI_m6eyfdPi71FsRbve3DgOzgZ8LgXpfRDl/s1600/9.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihOuzZLPYqxW4ZLfILznf87L5iWX7Ibv8jUkBtUCZ_Z89EdvhcV7Yd7r1ww136sgQSPhmc9Cftf8oTxu1InB-XcFSlW-qFOYcvRm1pcoUWwvI_m6eyfdPi71FsRbve3DgOzgZ8LgXpfRDl/s1600/9.png" /></a></div>
<br />
Tych inputType'ów jest znacznie więcej rodzajów, między innymi do wprowadzania adresów email, liczb etc .andrewhttp://www.blogger.com/profile/10069507480509251586noreply@blogger.com11