Android - aplikacja do zarządzania zdjęciami

Opublikowany: 18-07-2013 21:49 przez Krystian

Jak głosi stare przysłowie - 'ciekawość to pierwszy stopień do piekła', przez ostatnie lata intensywnie pracowałem na miejsce w najgorętszym kotle, ponieważ ciekawość nieustannie zwodziła mnie na różne, czasem dziwne ścieżki. Mam na myśli projekty i inicjatywy informatyczne i programistyczne, w których uczestniczyłem prywatnie oraz służbowo.

 

Do tej pory nie dane mi było zajmować się aplikacjami na urządzenia mobilne. Pierwszym impulsem w kierunku zmiany stanu rzeczy było kupienie tabletu jakieś pół roku temu. Po kilku godzinach użytkowania do głowy wpadła myśl - 'fajnie by było coś na to płaskie cudo napisać'. Niestety nadmiar obowiązków oraz nieprzeczytanych książek żałośnie spoglądających na mnie z regału na kilka miesięcy odwiodły mnie od tego tematu. Na szczęście na jednym z przedmiotów na mojej ulubionej Politechnice prowadzący laboratoria rzucił pytanie: 'wolicie kolokwium czy projekt androida na zaliczenie?'. Doszedłem do wniosku, że naskrobanie projektu może znacznie lepiej podnieść moje kwalifikacje niż nauczenie się na kolosa.

 

Wybór padł na aplikację, której wymagania funkcjonalne można było skrócić do: 'ma robić zdjęcia i być w stanie przesłać je na serwer'. Nie byłbym sobą gdybym do listy funkcji nie dorzucił kilkunastu innych elementów i nie postawił sobie też wymagania, że aplikacja ma działać szybko, ma być przyjemna w użytkowaniu i sprawiać wrażenie profesjonalnie wykonanej.


