Kotlin tutorial #3 – loops

Pętle w programowaniu umożliwiają powtarzanie niektórych czynności. Ale nie tylko proste powtarzanie, bo to było bezsensu na dłuższą metę, ale zmianę parametrów, albo ich pobieranie, przetwarzanie według jakichś geometrycznych, lub logicznych zasad. Java posiada 3 główne (może 4, w zależności od tego jaką konwencję nazewnictwa przyjmiemy) rodzaje pętli. Kotlin jednak jak ma to w zwyczaju daje nam dużo więcej możliwości, oraz – elastyczności.

Na początek prosta pętla for, czyli iterująca liczby w zakresie od 0 do 10. W javie:

        for (int i = 0; i < 10; i++) {
            System.out.print("for1 = " + i + ", ");
        }

W Kotlinie:

    for (i in 0..10) {
        print(i)
    }

lub:

    repeat(10) {
        print("repeat10")
    }

No dobra, repeat nie jest pętlą, tylko dodaną do biblioteki standardowej metodą, ale czyż nie jest czytelniejszy? Java wymaga zdefiniowania wartości zmiennej początkowej, warunku, oraz sposobu iteracji. Część z tych elementów może oczywiście znajdować się po za pętlą for, ale nie zawsze potrzebujemy ich wszystkich. Czasem chcemy coś po prostu.. powtórzyć. 😉

Kolejnym typem pętli for jest tzw forEach, który pozwala na iterację bez użycia dodatkowej zmiennej iterującej. W javie przedstawia się tak:

        private static String[] t = {"abc", "cde", "efg"};
        for (String s : t) {
            System.out.print("for2 = " + s + ", ");
        }

Z kolei w Kotlinie:

    val list = listOf("A", "B", "C")
    for(str in list) {
        print(str)
    }

Użyłem przykładu z listą, ponieważ Kotlin nie obsługuje typów prostych, takich jak javowa tablica[].

A co jeżeli chcemy iterować po mapie? Potrzebujemy czegoś na kształt: „for (Map.Entry<String, Object> entry : map.entrySet())”. Z kolei w Kotlinie:

    val map = hashMapOf(1 to "one", 2 to "two", 3 to "three")
    for ((key, value) in map) {
        println("Map[$key] = $value")
    }

Prawda, że przyjemnie? Jest jeszcze coś fajnego, mianowicie pętle foreach możemy wywołać jak metodę, albo funkcję, bezpośrednio na obiekcie na którym operujemy. Tak to wygląda:

    map.forEach {
        print(it.key)
    }
    println()
    (1..5).forEach(::print)
    println()

Słówko kluczowe it odnosi się do bieżącego obiektu. Potrzebowaliśmy np klucza – żaden problem.

Pozostałe dwa typy pętli, które są praktycznie takie same w obydwóch językach nazywają się while i do while. Stosujemy je najczęściej wtedy, kiedy nie znamy dokładnej ilości iteracji, ale istnieje jakiś warunek który zakończy nam działanie pętli. Poniższy przykład jest taki jak w pętli for, ale nie chciałem specjalnie utrudniać, tworząc dziwne warunki, chodzi mi o sam „kształt” pętli.

        //java
        int i = 0;
        while (i < 10) {
            System.out.print("while = " + i + ", ");
            i++;
        }
        System.out.println();
        do {
            System.out.println("do while");
        } while (1 > 10);

    //kotlin
    var i = 0
    while (i < 10) {
        print("while = $i, ")
        i++
    }
    println()
    do {
        println("do while")
    } while (1 > 10)

To by było na tyle, jeśli chodzi o informacje o pętlach. Temat jeszcze powróci, kiedy będziemy omawiali lambdy 😉 Kod w aktualnej wersji znajduje się na GitHubie. Wszystkie aktualne zmiany oczywiście w pakiecie loops. W następnej części opowiem o instrukcjach sterujących w obydwóch językach.

Pozdrawiam!

— status update —

Jak widać, minęło sporo czasu, a kontynuacja kursu Kotlina się nie pojawiła. Pojawi się tutaj: https://kurskotlin.pl/ tak więc tam zapraszam #kurskotlin

Kotlin tutorial #2 – variables

