Prelekcja Android View Morphing na Toast Wrocław

To już! Prelekcja do której przygotowywałem się od kilku tygodni odbędzie się już wkrótce!

Kiedy?
07.07.2016, czwartek 18:30-20:30

Gdzie?
PROZA
Przejście Garncarskie 2, 50-150 Wrocław

Bardzo serdecznie wszystkich zapraszam!
Prelekcja odbędzie się w ramach wydarzenia Toast którego organizatorami są Droids On Roids.

Temat?
Android View Morphing

Czyli będę opowiadał o tym, jak z dowolnych grafik w Androidzie stworzyć sobie np taką animację:
albo np taką:

Będzie zdecydowanie praktycznie i mam nadzieję, że temat przedstawię lekko i zrozumiale 😉
Jako zajawkę wrzucam tutaj swoje intro:

Wszystkie informacje znajdują się tutaj, gdzie klikamy też przycisk „Wybieram się”. Po za moją odbędą się jeszcze dwie prelekcje: Waldemara Zubika oraz Karola Wrótniaka. Dodatkowo organizatorzy zapowiadają niespodzianki. Będzie super, jeszcze raz serdecznie zapraszam! 😉

Pozdrawiam!

#17 Grywalizacja, czyli dodanie kolejnej logiki to zadań

Kolejnym krokiem w rozwoju naszej aplikacji jest napisanie następnej logiki 😉 Ta jednak będzie nieco bardziej będzie opierała się o zasady grywalizacji.

Przypomnę dla porządku czym jest grywalizacja, chodzi o zastosowanie zdobywania punktów, odznak, poziomów w (naszym przypadku) procesie uczenia. Pomysł jest taki, aby użytkownicy mogli sobie wybrać zestaw zadań do rozwiązania na 3 poziomach. Najwyższy poziom daje najmniej czasu na odpowiedź, oraz nie pozwala na pomyłki. Wyższe pozwalają na dwie lub jedną pomyłkę. Pokażę może teraz jak to działa:

Jak widać ekran po wykonaniu zadania nie jest jeszcze zaprojektowany. To tam będzie informacja, czy użytkownik dostał daną odznakę, czy mu się nie udało. Na ekranie widzimy wyświetlone zadanie, oraz pomniejszającą się ikonkę zegarka (taki na ten moment miałem pomysł na pokazanie uciekającego czasu 😉 oraz liczbę punktów. Brakuje jeszcze sporo rzeczy (między innymi nie wyświetlam jeszcze opisu zadań, czyli co trzeba zrobić i w jakim czasie na danym poziomie).
Parę słów o implementacji, po pierwsze, niech żyją interfejsy! Doszedłem do wniosku, że jedyną informacją jaką powinienem przekazywać jest informacja, czy użytkownik dał dobrą czy złą odpowiedź, oczekując w odpowiedzi czy w związku z tym uruchomić następną aktywność, czy niech odpowiada dalej. Stworzyłem więc sobie do tego interfejs:
public interface Operation {
    boolean nextPoint(boolean b);
}

Następnie stworzyłem klasę która dziedziczyła z klasy głównej i którą będą rozszerzały wszystkie zadania. Klasa ta zawiera np taki fragment:

Intent intent = getIntent();
setPractice(intent.getBooleanExtra(Const.PRACTICE, false));
setTest(intent.getBooleanExtra(Const.TEST, false));
if (isPractice())
    operation = new CounterOperation(this);
else if (isTest())
    operation = new CounterOperation(this, getIntent());
else
    operation = new StarsOperation(this);

Jak widzimy nadklasa decyduje której implementacji użyć, W klasie właściwej używam tylko:

@Override
@OnClick(R.id.successView)
public void onClickSuccessView() {
    if(operation.nextPoint(true))
        nextActivity(true, ChoiceCheckActivity.this);
    else {
        onClickCommon();
        unFillScreen(true, true);
    }
}

@Override
@OnClick(R.id.failureView)
public void onClickFailureView() {
    if(operation.nextPoint(false))
        nextActivity(false, ChoiceCheckActivity.this);
    else {
        onClickCommon();
        unFillScreen(false, true);
    }
}

Sprawdzam co zwraca implementacja i decyduję, czy startuję z aktywnością, czy czyszczę ekran i użytkownik ma następną szansę. Ta nadklasa decyduje również o wystartowaniu animacji (konstruktor przyjmujący obiekt Intent ją implementuje). Ma też metody start i stop time:

public void startTime() {
    if(isTest()) {
        filledScreen= false;
        counterOperation.startTime().setAnimationListener
                 (new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {  }
            @Override
            public void onAnimationEnd(Animation animation) {
                if(!filledScreen)
                    fillScreen(false, true);
            }
            @Override
            public void onAnimationRepeat(Animation animation) {  }
        });
    }
}

public void stopTime() {
    if(isTest())
        counterOperation.stopTime();
    filledScreen = true;
}

Niestety listener obiektu Animation wymusza na mnie implementację 3 metod, chociaż korzystam tylko z jednej.

To by było chyba na tyle jeśli chodzi o postępy w projekcie. Wkrótce następne wpisy, stay tuned! 😉

Pozdrawiam!

#12 Animacja zmiany aktywności

W poprzednich częściach tej serii zatrzymaliśmy się na tym, że jeden kolor wypełnia cały ekran. Dzisiaj pójdziemy krok dalej, czyli opiszę jak zrobić płynne przejście pomiędzy jedną, a drugą aktywnością.

Oto kroki które chcemy wykonać po wypełnieniu ekranu kolorem zieleni lub czerwieni:

1) płynna zmiana koloru na biały
2) jednoczesna animacja obiektu, zamiana symbolu „wykonane” na strzałkę
3) jednoczesna zmiana przycisku fab
4) kiedy już kolor zmieni się na biały usunięcie animacji przejścia pomiędzy aktywnościami
5) start nowej aktywności z animacją pojawiania się

Metoda, które wykonuje pierwsze cztery operacje wygląda tak:

MorphingAnimation morphingAnimationView = new 
    MorphingAnimation(getApplicationContext(), successV);
morphingAnimationView.animImageView(R.drawable.ic_done_to_line);
animateFab(R.drawable.ic_done_to_line, false);
ValueAnimator colorAnim = ObjectAnimator.ofInt(
    layoutSuccess, "backgroundColor",
    getResources().getColor(R.color.colorSuccessGreen), 
 getResources().getColor(R.color.icons));
colorAnim.setDuration(500);
colorAnim.setEvaluator(new ArgbEvaluator());
colorAnim.start();
colorAnim.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
        super.onAnimationEnd(animation);
        Intent intent = new Intent(NumbersOrderActivity.this, 
      NumbersCompareActivity.class);
        startActivity(intent);
        overridePendingTransition(0, 0);
    }
});

Nowością która się tutaj pojawia jest klasa ValueAnimator. Klasa akurat w tym przypadku pozwala na płynną zmianę kolorów obiektu, z zielonego na biały. Na klasie jest założony listener, który po zakończeniu animacji uruchamia nową aktywność, z kolei metoda overridePendingTransition(0, 0) powoduje usunięcie domyślnej animacji otwarcia nowego activiti. Chciałem umieścić animację rozjaśniania w tym miejscu, ale zadziałało by to tak, że rozjaśniał by wszystko łącznie z paskiem i przyciskiem fab, a tego nie chciałem, celem było aby zmieniała się tylko zawartość ekranu. Przyjmowane przez metodę parametry to zero i zero co oznacza usunięcie animacji, pierwszy z tych parametrów, to animacja włączenia nowego activiti, drugi to wyłączenie starego. Jest też tutaj pewna asynchroniczność, mianowicie czas zmiany kolou to 500ms, z kolei animacja zmiany kształtu trwa 200ms, możliwe, że jeszcze w jakiś sposób to zmienię, ale na ten moment wygląda chyba nieźle.

Ostatnią istotną rzeczą jest kod, którego użyłem w nowej aktywności, wykonuje on punkt 5, czyli odpowiada za animację rozjaśnienia i jest uruchamiany podczas tworzenia activity, dzięki temu mamy animację już od samego początku. Po stronie javy prezentuje się tak:

Animation animFadeIn = AnimationUtils.loadAnimation(
    getApplicationContext(), R.anim.fade_in);
numbersCompareActivity.startAnimation(animFadeIn);

Po stronie xml w ten sposób:

<alpha
    android:duration="@integer/anim_duration"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

Wydaje mi się, że jest samo-opisywalny i nie za wiele mogę tutaj powiedzieć 😉 A tak wygląda efekt finalny który udało mi się uzyskać:

Dodaliśmy kolejną funkcję, więc czas na rozszerzenie klasy bazowej, aby nie pisać ciągle tego samego. Ale jak bym to widział, opisze już następnym razem 😉
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!

#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!

#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!

Jak zrobić piękną aplikację

Na pewno każdy już zauważył następującą tendencję, że im aplikacja jest ładniejsza, tym ma więcej pobrań. Content jest oczywiście bardzo ważny, ale o wiele lepiej nam korzystać z czegoś co jest przyciąga wzrok, nie jednokrotnie godząc się nawet z częściowym ukryciem potrzebnej funkcjonalności pod jakąś piękną animacją. Czasem jednak coś jest piękne, ale bezużyteczne. Ważne, aby znaleźć złoty środek pomiędzy tymi dwoma wartościami. Ale nie o tym chciałem pisać. Pytanie które chciałem dzisiaj zadać, to jak ważny jest design dla programisty Android? Odpowiedź: coraz ważniejszy. Nie każdy ma jednak talent plastyczny, aby stworzyć piękną grafikę. Ja osobiście, mam taki problem, często wychodzą mi jakieś niedopasowane kolorami i kształtami szkarady. Jednak jest na to sposób, gotowce. Użyłem jednego w swojej poprzedniej aplikacji, dlatego chciałem dzisiaj przedstawić kilka przykładów, oraz podać miejsca gdzie szukać inspiracji.
Zaczynamy od animacji której sam osobiście użyłem ostatnio, czyli:

Ten twórca stworzył jeszcze jedną, bardzo podobną:

Prawda, że są ładne? Znalazłem je na stronie android-arsenal. Godnym polecenia miejscem, gdzie możemy szukać jest również strona dribbble. O to dwa piękne cuda które tam znalazłem:

Implementacja tego typu rozwiązań jest najczęściej bardzo prosta. I stosunkowo niewielkim kosztem można naprawdę bardzo polepszyć wygląd swojej aplikacji. Przy czym jak pisałem we wstępie, nie należy zapominać o funkcjonalności 😉
Pozdrawiam!

Kontrola biletów Wrocław

Mieszkańcy Wrocławia! Tak właściwie, to dla Was jest skierowany ten wpis. Chciałbym mianowicie podzielić się swoim ostatnim dziełem jakim jest aplikacja „Kontrola biletów Wrocław”. Aplikacja nie jest szczególnie rozbudowana, ale wydaje mi się, że przydatna. Pobiera ze strony wroclaw.pl dane o aktualnych kontrolach MPK i prezentuje je w ładnej formie. Myślę, że każdemu taka informacja może się przydać 😉 Oto jak działa:
A tutaj link skąd można ją pobrać
Mam nadzieję, że się przyda, ja na pewno będę z niej korzystał 😉
Pozdrawiam!