#9 Pierwsza lekcja dodawania

Pokazywaliśmy już liczby, jako ilości na wszelkie możliwe sposoby. Wyświetlając je na stosie, wyświetlając na ciągu stosów, przedstawiając je obok siebie. Najwyższy czas zrobić coś trudniejszego, mianowicie pora nauczyć się dodawać. Prezentację dodawania myślę, że zrobię innym razem, na razie przeszedłem dalej bezpośrednio do ćwiczeń dodawania. Mam nadzieję, że po takiej ilości prezentacji to, co chcemy osiągnąć będzie już dla ucznia jasne.

Aktywność w tym momencie działa w ten sposób, że losuje nam liczbę z przedziału 0 do 5 włącznie. Po wylosowaniu, wyświetla odpowiednią ilość belek. Użytkownik ma za zadanie dodać te belki przyciskami (+ i -) tak, aby wynik się zgadzał. Po wykonaniu tych operacji naciska na floating action buton, aby sprawdzić wynik. W przypadku, w którym wynik jest prawidłowy zostaje wyświetlona pełna gwiazdka. W przypadku błędnego wyniku, wyświetlona gwiazdka jest pusta w środku. Użytkownik ma pięć prób. Na ten moment nie ma żadnej animacji losowania belek, ponieważ zostaną one przykryte czymś extra, o czym w następnym wpisie 😉

A wygląda to tak:
Kodzik nie jest za bardzo złożony, tak naprawdę obsługę belek mamy już zrobioną, więc zrobiłem obsługę cyfr, ale ona i tak będzie obsłużona pewnie inaczej i w innym miejscu 😉

private void createReadyView() {
    firstRand = random.nextInt(6);
    secondRand = random.nextInt(6);
    if (firstRun) {
        bricksFirstArray = stackBricks.getBricksStack(includeFirstStack,
   firstRand);
        bricksSecondArray = stackBricks.getBricksStack(includeSecondStack,
   secondRand);
        bricksResultArray = stackBricks.getBricksStack(includeResultStack);
        firstRun = false;
    } else {
        bricksFirstArray = stackBricks.refreshStack(bricksFirstArray, 
   firstRand);
        bricksSecondArray = stackBricks.refreshStack(bricksSecondArray, 
   secondRand);
        bricksResultArray = stackBricks.refreshStack(bricksResultArray);
    }
    counter = 0;
    firstStackCounterTV.setText(String.valueOf(firstRand));
    secondStackCounterTV.setText(String.valueOf(secondRand));
    resultStackCounterTV.setText(String.valueOf(counter));
    result = firstRand + secondRand;
}
Wywołujemy tą metodę, która decyduje czy jest to pierwsze, czy kolejne jej uruchomienie. W przypadku pierwszego łączy widoki i wyświetla odpowiednią ilość belek, w przypadku kolejnego, po prostu losujemy ile belek ma się wyświetlać. Ponadto losujemy cyfrę, którą ustawiamy, jako napis pod stosem i po kliknięciu przez użytkownika sprawdzamy, czy suma cyfr zgadza się z wynikiem.
W tej aktywności po raz pierwszy zostały pokazane też operatory arytmetyczne dodawania i znaku równości. Mam nadzieję, że taki nieinwazyjny sposób uświadomi użytkownikom, co one oznaczają.

 

Pozdrawiam!

#8 Biblioteka ButterKnife, czyli najwyższy czas zacząć pisać mniej kodu

Standardowy kod, który trzeba napisać to rzecz, która sprawia radość tylko jak się pierwszy raz programuje 😉 Człowiek jest pełen pasji, odkrywa nowe rzeczy i wcale mu nie przeszkadza, że generuje lub pisze kolejny setter, czy powiela linijkę i zmienia tylko nazwy. Z czasem jednak coś zaczyna uwierać. Brzydko to potem wygląda, leży najczęściej na spodzie klasy, nikt tego nie czyta (bo i po co czytać auto-wygenerowane przez środowisko, gettery, settery itd). W Androidzie mamy jeszcze kilka rzeczy ponad standardowe, javowe instrukcje. A jak powszechnie wiadomo, co swoją drogą opisał już dawno temu Einstein:

No dobra, może Einstein nie koniecznie to miał na myśli, ale pasuje bardzo dobrze 😉 Im więcej kodu, tym większa szansa, że nie zauważamy czegoś ważnego. Czasem w ogóle zdarza się zapomnieć czegoś wygenerować i problem gotowy. Z czasem zaczyna się po prostu nie chcieć tego wszystkiego pisać. A jak wiadomo, lenistwo jest motorem postępu, dlatego opiszę bibliotekę Butter Knife, która pozwoli nam zaoszczędzić kilka linijek, i bardzo ułatwi późniejsze czytanie kodu. Biblioteka nie jest jakąś super nowością na rynku, nie mniej wato docenić to co oferuje.
Instalacja tej biblioteki to po prostu kwintesencja lenistwa, w pliku build.gradle, w module aplikacji:
compile 'com.jakewharton:butterknife:7.0.1'
Ewentualnie podbijamy wersję, jeśli pojawi się jakaś aktualizacja albo instalujemy wersję beta, jak ktoś lubi 😉
Pierwszą rzeczą, którą możemy wyeliminować jest zapis:
TextView stackCounterTV = (TextView) findViewById(R.id.bricksTV);
Czyli standardowy androidowy zapis łączący elementy widoku z plików xml z obiektami. Niby nic wielkiego, ale pamiętajmy, że trzeba go umieszczać w metodzie cyklu życia, najczęściej onCreate(), albo przynajmniej stamtąd wywoływać. A jak wiemy, obiektów dziedziczących po View możemy mieć w aktywności całkiem sporo. Dzięki Butter Knife można skorzystać z adnotacji:
@Bind(R.id.bricksTV) TextView stackCounterTV;
definiując nasz obiekt.
Z kolei w metodzie onCreate powinno być jeszcze wywołane (zresztą, za każdym razem kiedy korzystamy z Butter Knife):
ButterKnife.bind(this);
Ok, przy pierwszym przykładzie mamy taką samą ilość kodu, ale znacznie zmienia się jakość i czytelność. Widoki których będziemy używać w całej klasie mamy już zdeklarowane na samym początku, nie musimy już z nimi nic dodatkowo robić w metodach cyklu życia, możemy normalnie z nich korzystać. Kolejny przykład jest bardziej sexi:
@Bind({R.id.starIV1, R.id.starIV2, R.id.starIV3, R.id.starIV4, R.id.starIV5})
ImageView[] starContainer;
Pobrane obiekty wpakowujemy od razu do tablicy, nie musimy ich nigdzie deklarować, dzięki czemu zaoszczędzamy już 6 linii standardowego kodu.
Dalej spójrzmy sobie na:
@OnClick(R.id.addingPlusBtn)
   void add() { }

Nie musimy pisać metody onClick w xml, ani dodawać listenera na obiekcie. To wszystko, co potrzeba, żeby kliknięcie zadziałało. Możemy sobie jeszcze wywołać @onLongClick, do długich kliknięć, albo @onItemClick dla elementów typu listView.
Biblioteka radzi sobie także z fragmentami, trzeba jednak trochę zmienić standardową metodę na:
ButterKnife.bind(this, view);

Jeszcze kilka ciekawych funkcji z tej biblioteki:

@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red;

Czyli pobranie grafiki, koloru, albo napisów w aktualnym języku (o ile jest obsłużony, jeśli nie, pobiera w języku aplikacji). Jak widać, znowu oszczędzamy sporo linii.

Wciąż mało? 😉
To na koniec coś dla ekstremalnie leniwych, czyli dodatek, który po kliknięciu skrótu „Generate code” wygeneruje nam to co potrzebujemy 😉 Nie działa w aktualnej wersji Android Studio, ale autor pracuje nad poprawką. Dodatek nazywa się android-butterknife-zelezny i znajduje się tutaj i tutaj.

Wypisałem najważniejsze możliwości, które daje nam biblioteka Butter Knife. Patrząc obiektywnie, nie ma tego dużo. Korzystanie z niej jest dzięki temu bardzo proste, intuicyjne, próg wejścia jest niski, a korzyści naprawdę spore. Osobiście bardzo polecam 😉
Pozdrawiam!

#7 Porównywanie stosów

W dwóch ostatnich postach pokazywałem jak zrobić animację na ścieżkach. Dzisiaj pokażę w jaki sposób wykorzystałem te animację.

Efekt który uzyskałem prezentuje się w ten sposób:

Chodziło o to, aby zwrócić uwagę dzieciaków na to, kiedy ilość jest w równowadze. Tzn, jeśli po lewej mamy tyle samo obiektów jak po prawej, to kolor FAB zmienia się na zielony. Jeśli zmieniamy te ilości przycisk odpowiednio reaguje.
Kodzik do tego nie jest zbytnio skomplikowany, wszystko dokładnie można obejrzeć na gicie, wkleję tylko kawałki:
public void onClickCompareOperations(View view) {
    switch (view.getId()) {
        case R.id.plusLeftBtn:
            if (counterLeft < 10) {
                counterLeft++;
                stackBricksViewGroup.refreshView(counterLeft, bricksLeftArray, 
      getApplicationContext());
            }
            break;
        case R.id.minusLeftBtn:
            if (counterLeft > 0) {
                counterLeft--;
                stackBricksViewGroup.refreshView(counterLeft, bricksLeftArray,
              getApplicationContext());
            }
            break;
        case R.id.plusRightBtn:
            if (counterRight < 10) {
                counterRight++;
                stackBricksViewGroup.refreshView(counterRight, bricksRightArray, 
      getApplicationContext());
            }
            break;
        case R.id.minusRightBtn:
            if (counterRight > 0) {
                counterRight--;
                stackBricksViewGroup.refreshView(counterRight, bricksRightArray, 
     getApplicationContext());
            }
            break;
    }
    if(counterLeft!=counterRight&&firstChange) {
        morphAnim.animFab(R.drawable.ic_done_to_undone, false);
        firstChange=false;
    }
    if(counterLeft==counterRight) {
        morphAnim.animFab(R.drawable.ic_undone_to_done, true);
        firstChange=true;
    }
}

Jak widać po każdej zmianie sprawdzamy co było kliknięte, odświeżamy odpowiedni widok i odświeżamy fab.