Podstawą chyba każdego języka programowania są zmienne, w których można przechować wartość, którą można modyfikować. Są też stałe, kiedy nie chcemy, żeby przypadkiem coś zostało zmienione (np liczba PI, to wartość stała, nadpisywanie jej zawartości np liczbą 5 i nazywanie PI nie ma logicznych podstaw i będzie prowadzić do błędów). Java i Kotlin mają do tego aspektu podobne podejścia. Są to języki silnie, statycznie typowane, tzn jeżeli nadamy jakiś typ zmiennej, to nie da się go wprost porównać z innym typem, jeżeli np napis ma wartość 1 i zmienna typu liczbowego ma wartość 1, to one nie są sobie równe (szczegóły poniżej). Słowo statycznie oznacza z kolei, że zmienne przyjmują swoje typy w czasie kompilacji programu, a nie w czasie jego działania, jak ma to miejsce np w JavaScripcie. Kotlin posiada tutaj jednak ułatwienie, które nazywa się typowaniem zmiennych.

W praktyce oznacza to tyle, że mamy kod w javie:

        byte b = 127;
        short s = 32767;
        int i = 2147483647;
        final byte fb = 127;
        final short fs = 32767;
        final int fi = 2147483647;

Słówko final oznacza stałą, czyli po przypisaniu wartości nie można już jej zmienić. Zaprezentowane wartości to maksimum, jakie może przyjąć dana zmienna, po przekroczeniu jej zacznie się odliczanie od wartości ujemnych, coś na kształt licznika w samochodzie, tylko że tam nie ma ujemnych, tylko zero 😉 A jak to wygląda w Kotlinie?

    var c = 120
    var d: Byte = 120
    val e = 120
    //val f: String = 120

Słówko var (variables) oznacza zmienną, z kolei val (values) stałą. Druga sprawa jest taka, że na podstawie przypisanej wartości zostaje nadany odpowiedni typ zmiennej. Innymi słowy w tym przypadku domyślny będzie Int, chyba, że po dwukropku wymusimy inny typ. Kod w komentarzu się nie skompiluje, ponieważ do wprowadzania napisów (zmienna typu String) używa się dwóch znaków cudzysłowu: „120”.

W tym miejscu należy też wspomnieć o zmiennej Object w javie, po której dziedziczą wszystkie typy. Nie ma ona zbyt wielu metod, ale posiada equals – zawiera. Używamy jej do porównania zawartości obiektów.

        boolean bool = true;
        char c = 'A';
        final char e = 'E';
        String str = "Hello";
        Object ob = 1;
        int in = 1;

        for(int y = 0; y<10; y++) {
            System.out.print(c++ + " ");
            // System.out.print(e++ + " ");
            // System.out.print(ob++ + " ");
        }
        System.out.println();
        if(ob.equals(in)) {
            System.out.println(bool);
        }

Funkcja ta zwróci następujące wartości:

A B C D E F G H I J 
true

Dzieje się tak ponieważ zmienna typu char jest typem liczbowym, dzięki temu możemy zwiększając jej wartość wydrukować kolejną literę. Nie uda się to w przypadku e i ob, ponieważ jedna z nich jest stałą, druga z kolei jest typem Object, gdzie nie wiadomo jak obsługiwać iterację. Z kolei w Kotlinie:

    var c = 120
    var d: Byte = 120
    val e = 120
    //val f: String = 120
    for (i in 0..9) {
        //println(e++)
        println(c++)
    }
    println()
    for (i in 0..9) {
        println(d++)
    }
    var f = 'A'
    var g = "A"
    for (i in 0..9) {
        println(f++)
        //println(g++)
    }

Otrzymamy taką odpowiedź:

120
121
122
123
124
125
126
127
128
129

120
121
122
123
124
125
126
127
-128
-127
A
B
C
D
E
F
G
H
I
J

Co tu się stało? Po pierwsze iterujemy po c, który domyślnie został zapisany jako int, z racji wysokiego zakresu udaje nam się to bezbłędnie. Po e nie możemy iterować, ponieważ jest stała. Ale za to w zmiennej d wymuszamy typ byte, który może przyjąć wartość max 127, a następnie zaczyna iterację od najniższej wartości. Iterowanie typu char jest takie samo jak w Javie, z kolei Stringa nie można iterować, tutaj również bez niespodzianek. Słówko kluczowe equals występuje również w Kotlinie, działa tak samo jak w javie, innymi słowy porównuje wartości pomiędzy takimi samymi typami. Pojawił się też nowy operator === oznaczający, że oba obiekty mają taką samą referencję (czyli odnoszą się do tego samego obiektu). Wygląda to tak:

    var x = 1
    var y: String = "1"
    var z = x
