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

Android - prawidłowe budowanie aktywności, klasy, metody
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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ź.

bolson
  • Rejestracja:około 15 lat
  • Ostatnio:44 minuty
  • Lokalizacja:Zielona Góra/Poznań
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"

panryz
  • Rejestracja:prawie 17 lat
  • Ostatnio:około godziny
0
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

panryz
  • Rejestracja:prawie 17 lat
  • Ostatnio:około godziny
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

wojciechmaciejewski
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 lata
  • Postów:560
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ć :)

panryz
  • Rejestracja:prawie 17 lat
  • Ostatnio:około godziny
0
wojciechmaciejewski napisał(a):

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

Albo prośćiej - EventBus'a :)

wojciechmaciejewski
to prawda, chociaż ja Event busów raczej używam z serwisów, ale co kto lubi :)
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
...
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):

Kopiuj
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ł?

wojciechmaciejewski
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 lata
  • Postów:560
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:

Kopiuj
    @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

L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
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:

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

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

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

edytowany 3x, ostatnio: lukpio3
wojciechmaciejewski
a co jest napisane jak Ci podkreśla? podejrzewam że metoda undo klasy matchDb nie zwraca Undo tylko zwraca coś innego. Zastanów się nad tym
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
0

Tak wygląda moja metoda z klasy DatabaseHelper:

Kopiuj
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

edytowany 1x, ostatnio: lukpio3
wojciechmaciejewski
No dokładnie, Masz zwracany Cursor a oczekujesz Undo. No to nic dziwnego żę Ci leci wyjątek. Zastanów się jak to zmienić ;-)
L3
A jakaś podpowiedź drobna? :) Problem leży tylko w tej linii kodu czy muszę jeszcze dodatkowo coś przerobić?
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
0

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

Kopiuj
Undo fields = new Undo(matchDb.undo(count - 1));
wojciechmaciejewski
jest ok. Natomiast gorzej że nie wiedziałeś o co kaman. Poprzerabiaj sobie jakieś ćwiczenia z podstaw Javy
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
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?

wojciechmaciejewski
powyciągaj każdy warunek to metody.
L3
Jasne ale rozumiem że mam to zrobić w klasie głównej? Metody chyba powinienem porobić typu boolean i później w przycisku jeśli wynik metody 1 zwróci mi false to wykonuję metodę 2, jesli druga zwróci false to 3 aż nie dostanę true. Dobrze mówię?
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
 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:

Kopiuj
next.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {                
               if(calculatePlayer1Var1()) {
                }
                else if(calculatePlayer1Var2()) {
                }
                else if(calculatePlayer1Var3()) {
                }
                else {
                    calculatePlayer1Var4();
                }
});
edytowany 1x, ostatnio: lukpio3
wojciechmaciejewski
masz 2 razy wywolanie metody. źle
L3
Wiem, teraz zauważyłem jak testowałem i do tego już doszedłem. Poprawiłem. Teraz jest ok. Ale pytanie czy to jest właściwie zrobione, zgodnie ze sztuką programowania czy jeszcze powinienem coś poprawić? Chcę się nauczyć dobrych praktyk i nawyków bo zacząłem kiedyś źle pisać, na studiach nikt nie zwracał uwagi jak cały projekt zwaliłem do jednej klasy która miała parę tysięcy linii kodu i efekty teraz są takie jak widać ...
wojciechmaciejewski
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 lata
  • Postów:560
0

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

Kopiuj
    private boolean calculatePlayer1Var3() {
       return (queen1Checkbox.isChecked() && Integer.parseInt(coins1TextView.getText().toString()) == 0 && Integer.parseInt(total1AfterButton.getText().toString()) >= 22 &&         Integer.parseInt(coins2TextView.getText().toString()) == 0)
    }
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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?

L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
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:

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

Button:

Kopiuj
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ę.

wojciechmaciejewski
  • Rejestracja:ponad 12 lat
  • Ostatnio:około 2 lata
  • Postów:560
1

Dołóż sobie taki interface

Kopiuj
interface OnTick{
void setTextOnTick(String text);
}

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

Kopiuj
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;

Kopiuj
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)

    }
L3
Wojtek, czy mógłbyś krótko wyjaśnić na jakiej zasadzie to działa? Bo próbuję zrozumieć to i albo jest za późna godzina albo tego nie rozumiem :( Czy to nie jest to samo co start.setText(timer.hms);?? Bo to tak w skrócie wygląda tylko że takie przypisanie nie działa a to rozwiązanie które podałeś Ty, działa bez problemu.
wojciechmaciejewski
działa to tak bo jeżeli wykonujesz setText to robisz to raz. a chodzi o to żeby to aktualizować. przy setText nie przekazujesz referencji do tego string a jedynie kopiujesz jego wartość dlatego wraz ze zmianą hms nie zmienia się text w textView
L3
To znaczy wydaje mi się że referencję przekazuję tylko robię to jednorazowo. I chodzi o to aby stworzyć listener których nasłuchuje na zamianę hms i na bieżąco przekazuje tą zmianę do pola TextView. Czy źle mówię?
wojciechmaciejewski
generalnie tak. Ale przy Stringach jest inaczej bo stringi są immutable. Czyli jeżeli piszesz że String a= b to nie przekazujesz referencji ale kopiujesz wartość.
L3
A to tego nie wiedziałem. Czyli już wszystko jasne. Teraz rozumiem. Bardzo dziękuję i życzę Wesołych Świąt :)
L3
  • Rejestracja:prawie 15 lat
  • Ostatnio:ponad 3 lata
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:

Kopiuj
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:

Kopiuj
 
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.

edytowany 1x, ostatnio: lukpio3
wojciechmaciejewski
działa w taki sposób że Twoje activity implementuje OnTick. W takim razie jest OnTick. Jak przekazujesz this, to kompilator javy od razu wie że przekazujesz OnTick bo tego się spodziewa. Nazywa się to polimorfizm. Po raz kolejny odsyłam Cie do podstaw Javy żeby przerobić
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)