public void animFab(int resId, boolean success) {
    final Animatable icon = (Animatable) ResourcesCompat.getDrawable(context, resId);
    floatingActionButton.setImageDrawable((Drawable) icon);
    icon.start();
    if(success)
        floatingActionButton.setBackgroundTintList(context.getResources()
            .getColorStateList(R.color.colorSuccessGreen));
    else
        floatingActionButton.setBackgroundTintList(context.getResources()
     .getColorStateList(R.color.colorFailureRed));
}
Sama animacja Fab to również tylko kilka linijek kodu, ponieważ lwią pracę ostatnio odwaliliśmy po stronie xmla.
Następnym krokiem będzie stworzenie aktywności, która będzie sprawdzała po kliknięciu, czy stosy są w równowadze i wyświetlała tą informację.
Pozdrawiam!

#6 Morphing – animujemy floating action button cz.2 ścieżki

W poprzedniej części opisałem jak stworzyć animację morphingu bez jednego aspektu, mianowicie ścieżek, czyli rzeczy zgoła najpotrzebniejszej. Dlatego dzisiaj skupimy się na ścieżkach.

Przykładowa ścieżka wygląda tak:

M9,16.2 L4.8,12 L3.4,13.4 L9,19 L21,7 L19.6,5.6 L9,16.2 Z

W języku svg tak:

<svg fill="#FFFFFF" height="48" 
        viewBox="0 0 24 24" width="48" 
  xmlns="http://www.w3.org/2000/svg">
    <path d="M0 0h24v24H0z" fill="none"/>
    <path d="M9 16.2L4.8 12l-1.4 1.4L9 19 21 7l-1.4-1.4L9 
     16.2z"/>
</svg>

Z kolei jako png tak (kliknij aby zobaczyć):

Główna zasada tworzenia ścieżek do morphingu jest taka, że muszą być kompatybilne, tzn muszą być stworzone z takiej samej ilości elementów, czyli liter i cyfr przy nich, poukładanych w takiej samej kolejności, różniących się oczywiście zawartością, np:
<string name="path_undone">M 19.0,6.41 L 17.59,5.0 L 12.0,10.59 L 6.41,5.0 L 5.0,6.41 L 10.59,12.0 L 5.0,17.59 L 6.41,19.0 L 12.0,13.41 v 0.0 l 0.0,0.0 l 0.0,0.0 v 0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 L 17.59,19.0 c 0.0,0.0,0.0,0.0,0.0,0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 L 19.0,17.59 m 0.0,0.0 L 13.41,12.0 c 0.0,0.0,0.0,0.0,0.0,0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 v 0.0 l 0.0,0.0 l 0.0,0.0 v 0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 c 0.0,0.0,0.0,0.0,0.0,0.0 L 19.0,6.41 Z</string>
<string name="path_repeat">M 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 L 12.0,6.0 v 3.0 l 4.0,-4.0 l -4.0,-4.0 v 3.0 c -4.42,0.0,-8.0,3.58,-8.0,8.0 c 0.0,1.57,0.46,3.03,1.24,4.26 L 6.7,14.8 c -0.45,-0.83,-0.7,-1.79,-0.7,-2.8 c 0.0,-3.31,2.69,-6.0,6.0,-6.0 L 12.0,6.0 m 6.76,1.74 L 17.3,9.2 c 0.44,0.84,0.7,1.79,0.7,2.8 c 0.0,3.31,-2.69,6.0,-6.0,6.0 v -3.0 l -4.0,4.0 l 4.0,4.0 v -3.0 c 4.42,0.0,8.0,-3.58,8.0,-8.0 c 0.0,-1.57,-0.46,-3.03,-1.24,-4.26 L 18.76,7.74 Z</string>

Widzimy tutaj sporo 0, ale dzięki temu uzyskujemy kompatybilność, a efekt który dają nam te ścieżki w zwolnionym tempie wygląda tak:

Noo dobra, wytłumaczyłem jak to musi wyglądać, teraz jak transformować pliki svg na ścieżki? Na początku trafiłem na stronę svg2android. Jeśli wrzucimy tam plik svg, i zaznaczymy opcję:
„Bake transforms into path (experimental)”
to otrzymamy poprawną ścieżkę. To samo robimy z drugim plikiem do którego chcemy morphować i voilla. Chociaż nie do końca, bo jak pisałem powyżej w zależności od złożoności ikony możemy otrzymać różne długości ścieżek i trzeba to wyrównać, można to zrobić oczywiście ręcznie jeśli ścieżka jest krótka jak tutaj:
 
<string name="path_line">M22,12 L18,8 L18,11 L3,11 L3,13 L18,13 L18,16 Z</string>
<string name="path_done">M9,16.2 L4.8,12 L3.4,13.4 L9,19 L21,7 L19.6,5.6 L9,16.2 Z</string>

Które to ścieżki dają nam taki efekt:

Jest jednak szybszy sposób 😉
Biblioteka vectalign, pobierany sobie stamtąd plik .jar, uruchamiamy go i podajemy paramtery –start nazwa pierwszego svg, –end nazwa drugiego svg tak wygląda efekt:
Kopiujemy sobie ścieżki z pliku wynikowego i uzyskujemy bardzo ładną animację, np taką:
Dla porządku dodam jeszcze ścieżki dla tej konkretnej animacji:
 