//    if(x==y)
//        println(true)
    if(x.equals(y))
        println("x.equals(y)")
//    if(x===y)
//        println(true)
    if(x===z)
        println(true)
    if(x.equals(y.toInt()))
        println("x.equals(y.toInt())")

Co z kolei zwraca:

true
x.equals(y.toInt())

Czyli z i x mają tą samą referencję, oraz x ma taką samą wartość jak y, jeśli przekształcimy go na Int.

W podsumowaniu chciałem jeszcze zwrócić uwagę na wielkość liter. Typ zmiennej pisany z małej litery to prymityw, Kotlin już takich nie obsługuje i tam piszemy wszystko z dużej. Prymityw znaczy tyle, że nie jest obiektem, tylko typem prostym mogącym przechowywać jakąś wartość liczbową (char jest prymitywem, ponieważ przechowuje wartości liczbowe). Obiekty z kolei mają dodatkowe właściwości, mogą mieć np wartość null. Kotlin jednakże zabezpiecza nas przed taką ewentualnością.

W związku z powyższym Java posiada mechanizm automatycznego opakowywania prymitywów. Tzn, jeżeli porównujemy obiekt z prymitywem int, to automatycznie zostanie on skonwertowany do obiektu Integer o tej samej wartości. W Kotlinie z racji braku obsługi prymitywów takie operacje nie są potrzebne. To by było tyle na dzisiaj, jak widać mechanizm typowania zmiennych pozwala zaoszczędzić zbędnego kodu. Wszystkie użyte dzisiaj przykłady znajdują się też na GitHubie, zachęcam do samodzielnego pobawienia się 😉

Pozdrawiam!

— status update —

Jak widać, minęło sporo czasu, a kontynuacja kursu Kotlina się nie pojawiła. Pojawi się tutaj: https://kurskotlin.pl/ tak więc tam zapraszam #kurskotlin

Null budget exception – android app

Ostatnio napisałem o tym, że najważniejszą umiejętnością jaką możemy posiąść jest wiedza o tym, jak rozdzielać zadania pomiędzy tym co wykonujemy sami, a tym co powinniśmy oddelegować. Stworzyłem także taką małą aplikację do kształtowania nowych nawyków. Jeśli chodzi o nią, to nie wydałem podczas jej powstawania ani złotówki. Dzisiaj chciałem opisać jak to zrobiłem, aby jedynym moim kosztem był czas. I to najlepiej nie za dużo czasu 😉

Po pierwsze zewnętrzne biblioteki. Kiedy zerkniecie na aplikację, na starcie jest dostępny naprawdę ładny kalendarz. Nie jestem jego autorem, skorzystałem z gotowego komponentu dostępnego tutaj. A tak wygląda zrzut z mojego pliku gradle:

    compile 'com.jakewharton:butterknife:7.0.1′
    compile 'com.prolificinteractive:material-calendarview:1.4.0′
    compile 'petrov.kristiyan.colorpicker:colorpicker-library:1.1.0′
    compile 'com.github.javiersantos:MaterialStyledDialogs:1.4.1′
    compile 'com.github.PhilJay:MPAndroidChart:v2.2.5′

Wszystkie biblioteki których użyłem znalazłem na stronie android-arsenal. Jak już znalazłem wszystko co oszczędzało mi pracę, to po prostu przeczytałem trochę dokumentacji i mogłem korzystać. Bardzo ładnie zintegrowały się z Android Studio, i zostało mi o wiele mniej linii kodu do napisania 😉

Ale aplikacja, to nie tylko kod. Po za tym trzeba by jeszcze dorobić jakąś grafikę. Część grafik była we wcześniej pokazanych bibliotekach. Do ikon na toolbarze użyłem tych dostępnych na stronach design google, z kolei ikona aplikacji pochodziła ze strony flat-icon konkretnie stąd. Tutaj wymogiem było umieszczenie w about informacji o autorze, co też zrobiłem 😉 A z pozostałymi rzeczami graficznymi, np tymi potrzebnymi do sklepu Play poradziłem sobie sam, edytując choćby w paincie te elementy.

