Android - prawidłowe budowanie aktywności, klasy, metody

0

Witajcie,
Ciężko znaleźć konkretne informacje czy przykłady odnośnie dobrych praktyk programowania dlatego chciałbym poruszyć ten temat. Mam nadzieję że przyda się on również innym początkujących programistom.

Poradników, tutoriali dotyczących samego programowania na Androida jest mnóstwo. Jest sporo przykładów, poza tym dokumentacja jest bardzo dobra i wszystko jest w niej jasno pisane. W każdym z poradników czy przykładów dostrzegam jednak braki polegające na nauce dobrych praktyk programowania. Brak przykładów jak prawidłowo formatować kod, jak tworzyć aktywności.

Nie ukrywam że jest to dość frustrujące. Napisałem właśnie aplikacje. Ona działa. Wszystko jest ok. Ale z kodu który wytworzyłem jestem bardzo nie zadowolony. Praktycznie jedyne klasy jakie mam to klasy z aktywnościami. Jedna klasa na aktywność. Czy to jest faktycznie prawidłowa metoda tworzenia kodu? Średnio jedna klasa ma u mnie ok 500 linii kodu. Mam 3 przyciski po naciśnięciu których w mojej aplikacji następuje szereg różnych wyliczeń. I teraz nie wiem, czy jest to prawidłowe rozwiązanie? Czy może obsługa każdego z przycisków powinna znajdować się w osobnych klasach natomiast w klasie głównej aktywności powinny być te klasy jedynie wywoływane? Czy są jakieś takie ogólne zasady tworzenia kodu których się trzymacie? Taki szablon, schemat w który powinien przynajmniej próbować, wpisać się każdy programista tworzący kod na Androida?

Będę wdzięczny za każdą odpowiedź.

0

Pisanie aplikacji na Androida nie wyklucza stosowania dobrych praktyk programowania obiektowego (SOLID, KISS, DRY).
Na pewno pchanie całej logiki do jednej klasy (tymbardziej do Activity) nie jest dobrym rozwiązaniem aczkolwiek jest popularne wśród wielu początkujących programistów Androida. Sama dokumentacja za dużo nie pomoże, gdyż nie ma jednej 'oficjalnej' i słusznej drogi. Polecam http://androidweekly.net/, w każdym wydaniu jest średnio 1 artykuł o podejściu do projektowania architektury aplikacji. Poza tym warto śledzić repozytoria oraz prezentacje osób, które dużo wnoszą do community np. Jake Wharton. Dobrym źródłem wiedzy są też wystąpienia z konferencji, znajdziesz spokojnie na yt.

0

Teoretycznie powinno sie stosować mvp albo mvvm jeśli pytasz o "schemat". No ale konia z rzędem temu, który pokaże mi projekt na Androidzie, który z tego korzysta.

0

Przecież sam Google nie wspiera ani mvp ani mvvm, zobacz jak jest generowany kod przez Android Studio. Np wygeneruj sobie z kreatora szablon aplikacji z "hamburger menu", nie tylko wszystko siedzi w klasie MainActivity, ale jeszcze niejednokrotnie są klasy zagnieżdżone lub anonimowe.

A coś mi się zdaje że podpinanie klasy anonimowej np do zdarzenia onClick przycisku to już samo zło w swietle tzw " dobrych praktyk"

0

Tyle to ja sam wiem, podaj mi przykład realnego projektu, popularnej aplikacji OpenSource, która korzysta z MVP albo MVVM. Najlepiej link do rerpozytorium Githuba

0

podaj mi przykład realnego projektu, popularnej aplikacji OpenSource

Chyba masz trochę za duże wymagania. To że nie ma takiego projektu OpenSource nie znaczy że nikt z tego nie korzysta.
realny != popularny

0

Dobra architektura to taka, w której starasz się odzielić logikę aplikacji od kodu androidowego. Czy robisz to za pomocą MVP, MVVM czy inna kombinacja literek nie ma wielkiego znaczenia.

Czyli np. w activity button wywołuje akcję -> ta odpala kod jakiejś inne klasy która wykonuje obliczenia i następnie zwraca (najlepiej za pomocą jakiegoś callbacka, a jeżeli chcesz być fancy i umisz to RxJavy) informację do activity tak żeby poinformować usera.

Czy użyjesz do tego jakiegoś DI, czy np. czystej javy ale z ładnie zaimplementowanym MVP ( tutaj dobry przykład http://antonioleiva.com/mvp-android/) nie ma znaczenia.

A tak naprawdę jeżeli chodzi o ładną architekturę to proponuję zacząć pisać testy. Jeżeli to zrobisz to od razu Ci się odechce pisać g**no-kod bo się tego po prostu nie da stestować :)