<string name="path_line">M22,12   L18,8  L18,8  L18,11  L18,11  L3,11 L3,11  L3,13  L3,13 L18,13  L18,13  L18,16 Z</string>
<string name="path_undone">M19,6.41 L17.59,5 L12,10.59 L6.41,5 L5,6.41 L10.59,12 L5,17.59  L6.41,19  L12,13.41 L17.59,19 L19,17.59 L13.41,12 Z</string>
I to już wszystko co udało mi się odkryć w temacie morphingu, tak naprawdę dowolną ikonę możemy przerobić na svg, a następnie na ścieżkę, dlatego mam wrażenie że w projekcie jeszcze pojawi się tego typu animacja nie raz i to niekoniecznie na tak małych obiektach jak floating action button 😉
Pozdrawiam!

#5 Morphing – animujemy floating action button cz.1

Razem z pojawieniem się Material Design, Androida Lollipop wprowadzono nowy sposób przechowywania ikon. Zamiast plików graficznych jak np png postawiono na zgodny z ideą flat design plik typu svg. Nietrudno zrozumieć taką decyzję:

xxxhdpi to rozmiar dla fullHd, ileż jeszcze można tych x dodać, żeby obsługiwać 4k, albo w przyszłości 16k? 😉 Svg ma ten plus, że skaluje się bez utraty jakości, tzn jest jeden plik, który obsłuży wszystkie rozdzielczości. Dla bardziej zainteresowanych standardem, który opracował W3C zresztą link.

Aplikacji które korzystały by jednak z tego typu rozwiązań jakoś błyskawicznie nie przybywa (png znajdujący się w folderze xxxhdpi przeskaluje się na ekranie 4k, a że smartfony mają rozmiar jakieś 6-7 cali te większe, tablety jakieś 10 cali, to i tak tego gołym okiem nie widać. Może na telewizorze 4k to widać, jak się patrzy z bliska, ale nie po to są telewizory, żeby patrzeć z bliska ;). Dlatego też pliki png mają się dobrze. Zaletą stosowania svg jest możliwość korzystania z morphingu, który na png musielibyśmy wywołać jakimś zewnętrznym programem, przerabiając go zresztą na gifa. No i tak ten gif by się potem skalował na większy ekran.. Ok, wiemy już, że powinniśmy korzystać z svg. Samo korzystanie jest takie samo jak z png w Androidzie, wrzucamy plik tylko do jednego folderu: drawable, a widać go wszędzie. A teraz zabierzmy się za morphing, ufff trochę czasu mi zajęło rozgryzienie tego.

Aby móc użyć animacji morphingu na androidzie poniżej wersji 5.0 skorzystałem z biblioteki vector-compat, która dostarczyła kilka gotowych animacji:

Nie pasowały one jednak do tego co chciałem uzyskać, dlatego też zrobiłem kilka swoich. Oto jak się to robi w androidzie.
Zacznę może od kodu w javie, musimy odnieść się do pliku xml typu animated vector:
final Animatable icon = (Animatable) ResourcesCompat.getDrawable
      (getApplicationContext(), R.drawable.ic_line_to_done);
fab.setImageDrawable((Drawable)icon);
icon.start();
Zawartość tego pliku (ic_line_to_done.xml) to:
<?xml version="1.0" encoding="utf-8"?>
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@drawable/ic_line_vector">
    <target
        android:name="rotationGroup"
        android:animation="@anim/line_to_done_rotation" />
    <target
        android:name="v"
        android:animation="@anim/line_to_done_path" />
</animated-vector>
Gdzie widzimy 3 istotne rzeczy:
– android:drawable, czyli lokalizację opisu naszej początkowej ikony, tej od której będzie zaczynała się animacja
– target totationGroup, czyli odnośnik do aimacji rotacji, przed zmianą ikony następuje obrócenie aktualnej, taki efekt dodający fantazji 😉
-target v, czyli właściwy odnośnik do naszej animacji morphingu
Plik line_to_done_rotation, czyli nasz obrót:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="@integer/anim_duration"
    android:propertyName="rotation"
    android:valueFrom="0"
    android:valueTo="360"/>
Nic trudnego, po prostu zwykły obrót ikony, trwający 200ms. Plik line_to_done_path, czyli zmiana naszych ścieżek:
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"

    android:duration="@integer/anim_duration"
    android:propertyName="pathData"
    android:valueFrom="@string/path_line"
    android:valueTo="@string/path_done"
    android:valueType="pathType"
    app:vc_valueType="pathType"/>
Tutaj również jest łatwo, zwracam tylko uwagę na app:vc_valueType którego potrzebuje vector_comapt, żeby działać na starszych urządzeniach.
No i plik drawable, jest typu vector, oto jak wygląda:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:width="48dp"
    android:height="48dp"
    android:viewportHeight="24"
    android:viewportWidth="24"

    app:vc_viewportHeight="24"
    app:vc_viewportWidth="24">
    <group
        android:name="rotationGroup"
        android:pivotX="12.0"
        android:pivotY="12.0"
        android:rotation="360.0">

        <path
            android:name="v"
            android:fillColor="#FFFFFF"
            android:pathData="@string/path_line"

            app:vc_fillColor="#FFFFFF"
            app:vc_pathData="@string/path_line" />
    </group>
</vector>
Mamy tutaj grupę w której znajduje się plik path, czyli tak jak pisałem powyżej ścieżka opisująca wygląd naszej ikony. I przechodzimy do rzeczy najtrudniejszej, czyli właśnie ścieżek, oto jak przykładowo wyglądają:
M 48.0,54.0 L 31.0,42.0 L 15.0,54.0 L 21.0,35.0 L 6.0,23.0 L 25.0,23.0 L 25.0,23.0 L 25.0,23.0 L 25.0,23.0 L 32.0,4.0 L 40.0,23.0 L 58.0,23.0 L 42.0,35.0 L 48.0,54.0 
 