Ok, to mamy kodzik, mamy jakąś grafikę. Pozostał filmik promocyjny, który zawsze jest mile widziany. To również udało mi się zrobić, użyłem aplikacji shotcut. Ale skąd wziąłem elementy do tego filmiku? Elementy własnej aplikacji, które tam pokazuję nagrałem sobie sam 😉 Z kolei całe tło składające się z tych różnych biegaczy pobrałem ze stron videezy, oraz videvo, które udostępniają takie fragmenty za darmo 😉 Potem oczywiście odpowiedni montaż, podłączenie muzyki (Dave Depper „Motivational Beat” – „Compositions 2” ze strony freemusicarchive, oczywiście za darmo). I efekt końcowy jest taki:

I jak, wyszło w porządku, czy jednak czuć, że profesjonalista tego nie montował? 😉

Reasumując tak wyglądał mój przepis na w porządku wyglądającą aplikację, która kosztuje trochę czasu, ale za to ani złotówki 😉 Wydaje mi się, że jest on całkiem uniwersalny, także gdyby ktoś chciał tak samo jak ja, to polecam 😉 Można też oczywiście oddelegować część zadań, ważne, żeby ten wybór był świadomy 😉

Pozdrawiam!

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!

#20 Finał zmagań w konkursie DSP – podsumowanie

Noo i stało się. Jest to ostatni wpis w konkursie „Daj się poznać”. Jak pisałem, wcześniej, projekt nie został ukończony, ale nie to było celem. Jest stabilny, aby chwilę się pobawić i ma solidną bazę, aby móc dodawać kolejne zadania.

Ostatnimi rzeczami, które poprawiałem w tej wersji jest wymuszenie poziomej orientacji, poprzez dodanie w manifeście parametrów:

android:screenOrientation="landscape"
android:configChanges="keyboardHidden|orientation|screenSize"

Drobne poprawki w wyglądzie, np w tym miejscu:


I jakieś szybkie poprawki w kodzie, tak jak mówiłem, stabilizacja.

Skompilowany plik APK można pobrać z tego linku.
Z kolei kod źródłowy w wersji dotyczącej tego wpisu znajduje się tutaj.

W tym wpisie chciałem trochę podsumować, co dał mi udział w konkursie DSP. Na początek może tabelka z wszystkimi wpisami:

Trochę postów się tutaj pojawiło 😉 W związku z tym czuję, że chyba z czasem poprawiałem ich jakość, czyli rozwinąłem się jako bloger. Do tego udało się całkiem sporo zrobić w projekcie, przygotować API pod nowe zadanka. Poprawiłem trochę swoje umiejętności kodowania, zacząłem używać choćby butterknife, albo poprawiać grafiki przy pomocy 9-path. Założyłem snapchata w ramach projektu snapchat for developers (szukaj migellal jbc 😉 Ale najlepsze było community wokół konkursu, gorące rozmowy na Slacku, na każdy temat. Szczerze mówiąc nigdy bym nie pomyślał, że przeprowadzę tyle ciekawych rozmów z osobami ze środowisk .NET, czy Ruby 😉 Ani w ogóle, że poznam tyle fajnych osób z tego środowiska, wszystkim którzy się tam udzielali chciałbym bardzo podziękować, fajnie, że byliście, mam nadzieję, że te kanały na Slacku nie umrą po gali finałowej 😉
W ostatnim akapicie chciałem podziękować Maciejowi za zorganizowanie tego konkursu, oraz Wam, czytelnikom, że tak chętnie tu zaglądaliście 😉 To był naprawdę świetny pomysł, nauczyłem się i zrozumiałem sporo nowych rzeczy! 😉 A co do przyszłości bloga, cóż… Wkrótce wpis na ten temat 😉 Mam nadzieję, że zostaniecie ze mną 😉
Pozdrawiam!

#19 Szybkie rozszerzanie funkcjonalności

Zbliżamy się do końca konkursu DSP, to już przedostatni wpis, noo i trzeba pomyśleć o jakiejś stabilizacji. Aplikacja nie będzie ukończona do dwudziestego wpisu, ale w jakiś sposób musi działać. Finalnie pewnie będzie ukończona we wrześniu, nie będę nad nią działał sam 😉 Dzisiaj dodałem kod odpowiedzialny za uruchamianie zadań praktycznych, testów i customowych prób. Kod dla funkcjonalności porównywania i dodawania. Najwięcej czasu zajęły poprawki w wyglądzie i obsługa nowych przycisków, reszta poszła błyskawicznie, dzięki zaimplementowaniu wielu rzeczy w nadklasie. Szybki rzut oka na aplikację:

Więcej funkcji się już niestety nie pojawi, następny wpis będzie o drobnych poprawkach w wyglądzie, etc. Ale skupmy się na tym co dodałem, czyli np funkcji porównywania z opcją ustawienia sobie czasu i ilości odpowiedzi:

Kodzik głównie dziedziczy, te funkcjonalności się powtarzają, więc w sumie czemu nie 😉 Spójrzmy np na to:

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

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

@Override
@OnClick(R.id.fab)
public void onClickFab() {
    if (!isScreenFiled()) {
        stopTime();
        fillScreen(counterRight == counterLeft, true);
    } else {
        if (counterRight == counterLeft)
            onClickSuccessView();
        else
            onClickFailureView();
    }
}

@Override
public void onClickCommon() {
    stackBricksElementsOperation.hideStack(bricksLeftArray);
    stackBricksElementsOperation.hideStack(bricksRightArray);
    unFillScreen(counterRight == counterLeft, true);
    createReadyView();
}

Jak już pisałem wcześniej, mamy tutaj interfejs operation który decyduje którą implementację wybrać. Metoda nextPoint odpowiada true, jeśli mamy wystartować następną aktywność, lub false jeśli wyczyścić ekran i wykonać inne operacje, które mamy opisane w jednej metodzie: onClickCommon. Ważne aby wyczyścić ekran, zanim wylosujemy następne numery. Animacja się rozpocznie w osobnym wątku, i wszystko zdąży się wykonać, zanim zauważymy zmiany. To bardzo fajna cecha, nie trzeba o tym myśleć i przydatna w większości przypadków. Ale niestety nie we wszystkich, np trzeba pamiętać, aby wyłączać parametr clickable na elementach, kiedy uruchamiamy jakąś długo trwającą animację i nic ma jej nie przerywać.

Jak widać czasem przygotowanie sobie dobrego API, i wydzielenie kodu który się powtarza niejednokrotnie trwa dłużej, niż faktyczne korzystanie z niego. I wydaje mi się, że to dobra droga, w przypadku rozrastania się programu, o wiele łatwiej można dodawać nowe funkcje.

Projekt na Github.

Pozdrawiam!

#18 Tworzenie własnych zadań

Poprzednio pokazałem jak osiągnąłem aktywności obsługujące testy, a dzisiaj dzięki kilku zmianom w kodzie udało się zrobić tak, aby użytkownik mógł sam wyznaczyć sobie czas, ilość odpowiedzi i maksymalną ilość pomyłek. Było to naprawdę proste, a spójrzmy jak fajnie działa:

A teraz wszystkie zmiany, które poczyniłem w kodzie wyglądają tak:
1) w klasie MenuActivity obsługa następnego activity -> Custom

2) stworzenie CustomActivity w którym wpisujemy ile ma być zadań etc..
Intent intent = new Intent(NumbersCustomActivity.this, ChoiceCheckActivity.class);
intent.putExtra(Const.CUSTOM, true);
intent.putExtra(Const.CUSTOM_TIME,
     Integer.parseInt(timeAmount.getText().toString()));
intent.putExtra(Const.CUSTOM_GOOD_ANSWER,
    Integer.parseInt(goodAnswerAmount.getText().toString()));
intent.putExtra(Const.CUSTOM_BAD_ANSWER,
    Integer.parseInt(badAnswerAmount.getText().toString()));
startActivity(intent);

A także przekazujemy dane które zostały wpisane w pola. Od razu parsujemy na inty, żeby nie było problemu 😉 Formatki EditText po stronie xml są tak sformatowane, żeby przyjmować tylko cyfry 😉

Edycji w istniejącym kodzie też nie było zbyt wiele 😉

3) zmieniła się klasa CounterOperation, metoda getData()