0
wojciechmaciejewski napisał(a):

a jeżeli chcesz być fancy i umisz to RxJavy

Albo prośćiej - EventBus'a :)

0
wojciechmaciejewski napisał(a):

Czyli np. w activity button wywołuje akcję -> ta odpala kod jakiejś inne klasy która wykonuje obliczenia i następnie zwraca (najlepiej za pomocą jakiegoś callbacka, a jeżeli chcesz być fancy i umisz to RxJavy) informację do activity tak żeby poinformować usera.

Jeśli pozwolisz pociągnę trochę dyskusję na temat fragmentu który napisałeś. Sorki Panowie że tak drążę temat ale chcę się nauczyć pisać czytelny kod. To co robię obecnie mi nie wystarcza i postawiłem sobie to za cel. Muszę to zrozumieć i będę drążył ile się da. Pisałem różne proste projekty i wszystko sprowadzało się do użycia jednej, góra dwóch, trzech klas które miały milion linii kodu i dostawałem szału jak mi przyszło coś tam znaleźć. Chcę z tym skończyć raz na zawsze. Ale do rzeczy. Odnośnie tego co napisałeś, chciałbym posłużyć się konkretnym przykładem. Mam klasę MainActivity a w niej przycisk, wygląda to tak:

...
public class main extends AppCompatActivity {
    ...
    Button undoButton;
    ...
    protected void onCreate(Bundle savedInstanceState) {
        ...
        undoButton = (Button) findViewById(R.id.undo);
        ...
    }
}

Mniej więcej tak to wygląda. Pod tym przyciskiem wykonywanych jest szereg obliczeń, oto dokładnie co się pod nim dzieje (zmienne które widać w kodzie to jakieś buttony lub pola typu textView, nie wklejam inicjalizacji każdego z pól):

undoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {                
                Cursor undo = matchDb.undo(count_round - 1);
                if (undo.getCount() > 0) {
                    count = undo.getInt(0);
                    playerName1.setText(undo.getString(1));
                    total1After.setText(undo.getString(3));
                    score1 = undo.getString(4);
                    coins1.setText(undo.getString(5));
                    queen1.setChecked(false);
                    if (undo.getInt(6) == 1) {
                        queen1.setChecked(true);
                    } else {
                        queen1.setChecked(false);
                    }
                    if (undo.getInt(7) == 1) {
                        break1.setChecked(true);
                    } else {
                        break1.setChecked(false);
                    }
                    playerName2.setText(undo.getString(8));
                    total2After.setText(undo.getString(10));
                    score2 = undo.getString(11);
                    coins2.setText(undo.getString(12));
                    queen2.setChecked(false);
                    if (undo.getInt(13) == 1) {
                        queen2.setChecked(true);
                    } else {
                        queen2.setChecked(false);
                    }
                    if (undo.getInt(14) == 1) {
                        break2.setChecked(true);
                    } else {
                        break2.setChecked(false);
                    }                    
                }
                if (count == 1) {
                    undoButton.setVisibility(View.INVISIBLE);
                }
            }

        });

I teraz rozumiem że aby to było prawidłowo, powinienem z tego wszystkiego utworzyć osobną klasę a następnie po wciśnięciu przycisku w MainActivity, wywołać ją? Tylko teraz pytanie, ponieważ brak mi doświadczenia w budowaniu aplikacji w taki sposób (do tej pory wszystko co mogłem wciskałem w jedną klasę co jest olbrzymim błędem i chcę z tym skończyć) w jaki sposób prawidłowo przekazać do tej klasy wszystkie parametry? Tym bardziej że definicje zmiennych przycisków mam w klasie MainActivity więc w klasie która będzie odpowiadała za ten przycisk muszę się jakoś do nich dostać a następnie przekazać to co tam wpiszę do klasy głównej czyli wciskam przycisk undo, uruchamiam zawartość klasy, powiedzmy też nazwę ją Undo, wykonują się te wszystkie obliczenia bo czym wynik zostaje zwrócony do MainActivity i wyświetlony użytkownikowi. Czy ma ktoś z Was jakiś króciutki przykład który by mi to zobrazował?

0