M 48.0,54.0 L 48.0,54.0 L 48.0,54.0 L 48.0,54.0 L 31.0,54.0 L 15.0,54.0 L 10.0,35.0 L 6.0,23.0 L 25.0,10.0 L 32.0,4.0 L 40.0,10.0 L 58.0,23.0 L 54.0,35.0 L 48.0,54.0
Mam nadzieję, że zaciekawieni, w następnej części opiszę jak uzyskać prawidłowe ścieżki z ikon znajdujących się tutaj. Bez tłumaczenia co oznaczają te liczby i litery, skupię się po prostu na tym w jaki sposób te ścieżki uzyskać i móc morfować dowolne ikony 😉

Finalnie uzyskałem takie efekty.

Pozdrawiam!

#4 Hakuna Matata, czyli wprowadzenie do języka Suahili

Jak pisałem już wcześniej, podczas tworzenia aplikacji musimy skupić się na matematyce, oraz na nauce języka Suahili (żeby to chociaż był angielski ;). No ale cóż, pora trochę zapoznać się z tym językiem. Wybór dla tamtego obszaru jest raczej oczywisty, jest to jeden z najpopularniejszych języków w całej Afryce, posługuje się nim około 50 mln osób. Można było się z nim zetknąć w książce „W pustyni i w puszczy” lub w filmie: „Król Lew”. Całe szczęście, że obecnie jest on opisywany alfabetem łacińskim, kiedyś używano arabskiego 😉 Suahili w języku Suahili nazywa się kiswahili.

Tytułowe Hakuna Matata znaczy: Nie ma problemu 😉 Język ten jest naprawdę dźwięczny i ma mnóstwo zwrotów, które w Polsce wydają się śmieszne. Wkrótce pewnie dodam wymowę liczenia podczas dodawania klocków, które implementowałem ostatnio, oto jak wygląda liczenie w suahili

sufuri – 0
moja – 1
mbili – 2
tatu – 3
nne – 4
tano – 5
sita – 6
saba – 7
nane – 8
tisa – 9
kumi – 10

Przeglądając zasoby internetu znalazłem jeszcze kilka fajnych zwrotów:

Piga picha mimi! – Zrób mi zdjęcie!
Choo iko wapi? – Gdzie jest toaleta?
Moja kwa moja – Prosto
Sasa hivi saa ngapi? – Która godzina?

Na ten moment nie mamy jakiegoś konkretnego planu w jaki sposób przeprowadzić naukę podstaw tego języka, chcemy dodać obrazki na których będzie zmieniał się wykonawca, czynność i obiekt który będzie podlegał czynności, ale jak zacząć, nie mamy pojęcia 😉 Będzie zabawa, mam nadzieję, że u odbiorców aplikacji również 😉

Pozdrawiam!

#3 Animacja opadania, ruch jest ważny!