private void getData(Intent intent) {
    boolean test = intent.getBooleanExtra(Const.TEST, false);
    if(test) {
        int c = intent.getIntExtra(Const.CLASS, 0);
        if (c == 1) {
            time = Const.I_CLASS_TIME;
            goodAnswer = Const.I_CLASS_GOOD_ANSWER;
            badAnswer = Const.I_CLASS_BAD_ANSWER;
        } else if (c == 2) {
            time = Const.II_CLASS_TIME;
            goodAnswer = Const.II_CLASS_GOOD_ANSWER;
            badAnswer = Const.II_CLASS_BAD_ANSWER;
        } else {
            time = Const.III_CLASS_TIME;
            goodAnswer = Const.III_CLASS_GOOD_ANSWER;
            badAnswer = Const.III_CLASS_BAD_ANSWER;
        }
    }
    else {
        time = intent.getIntExtra(Const.CUSTOM_TIME, 0);
        goodAnswer = intent.getIntExtra(Const.CUSTOM_GOOD_ANSWER, 0);
        badAnswer = intent.getIntExtra(Const.CUSTOM_BAD_ANSWER, 0);
    }
}

Dzięki temu obsługuje dane przesłane przez użytkownika (obiekt Intent może być naprawdę bardzo przydatny 😉

4) doszła zmienna boolean o nazwie custom, która ma zastosowanie w tych samych przypadkach co zmienna test, czyli zasady są takie same, ale wartości może podać użytkownik.

W ten oto sposób w kilka chwil doszła kolejna logika do zadań. Także możliwości mamy już całkiem sporo i praktycznie zostało to wszystko osiągnięte dzięki kilku liniom kodu, dzięki wcześniejszemu przygotowaniu sensownych nadklas.

Ten post ma numer 18 w konkursie DSP, cieszę się, że jest tyle osób które przegląda mojego bloga (dzięki! :). Pozostały jeszcze dwa DSP i zastanawiam się, czy pisanie dalej, tzn po zakończeniu konkursu na temat tego projektu z tak dużą intensywnością ma sens? Bo mam w głowie kilka pomysłów na to co może się tutaj dziać, ale o tym za kilka wpisów 😉 Będzie fajnie 🙂

Projekt na github.

Pozdrawiam!

#15 Aktywność wskazania ilości i kolejne poprawki

Ale tytuł posta 😉 Tak naprawdę to tylko tak zawile brzmi sprawa jest naprawdę prosta, mamy narysowany stos (a jakże) i wybieramy ile elementów się na nim znajduje 😉 Noo i oczywiście trzymając się konwencji mamy najpierw wprowadzenie, a następnie 5 prób na punkty. Oto jak to wygląda.

Sam widok tablicy numerycznej można by jeszcze poprawić, ale nie będzie to trudne 😉 Dzięki ostatnim poprawkom dodawanie tego typu zadanek to już naprawdę chwilka, wydaje mi się, że nie wiele więcej da się jeszcze tutaj zrobić. Ale wprowadziłem jeszcze dwie istotne poprawki, mianowicie, kiedy dodałem zadanka i chciałem je umieścić w innym miejscu na tej liście:

To okazało się, że musiałem edytować kilka klas, aby wiedziały jak się prawidłowo przełączać. Dlatego wyciągnąłem to wszystko w jedno miejsce, usunąłem z funkcji jeden argument, tak to wygląda:

Intent intent = new Intent(packageContext,
    getNextActivity(packageContext));
startActivity(intent);

...

private Class getNextActivity(Context context)
{
    if(context.getClass().equals(BricksActivity.class))
        return BricksOrderActivity.class;
    else if(context.getClass().equals(BricksOrderActivity.class))
        return ChoiceActivity.class;
    else if(context.getClass().equals(ChoiceActivity.class))
        return ChoiceCheckActivity.class;
    else if(context.getClass().equals(ChoiceCheckActivity.class))
        return CompareActivity.class;
    else if(context.getClass().equals(CompareActivity.class))
        return CompareCheckActivity.class;
    else if(context.getClass().equals(CompareCheckActivity.class))
        return AddingActivity.class;
    else if(context.getClass().equals(AddingActivity.class))
        return AddingCheckActivity.class;
    else
        return MenuActivity.class;
}

I teraz już kolejne elementy mam w jednym miejscu 😉 Dodanie następnego zadanka nie będzie wymagało edycji w innych aktywnościach.

Kolejną rzeczą którą poprawiłem jest niepowtarzanie się animacji fab przeniesione do samej obsługi animacji, dzięki temu nie musimy o to dbać w każdej aktywności osobno.