Tutaj nawet nie chodzi o na siłę wyciąganie rzeczy do innej klasy. W tym kodzie który pokazałeś to to co by należało zrobić to:

  1. matchDb -to rozumiem że jakaś klasa opasująca twoją bazę danych. Na cholere ona zwraca kursor? Niech zwraca Twoją jakąś klasę. Tzn. Wrzuć do tej Klasy matchDb taki wrapper który zmienia pola Cursor na ładnie opisaną klasę

  2. Znaczące nazwy. Po twoich nazwach pól i zmiennych nie wiadomo co to jest. Lepsza jest długa nazwa która Ci od razu powie co jest co. I tak playerName1 powinno się nazywać playerName1TextView np.
    Czyli cały Twój kod powinien wyglądać tak:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        undoButton = (Button) findViewById(R.id.undo);

        undoButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                MyClass fields = matchDb.undo(count_round - 1);
                if (fields.count > 0) {
                    setUpTextViews(fields);
                    setUpScores(fields);
                    setUpCheckBoxes(fields);
                    if (count == 1) {
                        undoButton.setVisibility(View.INVISIBLE);
                    }
                }
            }

        });

    }

    private void setUpCheckBoxes(MyClass fields) {
        queen1.setChecked(fields.queen1);
        break1.setChecked(fields.break1);
        queen2.setChecked(fields.queen2);
        break2.setChecked(fields.break2);
    }

    private void setUpScores(MyClass fields) {
        score1 = fields.score1; //Nie wiem co to score1 ale też powinno mieć znaczącą nazwe
        score2 = fields.score2;
    }

    private void setUpTextViews(MyClass fields) {
        playerName1TextView.setText(fields.playerName);
        playerName2TextView.setText(fields.playerName2);
        total1AfterTextView.setText(fields.total1After);
        total1After2TextView.setText(fields.total2After);
        coins1TextView.setText(fields.coins1);
        coins2TextView.setText(fields.coins2);
    }

    class MyClass{
        String playerName,total1After,score1,coins1,playerName2,total2After,score2,coins2;
        Boolean queen1,queen2,break1,break2;
        int count;
        MyClass(Cursor undo){
            count  = undo.getInt(0);
            playerName = undo.getString(1);
            total1After =  undo.getString(3);
            score1 = undo.getString(4);
            coins1 = undo.getString(5);
            queen1 = (undo.getInt(6) == 1);
            break1 = (undo.getInt(7) == 1);
            playerName2 = undo.getString(8);
            total2After = undo.getString(10);
            score2 = undo.getString(11);
            coins2 = undo.getString(12);
            queen2 = (undo.getInt(13) == 1);
            break2 = (undo.getInt(14) == 1);
        }

    }

Klasę MyClass oczywiście można wywalić do innego pliku żeby nie robiła śmietnika

Oczywiście w środku metod setUpTextView możesz wydzielić jeszcze mniejsze metody np. setUp1TextView gdzie będziesz ustawiał tylko textViewsy z numerem 1 itd. Wszystko po to żeby kod był używalny wiele razy . Czyli żeby była zachowana zasada DRY -Do not Repeat Yourself.

Pamiętaj o kolejnej ważnej zasadzie Single Responsible. Każda klasa metoda itd ma mieć jedno zadanie. Wiem że to nie jest łatwe i często nie do końca wykonywalne ale staraj się pisać kod tak żeby 1 metoda robiła jedną rzecz.

Obejrzyj koniecznie prezentacje Venkat Subramaniam Core Software Design Principles for Programmers. Bardzo dobry wykład. Tutaj możesz go znaleźć
http://code4life.pl/#highlights-section
W razie jakichś pytań pisz :)

  1. Jeżeli nie korzystasz to zacznij korzystać z porządnego IDE. Intellij Idea ewentualnie Eclipsa. Te środowiska pomogą Ci w refaktoryzacji kodu

Jak chcesz wrzuć swój projekt na GitHub lub tutaj rara i daj po powieszać psy na sobie ale pewno się czegoś nauczysz :)

pzdr

0

Bardzo Ci dziękuję za odpowiedź. Właśnie na tym przykładzie zaczynam to rozumieć :). Oczywiście niektórych tych pól używam częściej więc idąc za Twoją radą z niektórych wydzieliłem sobie mniejsze metody.

Chciałbym jeszcze jednak wyjaśnić wszystko żebym tak w 100% był tego pewien i wiedział co robię. Zastosowałem to co mi napisałeś. Jeśli dobrze rozumiem to w klasie głównej tworzę sobie zmienne odpowiadające moim przyciskom, polom itd. Następnie je inicjuje. Utworzyłem sobie drugą klasę którą nazwałem Undo (Twoja MyClass) i tam wrzuciłem to co wrzuciłeś Ty. Następnie w klasie głównej utworzyłem sobie metody które oczywiście też rozumiem, wiem jak działają i co tam się dziej. Na końcu obsługa całości przyciskiem no i tu nie rozumiem pewnej rzeczy i w niej też mam błąd. Chodzi dokładnie o ten fragment kodu:

Undo fields = matchDb.undo(count_round-1);

Mam całą tą linijkę podkreśloną na czerwono. No i tego właśnie miejsca nie rozumiem bo tu powinienem do swojej klasy DatabaseHelper, przekazać parametr do Cursora po którym on się wykona. A tu nie bardzo rozumiem co się dzieje i mówiąc szczerze nie wiem jak ewentualnie to naprawić.

Android studio podpowiada aby zrobić to w taki sposób:

Undo fields = (Undo) matchDb.undo(count_round-1);

ale niestety po skompilowaniu i wciśnięciu przycisku wyświetla mi komunikat:

12-20 1825.258 2140-2140/? E/AndroidRuntime: java.lang.ClassCastException: android.database.sqlite.SQLiteCursor cannot be cast to com.example.user.apk.Undo

0

Tak wygląda moja metoda z klasy DatabaseHelper:

public Cursor undo(int id) {
        SQLiteDatabase db = this.getReadableDatabase();
        String sqlSelect = "SELECT " + COL_2 + ", " + COL_3 + ", " + COL_4 + ", " + COL_5 + ", " + COL_6 + ", " + COL_7 + ", " + COL_8 + ", " + COL_9 + ", " + COL_10 + ", " + COL_11 + ", " + COL_12 + ", " + COL_13 + ", " + COL_14 +  ", " + COL_15 + ", " + COL_16 + " FROM " + TABLE_NAME + " WHERE " + COL_2 + "=" + id;
        Cursor cursor = db.rawQuery(sqlSelect, null);

        if(cursor != null) {
            cursor.moveToFirst();
        }
        return cursor;
    }

Oczywiście ja to przerobię bo mam tu dwa błędy, pierwszy to taki że zwracam cursor a drugi to taki że powinienem korzystać z query a nie rawQuery ponieważ query jest mniej podatne na SQL injection.

A co mi zwraca? Chyba właśnie tu jest problem ponieważ błąd to: Incompatible types. Oczekuje typu klasy Undo (w Twoim przykładzie MyClass) a dostał typ Cursor

0

Chyba sobie poradziłem aczkolwiek jeszcze chciałbym abyś rzucił okiem czy prawidłowo :)

Undo fields = new Undo(matchDb.undo(count - 1));
0

Ok, wszystko sobie ładnie poprawiłem zgodnie z tym co napisałeś, sprawdziłem, działa. Została mi jeszcze jedna rzecz do zoptymalizowania i trochę się przy tym gubię. Bo mam jeden przycisk w klasie głównej który pobiera sobie z pól textView, Buttonów i Checkboxów wartości i w zależności co jest wybrane, wpisane, zaznaczone, wykonuje różne operacje. I teraz stworzyłem sobie klasę która będzie mi te wyliczenia robiła. I tu dochodzimy do sedna sprawy, że nie bardzo wiem jak przekazać do tej klasy te moje wartości z pól. Tak robię to w klasie głównej:

if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) > 0 && Integer.parseInt(total1AfterButton.getText().toString()) < 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0){
        total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 3));
        score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 3);

    }
    else if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) < 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0) {
        total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 3));
        score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 3);
    }
    else if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) >= 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0) {
        total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 1));
        score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 1);
    }
    else {
        total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString())));
        score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()));
    }

Czy to w ogóle jestem w stanie zrobić w innej klasie czy podzielić ten kod na mniejsze metody i wykonywać go w klasie głównej?

0

Może pokażę jak ja bym to zrobił a Wy powiecie czy jest to prawidłowe. Nie mówię że jest ale dopiero się uczę a najłatwiej robić to na własnych błędach. Wszystko mam w jednej klasie.

Metody:

 private boolean calculatePlayer1Var1() {
        if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) > 0 && Integer.parseInt(total1AfterButton.getText().toString()) < 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0){
            total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 3));
            score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 3);
            return true;
        }
        return false;
    }

    private boolean calculatePlayer1Var2() {
        if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) < 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0) {
            total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 3));
            score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 3);
            return true;
        }
        return false;
    }

    private boolean calculatePlayer1Var3() {
        if(queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) >= 22 && Integer.parseInt(coins2TextView.getText().toString()) == 0) {
            total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString()) + 1));
            score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()) + 1);
            return true;
        }
        return false;
    }

    private boolean calculatePlayer1Var4() {
        total1AfterButton.setText(Integer.toString(Integer.parseInt(total1AfterButton.getText().toString()) + Integer.parseInt(coins1TextView.getText().toString())));
        score1 = Integer.toString(Integer.parseInt(coins1TextView.getText().toString()));
        return true;
    }