Dla zainteresowany kod aplikacji na Androida znajdziecie tutaj, zaś serwer odbierający zdjęcia (napisany w zwykłym C pod Linuxa) tutaj. Pamiętajcie tylko, że używanie cudzego kodu jako swojego to lamerstwo :D Inna rzecz zajrzeć do niego i dowiedzieć się jak można dany element zrealizować! Pierwszą rzeczą było zorganizowanie sobie warsztatu pracy. Ponieważ miałem swoje własne, dość dobre urządzenie z Androidem mogłem zrezygnować z używania wirtualek. Zainteresowałem sie zestawem ADT, który okazał się strzałem w dziesiątkę, ponieważ po podłączeniu kabelkiem tabletu do komputera (i po ustawieniu jednej rzeczy na nim) mogłem za pomocą jednego kliknięcia załadować aktualną wersję kodu do tabletu i na bieżąco obserwować wprowadzane zmiany. Świetnym udogodnieniem było też debuggowanie na żywo (-:


Komfort pracy zaowocował radością tworzenia. Ponieważ chciałem profesjonalnego wyglądu i zachowania doszedłem do wniosku, że zrobione zdjęcia chcę trzymać w takim fajnym, kafelkowym, skrolowanym widoczku, który jak dowiedziałem się po bliższym zaznajomieniu z realizacją layoutów w Androidzie zowie się GridView. GridView okazał się być bardzo przydatnym bytem po bliższy zapoznaniu. Po napisaniu odpowiedniego adaptera (klasa ImageAdapter w projekcie) do feedowania zdjęciami zabrałem się za możliwość zaznaczania obrazków w celu dalszej manipulacji nimi, oraz za dodawanie odpowiedniego oznaczenia zaznaczonym przez użytkownika zdjęciom. To było zadaniem stosunkowo łatwym, wystarczyło użyć odpowiedniego listenera (setOnItemClickListener(new OnItemClickListener()... w głównej klasie MediaMateActivity). Wygląd głównego okna mojej aplikacji zobaczyć możecie na pierwszych dwóch screenshotach. Oczywiście utworzyłem odpowiednie layouty dla ułożenia poziomego oraz pionowego.


Ponieważ aplikacja traciła swój stan po zmianie orientacji oraz działała dość wolno po zaznaczeni lub odznaczeniu elementu zainteresowałem sie tymi dwoma tematami. Rozwiązując pierwszy problem dowiedziałem się, że aplikacja podczas zmiany orientacji (oraz w kilku innych sytuacjach) w pewien sposób respawnuje się (kolokwialnie rzecz ujmując). Programista ma do dyspozycji możliwość zachowania odpowiednich danych przed oraz przywrócenia ich po owym odświeżeniu stanu. Do obsługi tego służą metody onSaveInstanceState oraz onRestoreInstanceState w głównej klasie projektu.

 

Drugi problem związany był z tym, że mój jeszcze niedoskonały adapter ładował fotki z dysku przy każdym odświeżeniu grida, co nie miało sensu we wszystkich sytuacjach po za momentem kiedy została zrobiona nowa fotka, lub któraś ze starych została usunięta. Metodę odświeżającą GridView podzieliłem na dwie fullGridRefresh, która używana była w wymienionych sytuacjach oraz thumbnailsGridRefresh, która używana była we wszystkich pozostałych przypadkach. Jak można się domyśleć dodałem też tablicę z miniaturkami w celu ograniczenia odwoływania się do pamięci urządzenia.


Po tych poprawkach aplikacja zaczęła działać bardzo żwawo. Wygląd jej po zaznaczeniu kilku elementów możecie zobaczyć na trzecim screenshocie. Jak bystre oko zauważy zaznaczenie przynajmniej jednego elementu skutkuje odblokowaniem przycisków Delete i Upload (-;

 

Doszedłem do wniosku, że gdybym był użytkownikiem chciałbym sobie zrobić zooma na wybranych fotkach. Do tego celu utworzyłem oddzielny layout zdefiniowany w pliku imagezoom.xml, który wywoływany jest dla odmiany po wykonaniu długiego kliknięcia (setOnItemLongClickListener(new OnItemLongClickListener() ... ) i który tymczasowo przesłania widok normalnego okna. Efekt zrobienia zooma możecie zobaczyć na screenie numer cztery.

 

Obsługę zachowania klawisza Delete pominę, bo jak możecie się domyślać usuwanie fotki to wykonanie metody delete() na odpowiednim obiekcie (-:

 

Jeśli chodzi o obsługę klawisza Upload to podczas swoich pierwszych prób odkryłem, że programiści googla wymusili na developerach platformy uruchamianie kodu odpowiedzialnego za komunikację sieciową w oddzielnym/oddzielnych wątkach. Słuszny wybór, dzięki temu nasza aplikacja będzie repsonsywna nawet podczas transferu większych bloków danych.


Do realizacji zadania wysyłania fotek użyłem klasy UploadFile, która dziedziczy po klasie AsyncTask. Wszystko co interesujące znajdziecie w metodzie doInBackground (-: Oczywiście utworzenie nowego gniazda służącego do transmisji jest tak proste jak zapis socket = new Socket(this.ipAddress, Integer.parseInt(this.port)); . Ponieważ użytkownicy nie lubią być trzymani w niepewności dodałem też obsługę komunikatów o błędach.

 

Oczywiście, żeby móc wysyłać fotki trzeba je najpierw mieć. Nie ma sensu próbowanie pisania własnej obsługi kamery, jeśli potrzebujemy podstawowych możliwości. W tym celu wystarczy posłużyć się Intentem, tak jak ja to zrobiłem za pomocą: Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); . Po powrocie z kamery dostajemy odpowiednie dane (w naszym przypadku plik ze zdjęciem) do naszego programu. Intenty oczywiście służą do wielu innych rzeczy, a nie tylko do robienia fotek.


Skoro już chciałem mieć profesjonalny klimat to nie mogłem pozwolić na trzymanie danych na temat serwera odbierającego fotki gdzieś w kodzie strony. Zapoznałem się z informacjami na temat tworzenia ustawień aplikacji i udało mi się stworzyć menu, które możecie zobaczyć na screenshocie numer pięć. Można w nim swobodnie zdefiniować IP oraz port.

 

Skoro już mowa o serwerze to na sam koniec wspomnę tylko o nim w telegraficznym skrócie. W aplikacji na Androida dodałem coś w rodzaju quasi protokołu do transmisji danych. Quasi, ponieważ strumień jest dość prosty i ma wzór: 1000*nazwa_pliku*zawartość_zdjęcia . To co robi serwer to wyciąga z nadsyłanego strumienia nazwę pliku, tworzy go i ładuje do niego treść zdjęcia. Komunikaty pojawiające się przy okazji tych zdarzeń możecie zobaczyć na ostatnim screenshocie.

 

To w zasadzie wszystko. Dzięki za ewentualne uwagi do mojego kodu (-:


bro

Brak komentarzy