Wraz z pojawieniem się Androida Lollipop w systemie Android pojawiło się sporo ciekawych animacji. Po wcześniejszych wersjach to była prawdziwa rewolucja, ludzie naprawdę potrzebowali ładnych rzeczy (swego czasu jeszcze przed Androidem 5.0 w sklepie Play przez jakiś czas bardzo popularny był kalkulator. Nie dlatego, że był super kalkulatorem, był po prostu kalkulatorem. Ale był bardzo ładny, naprawdę dopracowany pod względem designu i to wystarczyło, żeby coś tak oczywistego jak kalkulator, stało się jedną z najpopularniejszych aplikacji. Nie pamiętam jak się nazywał, to była moja własna obserwacja, ale wnioskuję, że ludzie pragnęli ślicznych aplikacji). Razem z tymi animacjami pojawiło się sporo wytycznych i przykładów jak powinny wyglądać i zachowywać się aplikacje w stylu Material Design, np tutaj. O albo taki widoczek:
Wygląda naprawdę dobrze, spróbujmy więc może zrobić chociaż namiastkę tego co tam widać. 😉
Problemem z którym musiałem się dzisiaj zmierzyć była animacja. Celem było osiągnięcie jak najbardziej „naturalnego efektu” podczas opadania klocków na stos. Chcemy, aby użytkownik odniósł wrażenie, że oto prawdziwy klocek upada, żeby był w stanie fizycznie „w życiu” sobie taką sytuację wyobrazić. Cały czas mamy w pamięci, że użytkownikami są dzieciaki, które nigdy nie miały kontaktu z technologią, nie możemy polegać na nawykach, które mają wykształcone użytkownicy z Polski, musimy takie nawyki stworzyć od nowa. Dlatego wybrałem animację odbijania, ale po kolei, najpierw troszkę logiki dodawania i odejmowania klocków
    public void refreshView(int counter, View[] bricksArray, Animation animation, boolean animOnlyLast) {
            if (counter>0&&bricksArray[counter].getVisibility() == View.INVISIBLE) {
                if(counter>1&&animOnlyLast)
                    bricksArray[counter-1].clearAnimation();
                bricksArray[counter].setVisibility(View.VISIBLE);
                animation.setInterpolator(new BounceInterpolator());
                bricksArray[counter].startAnimation(animation);
            } else {
                if(counter<9&&animOnlyLast)
                    bricksArray[counter + 2].clearAnimation();
                bricksArray[counter + 1].setVisibility(View.INVISIBLE);
                ReverseInterpolator reverseInterpolator = new ReverseInterpolator();
                animation.setInterpolator(reverseInterpolator);
                bricksArray[counter+1].startAnimation(animation);
            }

 

 
Trochę mi napisanie tego kawałka kodu czasu zajęło, już tłumaczę o co chodzi. Dostajemy licznik, czyli który klocek ma się wyświetlać (numer klocka na stosie), tablicę naszych obiektów, animację, oraz czy animować tylko ostatni element.
Główny if, licznik jest większy od zera, tutaj kolejność warunków jest ważna, jeśli bym je zamienił, to spróbował bym sprawdzić widzialność obiektu na null-u. Następnie, jeśli licznik jest większy od 1 (powód znowu null), oraz animuj ostatni element ma wartość prawda, wtedy wywołuję na obiekcie znajdującym się poniżej stosu metodę czyść animację. Jeśli bym tego nie zrobił, animował bym też tamten poprzedni obiekt. Czyli opadał by mi cały stos, zamiast tylko ostatniego klocka. Dalej już łatwo, ustaw widzialność, ustaw Interpolator (o tym poniżej, będą obrazki 😉 i uruchom animację na obiekcie.
Główny else i pierwszy if w nim, sprawdzenie czy znowu nie wywołamy wyczyszczenia animacji na nieistniejącym obiekcie (główny else oznacza tutaj ukrywanie obiektów, czyli mamy stos i przemieszczamy się w dół). Następnie oczywiście niewidzialność i odwrócona animacja na obiekcie znajdującym się nad aktualnym licznikiem. Przy odwróconej interpolacji wspomogłem się stackOverflow, okazało się, że Android nie dostarcza możliwości prostego odwrócenia animacji.
 
Jak wygląda samo tworzenie animacji w androidzie? Ano tworzymy sobie xml-a z parametrami opisującymi naszą animację, np takiego:
 
<set xmlns:android="http://schemas.android.com/apk/res/android"
    android:fillAfter="true"
    android:interpolator="@android:anim/linear_interpolator" >

    <translate
        android:duration="500"
        android:fromYDelta="-50%p"
        android:toYDelta="0%p" />

    <alpha
        android:duration="300"
        android:fromAlpha="0.0"
        android:toAlpha="1.0" />

</set>

 

 
Możemy tutaj wrzucić jeszcze scale i rotate. Czyli możemy poruszać, obracać, powiększać/pomniejszać, pokazywać/ukrywać nasze obiekty. Do tego dodać parametry, np opóźnienie startu, kiedy chcemy mieć animację sekwencyjną, czas trwania, pozycje na ekranie, itd.. To samo możemy uzyskać w kodzie javy w klasie, ale jakoś tak tutaj mi przejrzyściej 😉
 
Jeszcze dwa słówka o tym, jak tego użyć w klasie
 
       Animation animPut = AnimationUtils.loadAnimation(getApplicationContext(),
                R.anim.put_anim);

 

Koniec dwóch słówek 😉
 
Przechodzimy do interpolatora, czyli sposobu w jaki nasza animacja ma się zachowywać. Wpisany do góry w xmlu to linear_interpolator. Klasyczny i nudny, ciągły ruch z tą samą szybkością przez cały czas, o tak to wygląda:
 
Prosty, płynny ruch, według mnie robotyczny, na pewno nie naturalny.
Spróbowałem więc następnego, nazywa się bounce i jak sama nazwa wskazuje, udaje odbijający się element. Hmmm jeśli rzucimy jakąś belkę na stos, to prawdopodobnie się odbije, wybrałem właśnie jego jako najbardziej naturalny, kojarzący się z rzeczywistością. Prezentuję:
Ładniutko, w momencie, w którym zamiast tej jednolitej belki będziemy mieli jakieś png, wydaje mi się, że będzie wyglądał w porządku. Na koniec sprawdziłem jeszcze jednego, nazywa się Overshot. Ja tam nazwał bym go plum, bo kojarzy mi się z wrzuceniem czegoś pływającego do wody 😉 Oceńcie sami:
Ciekawie wygląda, przyjmuje parametr pozwalający określić głębokość na jaką ma zejść 😉 Nie wymieniłem tutaj wszystkich interpolatorów są jeszcze np accelerate, decelerate, accelerate&decelerate.
Uff dobrnęliśmy do końca, chociaż tak naprawdę, możemy sobie stworzyć własny interpolator, i wydaje mi się, że to jeszcze nie wszystkie możliwości, jeśli chodzi o tworzenie animacji w Androidzie 😉 Chyba coś jeszcze na ten temat napiszę.
Ps. Dla osób startujących w konkursie dsp, zauważyłem, że ten konkurs sporo daje jeśli chodzi o tworzenie się społeczności. Jeśli trafiłeś tutaj w ramach konkursu, zapraszam na Slacka, toczą się tam naprawdę ciekawe rozmowy, i na bieżąco można otrzymywać recenzje swoich postów, kolejna motywująca rzecz 😉
Pozdrawiam!

#2 Nie dla metody Copiego-Paste’a, czyli ponowne użycie layoutów w Androidzie

Możliwość ponownego użycia fragmentu kodu, funkcjonalności jest jedną z wytycznych tworzenia dobrego, obiektowego oprogramowania. Znamy klasy, z których możemy dziedziczyć (btw, wiecie jaki jest najlepszy obiektowy sposób zdobycia fortuny? Dziedziczenie :D), znamy interfejsy, przeciążanie itd. Logicznie podobne podejście możemy zastosować tworząc interfejsy użytkownika w androidzie.

Zaczynamy od najmniejszego elementu który chcemy wykorzystać, czyli w przypadku mojej aplikacji, był to ogólny typ: View (przynajmniej na razie, finalnie pewnie zostanie zastąpiony jakimś ImageView). Dla tego typu View, który w aplikacji będzie symbolizował pojedynczą belkę tworzymy odpowiedni styl (styles.xml), u mnie wyglądało to tak:

    <style name=”BricksStyle” parent=”AppTheme”>
        <item name=”android:layout_width”>match_parent</item>
        <item name=”android:layout_height”>wrap_content</item>
        <item name=”android:layout_weight”>1</item>
        <item name=”android:background”>@color/colorAccent</item>
        <item name=”android:layout_margin”>5dp</item>
        <item name=”android:visibility”>invisible</item>
    </style>

Warto korzystać ze stylów nawet kiedy mamy mieć dwa takie same przyciski w aplikacji, dzięki temu zmiana jednego parametru, będzie następowała w jednym miejscu. Ja będę miał naprawdę sporo tych obiektów, dlatego chociażby zmiana parametru visibility w jednym miejscu zamiast stu, pozwala mi podejrzeć aplikację na podglądzie w android studio. Przed zbudowaniem aplikacji też wystarczy zmiana tylko w tym jednym miejscu, żeby zachować jej logikę działania. Gdybym chciał zmienić kolor belek, to znowu tylko jedna linia kodu. A oto jak wykorzystałem ten styl:

<LinearLayout xmlns:android=”http://schemas.android.com/apk/res/android”
    android:id=”@+id/container”
    android:layout_width=”match_parent”
    android:layout_height=”wrap_content”
    android:orientation=”vertical”>

    <View
        android:id=”@+id/brick10V”
        style=”@style/BricksStyle” />

    <View
        android:id=”@+id/brick9V”
        style=”@style/BricksStyle” />

    <View
        android:id=”@+id/brick8V”
        style=”@style/BricksStyle” />
…

Jak widać, zamiast do każdego obiektu View wklepywać to samo, wpisuję tylko jedną linię kodu.

Ok, a jak tego użyć we właściwym activiti layoucie? Korzystamy z dyrektywy include, która może przyjmować parametry takie jakie dostarcza nam właściwy layout. Oto jakie efekty udało mi się osiągnąć:


A oto jak wygląda fragment layoutu:

    <include
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        layout=”@layout/stack_bricks_layout”
        android:layout_above=”@+id/lineTV”
        android:layout_alignParentStart=”true”
        android:layout_marginLeft=”15dp”
        android:layout_marginRight=”15dp”
        android:layout_toStartOf=”@+id/ITV”
        android:layout_below=”@+id/plusLeftBtn”
        android:id=”@+id/includeLeftStack” />

    <include
        android:layout_width=”wrap_content”
        android:layout_height=”wrap_content”
        layout=”@layout/stack_bricks_layout”
        android:layout_above=”@+id/lineTV”
        android:layout_alignParentEnd=”true”
        android:layout_marginLeft=”15dp”
        android:layout_marginRight=”15dp”
        android:layout_toEndOf=”@+id/ITV”
        android:id=”@+id/includeRightStack”

        android:layout_below=”@+id/minusRightBtn” />

…

Jak widać, zawiera ona dwie dyrektywy include, które działają z tymi przyciskami do góry (plus i minus z prawej i lewej strony). I tutaj dochodzimy do sedna problemu. Mamy po dwa takie same identyfikatory na jednym activiti-layoucie.  Tzn np: @+id/brick1V po prawej i po lewej stronie, w jaki sposób przyciski odróżniają, do którego layoutu mają dodawać i odejmować? Działa to w ten sposób, że łączymy się z każdym z tych layoutów dzięki identyfikatorowi dyrektywy include. Znowu będę posiłkował się kodem:
…
    public View[] getBricksStack(View container)
    {
        View bricks1 = container.findViewById(R.id.brick1V);
…
        View bricks10 = container.findViewById(R.id.brick10V);
        return new View[]{null, bricks1, bricks2, bricks3, bricks4, bricks5, bricks6,
                bricks7, bricks8, bricks9, bricks10};
    }


    public void refreshView(int counter, View[] bricksArray)
    {
        for(int i = 1; i<=10; i++)
        {
            if(i<=counter)
                bricksArray[i].setVisibility(View.VISIBLE);
            else
                bricksArray[i].setVisibility(View.INVISIBLE);
        }
    }
…

…
    private View[] bricksLeftArray;
    private View[] bricksRightArray;
…
    StackBricksViewGroup stackBricksViewGroup = new StackBricksViewGroup();
        View stackBricksContainerLeft = findViewById(R.id.includeLeftStack);
        View stackBricksContainerRight = findViewById(R.id.includeRightStack);
        bricksLeftArray = stackBricksViewGroup.getBricksStack(stackBricksContainerLeft);
        bricksRightArray = stackBricksViewGroup.getBricksStack(stackBricksContainerRight);

Zastosowanie tego typu podejścia znacznie ogranicza potrzeby kopiowania kodu, możemy stworzyć wielopoziomowy layout i wykorzystywać go w naprawdę fajny sposób, dzięki czemu zmian będziemy dokonywali zawsze tylko w jednym miejscu.

Na ten moment zrezygnowałem z korzystania z fragmentów w aplikacji, w następnym wpisie spróbuję pokazać jak poprawić animację pojawiania się naszego obiektu, jeszcze nigdy tego nie robiłem 😉

PS. Pozwolę sobie na pochwalenie się statystykami od kiedy wystartowałem w tym konkursie 😉

Może w dobie internetu, gdzie zdjęcie z kotem zdobywa milion odsłon to nie dużo, nie mniej dla mnie, to aż 100 osób które interesowało to co mam zaoferowania. Nabrałem ochoty stworzyć post który będzie miał o wiele lepszą jakość, niż zwykła notka. Chcę to robić lepiej, ponieważ ktoś chce to czytać. Dzięki! 😉
Pozdrawiam!

#1 Pierwszy problem, jak zrozumieć liczby

Czym jest liczba? Korzystamy z nich na co dzień, ponieważ kiedyś nauczyliśmy się jak to robić, ale wyobraźmy sobie, że mamy przed sobą młody umysł. Umysł w którym ciężko się do czegoś odnieść, ponieważ nie ma zbyt wielu doświadczeń. W jaki sposób wytłumaczyć, czym jest liczba?

Po głębszym przemyśleniu chyba najlepiej będzie połączyć liczbę z ilością. Coś, co dziecko potrafi znaleźć w realnym świecie, jak 2 sztuki jabłka, czy 5 orzechów. W tej wersji spróbujmy po ilości „belek”, co zresztą sugeruje Montessori. Implementacja nie jest zbyt skomplikowana, dodajemy dwa przyciski, przy czym plus jest znacznie wyraźniejszy, bo chcemy zwrócić na niego uwagę. A do tego licznik ilości naszych belek, aby było maksymalnie 10. Po dojściu do maksymalnej liczby odblokuje się następne zadanie, jeszcze nie do końca wiem jak to będzie wyglądać 😉

Następnym krokiem będzie jakaś możliwość porównania ilości i dodania bądź odjęcia z każdej strony, aby nasza waga pozostała w równowadze. Mam nadzieję że będzie to zrozumiałe dla młodych osób 😉

Plany na najbliższym czas:
-dodanie fragmentów
-poprawienie wyglądu belek
-dodanie animacji dodawania poprzez zsuwanie z góry, odejmowania
-dodanie animacji plus/minus na dole
-następna aktywność: porównywanie

Projekt na github.

Pozdrawiam!

No to zaczynamy, daj się poznać!

Jakiś czas temu dotarła do mnie informacja, o bardzo ciekawym konkursie, który wymyślił Maciej Aniserowicz. W największym skrócie, zobowiązujemy się, że będziemy pisali dwa posty tygodniowo, oraz rozwijali projekt open source publikując kod na Github. Zobowiązujemy się do tego przez okres 10 tygodni, czyli musi się pojawić minimum 20 postów projektowych. Mówiąc szczerze, bardzo duże wyzwanie, do tej pory nie raz nie pojawiały się nawet 2 wpisy miesięcznie 😉 Cóż będzie trudno, ale skoro jest trudno tzn, że kierunek powinien być prawidłowy 😉

Projekt który w ramach konkursu będę rozwijał został również zgłoszony na inny konkurs (combo;) Projekt nazywa się E-lementarz i bierze udział również w x-prize. Jest to konkurs ogólnoświatowy, chodzi o stworzenie aplikacji która pozwoli się uczyć dzieciakom, które nigdy nie miały styczności z technologią, może nawet z klasyczną edukacją.. Projekt wygląda trochę jak eksperyment „computer in the wall”, po zakończeniu przyjmowania zgłoszeń, komisja wybierze najlepsze aplikacje które zostaną preinstalowane na tabletach, które trafią do dzieciaków. Za jakiś czas nastąpi porównanie wyników i wtedy będzie jasne, czy projekt zakończył się sukcesem, czy nie 😉 Nie jestem jedyną osobą biorącą udział w projekcie, ale na czas trwania, pisanie spada na mnie, a że postanowiliśmy po raz kolejny zacząć od początku, mam w pełni wolną rękę 😉 Przy okazji, na 198 zgłoszonych drużyn, jesteśmy jedyni z Polski.

Trochę może o metodyce, zagadnienie jest trudne, ponieważ osoby do których trafią urządzenia, mogły nie mieć nigdy wcześniej styczności z edukacją. Postanowiliśmy, że będziemy się trzymali założeń pedagogiki opracowanych przez M. Montessori. Metoda polega na tym, że wykorzystuje dziecięcą ciekawość do nauki konkretnych umiejętności. Na ile to podejście okaże się skuteczne, nie mamy pojęcia, ale postanowiliśmy podążać tą drogą 😉

Tyle tytułem wstępu, wkrótce pojawią się kolejne posty, będę starał się omawiać pomysł na daną funkcjonalność, implementację i dlaczego wygląda to właśnie w ten sposób 😉 Mam nadzieję, że będzie ciekawie, złożoność zagadnienia nie tylko od strony implementacyjnej jest ogromna 😉

Projekt na Github.

Pozdrawiam!