Wywołanie:

next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {                
               if(calculatePlayer1Var1()) {
                }
                else if(calculatePlayer1Var2()) {
                }
                else if(calculatePlayer1Var3()) {
                }
                else {
                    calculatePlayer1Var4();
                }
});
0

Zrob metode tylko na warunek cos takiego. A w ifie rob to co masz zrobic. Pamietaj jedna metoda jedna odpowiedzialnosc

    private boolean calculatePlayer1Var3() {
       return (queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) >= 22 &&         Integer.parseInt(coins2TextView.getText().toString()) == 0)
    }
0

Czyli jeżeli warunek z pierwszego if-a jest spełniony, wykonaj metodę 1, jeśli nie, sprawdź warunek w drugim if-ie, jeśli spełniony wowołaj metodę drugą itd?

0

A jeszcze mam jedną rzecz z którą sobie powiedzmy w 3/4 poradziłem. Mam zrobiony timer który odlicza mi czas. Przeniosłem sobie go do osobnej klasy, stworzyłem metodę w klasie głównej i uruchomiłem ją przyciskiem. Tak wygląda kod:

Klasa Timer:

public class Timer extends CountDownTimer{
    String start, hms;
    public Timer(long miliseconds, long interval) {
        super(miliseconds, interval);

    }

    @Override
    public void onTick(long millisUntilFinished) {
        hms = String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished),
                TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished)));
        System.out.println(hms);
        start = hms;

    }

    @Override
    public void onFinish() {
        start = "00:00";
    }
}

Metoda:

private void timer() {
    Timer timer = new Timer(milliseconds, interval);
    timer.start();
    start.setText(timer.hms);
}

Button:

start.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        timer();
    }
});

I wszystko jest ok, zegar się odpala, odlicza czas ponieważ widzę to w logcat'ie ale niestety nie chce mi się ten czas odliczać w aplikacji. Mam na starcie w TextView wartość 45:00 ponieważ taką wartością inicjuję to pole, wciskam odliczanie, 45:00 znika i nic się nie pojawia. Problem leży gdzieś na styku tych dwóch klas, zmienna start która powinna mi pokazywać upływający czas albo nie jest przekazywana właściwie do klasy głównej albo ją niewłaściwie obsługuję.

1

Dołóż sobie taki interface

interface OnTick{
void setTextOnTick(String text);
}

i zrób tak żeby Twój Activity go implementował np tak:

public class MyActivity extends Activity implements OnTick{
.......
.....

@Override
public void setTextOnTick(String text){
runOnUiThread{//nie wiem czy to tu potrzebne sprawdź sam
 start.setText(text);
}

}


}

i teraz dołoż to jako parametr konstruktora swojej klasy z timerem

String start, hms;

OnTick myTickListener;
    public Timer(long miliseconds, long interval,OnTick myTickListener) {
        super(miliseconds, interval);
        this.myTickListener=myTickListener;

    }

  @Override
    public void onTick(long millisUntilFinished) {
        hms = String.format("%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished),
                TimeUnit.MILLISECONDS.toSeconds(millisUntilFinished) - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(millisUntilFinished)));
        System.out.println(hms);
        this.myTickListener.setTextOnTick(hms)

    }
0

Pozwolę sobie jeszcze Ciebie dopytać ponieważ próbuję sam to naprawić ale mi się nie udaje. Dodałeś do klasy Timer dodatkowy parametr OnTick myTickListener:

public Timer(long miliseconds, long interval, OnTick myTickListener) {

Więc teraz tworząc metodę Timer muszę do niej wrzucić 3 parametry. Problem mam z tym trzecim bo nie bardzo wiem jaki parametr tam zapodać. Natomiast gdy usunę: OnTick myTickListener ze swojej klasy Timer to mi rzuca wyjątkiem:

 
Attempt to invoke interface method 'void com.example.user.apk.OnTick.setTextOnTick(java.lang.String)' on a null object reference

---EDIT---
Ok, udało mi się. Dałem jako ten trzeci parametr metody po prostu "this". Teraz muszę zrozumieć jak to działa.

1 użytkowników online, w tym zalogowanych: 0, gości: 1