private boolean currentShapeIsDifferent(int resId)
{
    if(resId==previousAnim)
        return false;
    else
    {
        int pAnim = previousAnim;
        previousAnim = resId;
        if(resId==R.drawable.ic_line_to_undone&&
      pAnim==R.drawable.ic_done_to_undone)
            return false;
        else if(pAnim==R.drawable.ic_line_to_undone&&
      resId==R.drawable.ic_done_to_undone)
            return false;
        else if(resId==R.drawable.ic_line_to_done&&
      pAnim==R.drawable.ic_undone_to_done)
            return false;
        else if(pAnim==R.drawable.ic_line_to_done&&
      resId==R.drawable.ic_undone_to_done)
            return false;
        else
            return true;
    }
}

Jak widać logika nie jest taka całkiem prosta, ale ogólnie chodzi o to, że jeśli ostatnia figura w animacji jest taka sama, to nie trzeba animować, wizualnie wygląda to dobrze 😉 Przy okazji, Android Studio, czasem podpowiada jak można rozwiązać to w kodzie krócej, opcją simplify. Tym razem też podpowiedział:

return !(resId == R.drawable.ic_line_to_undone
&& pAnim == R.drawable.ic_done_to_undone)
&& !(pAnim == R.drawable.ic_line_to_undone
&& resId == R.drawable.ic_done_to_undone)
&& !(resId == R.drawable.ic_line_to_done
&& pAnim == R.drawable.ic_undone_to_done)
&& !(pAnim == R.drawable.ic_line_to_done
&& resId == R.drawable.ic_undone_to_done);
}

Sam dodałem entery, żeby nie był to jeden ciąg 😛 Simplify, że hej, oczywiście nie zostawiłem w tej wersji 😉

Ps. Aktualna liczba wyświetleń to: 4993, także wkrótce pyknie okrągłe 5000 odsłon bloga 😉 Dzięki wielkie 🙂 A po zakończeniu konkursu blog na pewno nie umrze, mam już kilka pomysłów na niego 😉 Ale o tym za jakiś czas, na razie mam 75% DSP wpisów 🙂

Projekt na github.

Pozdrawiam!

#14 Porządki są dobre – 2 nowe aktywności – porównywanie i dodawanie

Wczoraj pisałem o refactoringu, a dzisiaj udało mi się dodać dwie nowe funkcjonalności, co pozwoliło na to, że projekt trochę bardziej przypomina coś, co kiedyś mogło by działać 😉 Nie są to zupełne nowości, ale takie, które były potrzebne, aby utrzymać logikę, jest nowa aktywność dodawania i porównywania. Ogólnie wygląda to w ten sposób, że najpierw mamy etap prezentacji, w którym jest pokazywane po reakcji fab jak to ma działać, a potem rozwiązujemy zadanka. Na razie tylko 5 prób, pewnie finalnie będzie opcja odpalenia ich również na punkty 😉
To ja może na początek zademonstruję:
Jak widzimy, najpierw mamy pokazane, kiedy jest ok (belki są w równowadze) a po kliknięciu przechodzimy do testów, gdzie kliknięcie sprawdza, czy się nie pomyliliśmy 😉 Mamy gwiazdki informujące o tym, czy wszystko się udało.
Kodziku za bardzo nie mam co pokazywać, ponieważ z powodu refaktoryzacji, jest bardzo podobny do tego co już pokazywałem.
Kolejne dwie funkcjonalności to podanie wyniku dodawania, tutaj również na początku jest pokazane jak to działa, a później sprawdzenie z gwiazdkami 😉
Tutaj jeśli chodzi o kodzik, to mogę pokazać metodę która odpowiada za zmiany FAB:
private void changeFab() {
    if (counter != result && firstChange) {
        morphingAnimation.animFab(
  R.drawable.ic_done_to_undone, R.color.colorFailureRed);
        firstChange = false;
    }
    if (counter == result) {
        morphingAnimation.animFab(
  R.drawable.ic_undone_to_done, R.color.colorSuccessGreen);
        firstChange = true;
    }
}
I jeszcze na koniec, znalazłem dzisiaj niezłego buga w mojej aplikacji, mianowicie kod .setVisibility(View.VISIBLE) / .setVisibility(View.INVISIBLE) nie reagował. Tzn, element ciągle był niewidoczny. Wymieniłem implementację na .setAlpha (dokonywałem zmian tylko w dwóch klasach, czyli chyba refactoring się powiódł), ale błąd dalej występował. Okazało się, że obiekty zapamiętują jeśli były animowane i wtedy, jeśli animacja kończyła się zniknięciem, to można sobie było próbować. Naprawa była trywialna, ale nim do tego doszedłem…. Oto jak wygląda kod który naprawia tego buga:
public void hideStack(View bricks[]) {
    for (View brick : bricks) {
        brick.setAlpha(0);
        brick.clearAnimation();
    }
}
A właściwie za naprawdę odpowiada dyrektywa .clearAnimation(). Cóż, jestem z siebie naprawdę dumny, że to odkryłem 😉
Projekt na github, a także coś extra, tutaj znajduje się plik apk, który można sobie bezpośrednio zainstalować na telefonie. Link jest aktywny, tzn aplikacja będzie się tam co jakiś czas podmieniać 😉
Pozdrawiam!

#13 Refactoring, czy na pewno zrobiłem go właściwie?

Dawno nic nowego nie napisałem, a to dlatego, że następny wpis miał być o refactoringu. Skoro ma być o refactoringu, to trzeba go zrobić. Noo i się zaczęło 😉 Ale po kolei.

Założenia wstępne były takie, że chcę dodać nadklasę, w której będą metody odpowiedzialne za rzeczy wspólne dla wszystkich aktywności. Noo właśnie, ale co to tak właściwie znaczy rzeczy wspólne? Wymyśliłem, że każda aktywność kończy się wypełnieniem ekranu i po kliknięciu uruchamia nową aktywność. Noo dobrze, ale ekran możemy wypełnić jednym lub drugim widokiem w zależności od tego, czy poprzednie zadanie zakończyło się sukcesem, czy porażką. I co wtedy, oddzielne metody, czy zmienna typu boolen w parametrze wywołania? Takich pytań nasuwa się setki, a nad odpowiedziami na niektóre naprawdę długo myślałem. Nawet takie rzeczy jak decyzja o nazwie metody, to wszystko dosyć mocno mnie przytłoczyło, dlatego też długo nie mogłem skończyć ani poprawek, ani tego wpisu. Ostatecznie jednak sądzę, że mi się udało i staram się nie dopuszczać do siebie myśli, że to mogło być zrobione inaczej 😉

Klasa nadrzędna przejęła całkiem sporo funkcji, np przy pomocy ButterKnife podpinam kilka rzeczy właśnie w klasie nadrzędnej:

@Bind(R.id.fab)
FloatingActionButton fab;
@Bind(R.id.layoutSuccess)
ViewGroup layoutSuccess;
@Bind(R.id.successView)
ImageView successView;
@Bind(R.id.layoutFailure)
ViewGroup layoutFailure;
@Bind(R.id.failureView)
ImageView failureView;

Wymuszam też przeciążenie metody createViews(), aby w aktywności docelowej wywołać ją w metodzie onCreate. Dzięki temu nie muszę tych samych kawałków kodu pisać wszędzie, i mam kilka rzeczy załatwionych zaraz na starcie (np metoda calculateValues() ustawia wartości dla animacji circularReveal).

public void createViews() {
 setNaviBarColor();
 calculateValues();
 ButterKnife.bind(this);
 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
 setSupportActionBar(toolbar);
 assert getSupportActionBar() != null;
 getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}

W tej klasie obsługuję też wypełnienie ekranu i cofnięcie tego wypełnienia, oraz start aktywności z przygotowaniem całej animacji etc.

Używam tego w ten sposób, że teraz moja metoda onCreate() wygląda tak:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_compare);
        createViews();
    }

dyrektywa super w metodzie createViews() zapewnia mi wywołanie wszystkich uniwersalnych zadań, a ja mogę zająć się tym co jest istotne, czyli logiką.

Start nowej aktywności też zajmuje już tylko kilka linii:

@Override
@OnClick(R.id.successView)
public void onClickSuccessView() {
    nextActivity(true, CompareActivity.this, CompareCheckActivity.class);
}

Podsumowując, próba zrobienia tego refactoringu całkiem sporo mi dała. Musiałem zadać sobie kilka pytań i uświadomić, czego jeszcze nie wiem. A wiedza o tym, czego się nie wie, to już całkiem sporo. Wiem, że jeszcze dużo muszę się nauczyć. I pewnie kiedyś jeszcze napiszę wpis o refactoringu, jak na razie wiem, że nic nie wiem 😉

Projekt na github, gdyby ktoś chciał obejrzeć zmiany, porównywać z 288370c

Pozdrawiam!