ListView custom adapter with button

0

Witam mam taki oto kawałek kodu

IteamListViewAdapter.java

package com.example.green.snizo.naukaadaptery3;

import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import java.util.List;

public class IteamListViewAdapter extends ArrayAdapter<Iteams> {

    private Context mContext;
    private int mResources;
    private  List<Iteams> row;
    DBHelper databaseHelper;

    public IteamListViewAdapter(@NonNull Context context, int resource, @NonNull List<Iteams> objects) {
        super(context, resource, objects);
        this.mContext = context;
        this.mResources = resource;
        this.row=objects;
    }

    public void updateResults(List<Iteams> results) {
        row = results;
        //Triggers the list update
        notifyDataSetChanged();
    }

    static class ListViewHolder {
        TextView name;
        TextView quantity;
        Button button;
        ProgressBar progressBar;
    }

    @Override
    public View getView(final int position, View convertView, final ViewGroup parent) {

        final ListViewHolder viewHolder;

        if (convertView==null) {
            LayoutInflater layoutInflater= (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView=layoutInflater.inflate(mResources,parent,false);
            viewHolder=new ListViewHolder();
            viewHolder.name =(TextView) convertView.findViewById(R.id.textViewIteamName);
            viewHolder.quantity =(TextView) convertView.findViewById(R.id.textViewQuantity);
            viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressBarrQuantity);
            viewHolder.button= (Button) convertView.findViewById(R.id.buttonAddQuantity);
            convertView.setTag(viewHolder);
        }
        else {
            // Retrieve holder from view
            viewHolder = (ListViewHolder) convertView.getTag();
        }
            viewHolder.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                row.get(position).setProgres(getItem(position).getProgres()+1);
                Toast.makeText(mContext,  getItemId(position)+" An item of the ListView is clicked.", Toast.LENGTH_LONG).show();
                databaseHelper=new DBHelper(mContext);
                databaseHelper.update(String.valueOf(getItem(position).ids),getItem(position).getProgres());
                updateResults(row);
                if(getItem(position).getQuantity() < getItem(position).getProgres())
                    Toast.makeText(mContext,"Ilość nadprogramowa",Toast.LENGTH_SHORT).show();
            }
        });
    // Set text to each TextView of ListView item
        ListViewHolder holder = (ListViewHolder) convertView.getTag();
        holder.name.setText(getItem(position).getIteamName()+ " ids="+getItem(position).getIds());
        holder.quantity.setText(quantity(getItem(position).getQuantity(),getItem(position).getProgres()));
        holder.progressBar.setProgress(progres(getItem(position).getQuantity(),getItem(position).getProgres()));

        return convertView;
    }

    private int progres(int full, int quantity) {
        return (quantity*100)/full;
    }

    private String quantity(int full, int quantity) {
        return quantity + "/" + full;
    }
}

Iteams.java

package com.example.green.snizo.naukaadaptery3;

public class Iteams {
    String iteamName;
    int progres;
    int quantity;
    int ids;

    public Iteams(String iteamName, int progres, int quantity, int id) {
        this.iteamName = iteamName;
        this.progres = progres;
        this.quantity = quantity;
        this.ids = id;
    }

    public String getIteamName() {
        return iteamName;
    }

    public void setIteamName(String iteamName) {
        this.iteamName = iteamName;
    }

    public int getProgres() {
        return progres;
    }

    public void setProgres(int progres) {
        this.progres = progres;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getIds() {
        return ids;
    }
}

IteamlistActivity.java

package com.example.green.snizo.naukaadaptery3;


import android.database.sqlite.SQLiteCursor;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;

public class IteamlistActivity extends AppCompatActivity {
    
public ArrayList<Iteams> iteamsArrayList=new ArrayList<>();
DBHelper databaseHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_iteamlist);
        databaseHelper=new DBHelper(this);
        SQLiteCursor cursor=databaseHelper.getData();
        //--------------- testowe dane--------
        if(cursor.getCount() == 0) {
            for (int i = 0; i < 7; i++) {
                if (databaseHelper.addData("cursor one " + i, 10, 20)) {
                    Toast.makeText(this, "udalosie", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "nie udalosie", Toast.LENGTH_SHORT).show();
                }

                if (databaseHelper.addData("cursor two " + i, 12, 30)) {
                    Toast.makeText(this, "udalosie", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(this, "nie udalosie", Toast.LENGTH_SHORT).show();
                }
            }
        }
        //--------------- testowe dane--------
        dbToArray();

        ListView listViewIteam = findViewById(R.id.listViewIteam);
        IteamListViewAdapter arrayAdapter=new IteamListViewAdapter(this, R.layout.adapter_list_view_layout, iteamsArrayList);
        listViewIteam.setAdapter(arrayAdapter);
    }

    public void add(View view){
        databaseHelper=new DBHelper(this);
        databaseHelper.deleteAllData();
        iteamsArrayList.clear();
        dbToArray();
        ListView listViewIteam = findViewById(R.id.listViewIteam);
        IteamListViewAdapter arrayAdapter=new IteamListViewAdapter(this, R.layout.adapter_list_view_layout, iteamsArrayList);
        listViewIteam.setAdapter(arrayAdapter);
    }

    private void dbToArray(){
        SQLiteCursor cursor=databaseHelper.getData();
        while (cursor.moveToNext()) {
            iteamsArrayList.add(new Iteams(cursor.getString(1), cursor.getInt(2), cursor.getInt(3),cursor.getInt(0)));
        }
    }
}

Ogólnie kod działa tak jak chciałem, pobiera dane z bazy, edytuje itd. Pytanie o sam kod co można poprawić... co jest słabo zrobione.. itd...

1

Słabe samo w sobie jest to, że jest to ListView zamiast RecyclerView:)

  1. zmiana na RecyclerView + adapter + viewholder

  2. RecyclerView.Adapter<VH> (onCreateViewholder, onBindViewholder, getItemCount) + ViewHolder pattern (onClickListener ustawiać w konstruktorze + jakaś metoda bind - bind ma być tak lekkie jak się da)
    a) na pewno ten DBHelper jest potrzebny w adapterze?
    b) tworzysz nowy w każdym onClick :/
    c)

           LayoutInflater layoutInflater= (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
           ...
           można
           convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.id, parent, false);

d)

    public void setData(List<Iteams> items) {
        this.items.clear();
        this.items.addAll(items);
        this.notifyDataSetChanged();
    }

e) Jak masz zamiar zostać przy Java to używaj chociaż biblioteki ButterKnife żeby pozbyć się tych wszystkich findViewById

        if (convertView==null) {
            ...
            viewHolder.name =(TextView) convertView.findViewById(R.id.textViewIteamName);
            viewHolder.quantity =(TextView) convertView.findViewById(R.id.textViewQuantity);
            viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressBarrQuantity);
            viewHolder.button= (Button) convertView.findViewById(R.id.buttonAddQuantity);
            convertView.setTag(viewHolder);
        }

f) na pewno potrzebne jest tryllion nowych obiektów databaseHelper=new DBHelper(mContext); ?

3.public class Iteams:
a) powinny dążyć do tego by być niezmienne ( wszelkiego rodzaju entity, wrappery, klasy które są tylko kontenerami na dane) final
b) skoro piszesz w Java, to weź użyj jakiegoś Lobok albo czegoś podobnego... nie będziesz robił chyba setterów/getterów dla np. 20 zmiennych?
c) co to jest Iteams? Items? Item? Sprawdzaj literówki bo głupio się je czyta.
d) w twoim kodzie gettery i settery są niepotrzebne - wszak wszystko dałeś jako local public i mamy do tego dostęp lokalny

public class Iteams {
    **private final** String iteamName;
    **private final** int progres;
    **private final** int quantity;
    **private final** int id;
 
    public Iteams(String iteamName, int progres, int quantity, int id) {
        this.iteamName = iteamName;
        this.progress = progress;
        this.quantity = quantity;
        this.id = id;
    }
 
    public String getIteamName() {
        return iteamName;
    }
...
}
  1. ostatniej nie chce mi się już czytać, może ktoś się wypowie

Pokrótce tyle na pierwszy rzut oka. Spora część wychodzi z tego, że użyłeś ListView zamiast RecyclerView. Większość tego kodu powinno być przebudowane.
Metody są za długie, klasy mają za dużo odpowiedzialności.
Zmień na RV + Viewholder, wdrąż Butterknife, Lombok ( na takie małe projekty-ćwiczenia nic nim nie spsujesz), pilnuj żeby kontenery na dane były raczej immutable ( final kiedy się da).

2
public class IteamListViewAdapter extends ArrayAdapter<Iteams> {

    private Context mContext;
    private int mResources;
    private  List<Iteams> row;
    DBHelper databaseHelper;

Nie mieszaj sposobów nazewnictwa. Albo trzymaj się patologicznej notacji pseudowęgierskiej albo jak ludzie używaj normalnych nazw. Same nazwy też złe - zarówno zmiennych jak i klas. mResources powinny być np. itemLayoutId. DBHelper absolutnie nic mówi czym jest. Coś takiego jak DBHelper mogłoby być prywatną klasą utlisową, z której korzysta warstwa bazy danych a nie widok aplikacji. List<Item> row powinno być rows jak już, ale mało to opisowe - lepiej items. Pola powinny być final. Może poza listą, ze względu na naturę adapetrów w Androidzie. Jeszcze zależy czy adapter ma sztywną listę. Iteams - lietrówka jak się domyślam.

public IteamListViewAdapter(@NonNull Context context, int resource, @NonNull List<Iteams> objects) {
    super(context, resource, objects);
    this.mContext = context;
    this.mResources = resource;
    this.row=objects;
}

Adapter nie powinien przyjmować Context jeżeli jedyne do czego jest wykorzystywany to wyszukiwanie LayoutInflater. Niby używasz kontekstu jeszcze do tworzenia dalej w kodzie DBHelper i dla pokazywania Toast, ale już wspomniałem, że DBHelper nie powinno tu być w ogóle. Natomiast obsługa kliknięcia powinna sterowana z zewnątrz a nie wewnątrz adaptera - o tym troche później. int resource też jest złe. Adapter sam powinien wiedzieć jak mają wyglądać obiekty, które będzie wyświetlać bez dostawanie tej zależności z zewnątrz. A na pewno nie w tym przypadku. W Twoim przypadku lista w konstruktorze też jest niepotrzebna, bo masz od tego metodę updateResults.

public void updateResults(List<Iteams> results) {
    row = results;
    //Triggers the list update
    notifyDataSetChanged();
}

Tutaj długo można pisać. Używanie ListView może być ok, ale z dużym nastawienim na może. Problem z ListView i konsekwentnie z adapterem do niego jest taki, że nie można z marszu informować o tym, że podmieniło się tylko jeden element listy i należy go zaktualizować. Trzeba niestety wołać notifyDataSetChanged i wszystko musi być bindowane od nowa. Do prototypowania, nauki czy prostych widoków to jeszcze ujdzie. Zazwyczaj jednak używa się RecyclerView, DiffUtils i osobnego wątku do liczenia różnić w liście. Podlinkuję jeden ze swoich ostatnich postów, bo nie chce mi się drugi raz tego pisać - https://4programmers.net/Forum/1490907.

static class ListViewHolder {
    TextView name;
    TextView quantity;
    Button button;
    ProgressBar progressBar;
}

Dobrze, że korzystasz z wzorca ViewHolder. Trochę nietypowo jest wrzucać klasę tak w środoku innej klasy. Robi się to raczej na jej końcu jak chcesz mieć zagnieżdżoną klasę. ViewHolder sam powinien wiedzieć jak ma znaleźć te wszystkie widoki, które ma w środku. I sam powinień umieć się zbindować do klasy, która jest do niego przekazywana - tutaj Iteam.

@Override
public View getView(final int position, View convertView, final ViewGroup parent) {

    final ListViewHolder viewHolder;

    if (convertView==null) {
        LayoutInflater layoutInflater= (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        convertView=layoutInflater.inflate(mResources,parent,false);
        viewHolder=new ListViewHolder();
        viewHolder.name =(TextView) convertView.findViewById(R.id.textViewIteamName);
        viewHolder.quantity =(TextView) convertView.findViewById(R.id.textViewQuantity);
        viewHolder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressBarrQuantity);
        viewHolder.button= (Button) convertView.findViewById(R.id.buttonAddQuantity);
        convertView.setTag(viewHolder);
    }
    else {
        // Retrieve holder from view
        viewHolder = (ListViewHolder) convertView.getTag();
    }
        viewHolder.button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            row.get(position).setProgres(getItem(position).getProgres()+1);
            Toast.makeText(mContext,  getItemId(position)+" An item of the ListView is clicked.", Toast.LENGTH_LONG).show();
            databaseHelper=new DBHelper(mContext);
            databaseHelper.update(String.valueOf(getItem(position).ids),getItem(position).getProgres());
            updateResults(row);
            if(getItem(position).getQuantity() < getItem(position).getProgres())
                Toast.makeText(mContext,"Ilość nadprogramowa",Toast.LENGTH_SHORT).show();
        }
    });
// Set text to each TextView of ListView item
    ListViewHolder holder = (ListViewHolder) convertView.getTag();
    holder.name.setText(getItem(position).getIteamName()+ " ids="+getItem(position).getIds());
    holder.quantity.setText(quantity(getItem(position).getQuantity(),getItem(position).getProgres()));
    holder.progressBar.setProgress(progres(getItem(position).getQuantity(),getItem(position).getProgres()));

    return convertView;
}

Źle, że tak dużo się dzieje w tej jednej metodzie. O tym, że ViewHolder sam ma wiedzieć jak się utworzyć przed chwilą pisałem. Z innych rzeczy to wyszukiwanie za każdym razem LayoutInflater. Po pierwsze powinien być przekazany przez konstruktor dla samej czytelności chociażby. Po drugie mogą się zacząć dziać śmieszne rzeczy gdybyś tam nie przekazywał AppCompatActivity jako kontekstu. Kolejny problem to ustawianie OnClickListener w każym cyklu bindowania. Listener powinien być tworzony raz w konstruktorze a jego akcja powinna być oddelegowana do interfejsu przekazanego w konstruktorze adaptera. Wychodzi tutaj też głupoa posiadania pola databaseHelper, bo przy każdym kliknięciu tworzysz nowy i referencja nie jest Ci do niczego nie potrzebna. No i tworzenie nowego DBHelper czym by on nie był przy każdej akcji kliknięcia jest kiepskie. Dodatowko jakiekolwiek operacje zapisu/odczytu na głównym wątku aplikacji są złe.

public class Iteams {
    String iteamName;
    int progres;
    int quantity;
    int ids;

Wszystko powinno być final. Nazwa iteamName powinna być name. Nazwa ids jest nic nie mówiąca. Z Lomboka bym nie korzystał tak jak ktoś wcześniej tam polecił. Gettery i settery nie są do niczego potrzebne. Zmienne mogą być public albo package private zależnie od potrzeby. Lombok nie daje wtedy żadnej wartości. Niby można zyskać z Lombokiem equals, hashCode i toString, ale na Androidzie zdecydowanie lepsza jest biblioteka AutoValue. Wymaga trochę więcej kodu do napisania samemu niż Lombok, ale ma dużo przydatnych rozszerzeń pod Androida i nie ma obrzydliwej składni. Ale nie uważam, żebyś potrzebował ani jednego ani drugiego.

for (int i = 0; i < 7; i++) {
    if (databaseHelper.addData("cursor one " + i, 10, 20)) {
        Toast.makeText(this, "udalosie", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "nie udalosie", Toast.LENGTH_SHORT).show();
    }

    if (databaseHelper.addData("cursor two " + i, 12, 30)) {
        Toast.makeText(this, "udalosie", Toast.LENGTH_SHORT).show();
    } else {
        Toast.makeText(this, "nie udalosie", Toast.LENGTH_SHORT).show();
    }
}

Domyślam się, że te Toast masz tutaj do sprawdzania poprawności działania aplikacji. Powinieneś stosować do tego klasę Log i zaznajomić się z Logcatem. Ewentualnie stawiać breakpointy i debugować, jeżeli kompletnie program świruje.

public void add(View view){
    databaseHelper=new DBHelper(this);
    databaseHelper.deleteAllData();
    iteamsArrayList.clear();
    dbToArray();
    ListView listViewIteam = findViewById(R.id.listViewIteam);
    IteamListViewAdapter arrayAdapter=new IteamListViewAdapter(this, R.layout.adapter_list_view_layout, iteamsArrayList);
    listViewIteam.setAdapter(arrayAdapter);
}

Zamiast skorzystać z metody update na swoim adapterze to tworzysz jescze raz nowy. Domyślam się, że metody typu deleteAllData są dla Ciebie, bo się bawisz z kodem, ale normalnie czegoś takiego nie powinno być w metodzie, która ma sygnaturę void add(View view) - sama nazwa metody też raczej kiepska. No i nie na obiekcie DBHelper, który notabene ponownie jest tworzony za każdym razem.

private void dbToArray(){
    SQLiteCursor cursor=databaseHelper.getData();
    while (cursor.moveToNext()) {
        iteamsArrayList.add(new Iteams(cursor.getString(1), cursor.getInt(2), cursor.getInt(3),cursor.getInt(0)));
    }
}

To powinna być metoda jakiegoś obiektu np. IteamDao, który by zwracał List<Iteams>. I nie powinno to być robione na głównym wątku.

0

Dzięki nie miałem pojęcia ze można skrócić prace z elementami interfejsu już zaglądam do tej biblioteki.
Wczoraj po wstawieniu posta trafiłem właśnie na lomboka.
jeśli chodzi o DBhelper nie bardzo mam pomysł jak inaczej to wykonać, chodzi o to ze chce aby zmiany były zapisywane od ręki i nawet w przypadku nagłego zamknięcia aplikacji zmiany nie zostały stracone. Na razie wywaliłem go na sam początek getView.

Zapytam jeszcze o ogólny zamysł który mam...

cel aplikacji: wyświetlenie obiektów, oraz ilości jaką ich posiadamy przedstawioną w postaci progressbar (różne przedmioty np. zapasy produktów żywnościowych w domu ), przy każdym produkcie przycisk którym zwiększamy ilość posiadanych produktów danego typy.

mój pomysł na kod apki..
1 baza danych zawierająca produkty wraz z ich ilością,
2 a)mechanizm pobierania danych z bazy, b)mechanizm edytujący dane w bazie o ilości konkretnych produktów, c)mechanizm dodania nowego produktu do bazy
3 mechanizm wyświetlający produkty w postaci listy.

sposób realizacji :
1 baza sql zawierająca nazwy, ilość oraz minimalna zalecana ilość(100%progresbara) produktów danego rodzaju.
2a podczas uruchomienia aktywności wyświetlającej listę obiektów pobranie danych z bazy sql użycie cursora i wprowadzenie do arraylist
2b button wyświetlany w liście służący do inkrementacji ilości produktu w bazie danych i powodujący aktualizacje wyświetlanego w liscie progresbara. Do tego celu wykorzystany adapter.
2c osobna aktywność służąca do wprowadzenia nowego produktu do bazy.
3 adapter pobierający dane o obiekcie z arraylist, obsługa przycisku inkrementacji ilości.

W jakim stopniu ta koncepcja jest sensowna?

0

Dziki Michał za wyczerpująca wypowiedź analizuje ją stopniowo i wyciągam wnioski. Nazewnictwo faktycznie leży ale tak to jest jak różne kursy się przerabia:P
Jeśli chodzi o adapter... można prosić o jakieś dodatkowe informacje, porady, przykład. Dość sporo materiałów na ten temat przeglądałem i tak różnie było to prezentowane ze nie mam pojęcia która metoda właściwa. Książki prezentują najprostsze adaptery typu wyświetlanie string i image, a obsługa uproszczona do kliknięcia całego elementu listy w celu wyświetlenia toasta lub nowego activity... :/

w miedzy czasie biorę się za stopniowe pisanie od nowa całości... trzeba się nie poddawać i klepać kod:)

2

Na ListView mogłoby to wyglądać np. tak jak poniżej. Ale i tak zdecydowanie lepiej jest użyć RecyclerView razem z DiffUtils. Tutaj niestety dla szybkości updateList() wygląda jak wygląda.

class Counter {
  final long id;
  final String name;
  final int count;

  Counter(long id, String name, int count) {
    this.id = id;
    this.name = name;
    this.count = count;
  }

  Counter increment() {
    return new Counter(id, name, count + 1);
  }
}

class CounterViewHolder implements View.OnClickListener {
  private final TextView name;
  private final TextView count;
  private final CounterAdapter.Callback callback;

  private Counter counter;

  CounterViewHolder(View view, CounterAdapter.Callback callback) {
    this.name = view.findViewById(R.id.name);
    this.count = view.findViewById(R.id.count);
    this.callback = callback;

    view.findViewById(R.id.increase_count_button).setOnClickListener(this);
  }

  @Override
  public void onClick(View view) {
    if (counter == null) {
      throw new IllegalStateException("Can't click on a counter before binding the view!");
    }
    callback.onCounterClick(counter);
  }

  void bindItem(Counter counter) {
    this.counter = counter;
    name.setText(counter.name);
    count.setText(counter.count);
  }
}

class CounterAdapter extends ArrayAdapter<Counter> {
  private final LayoutInflater layoutInflater;
  private final Callback callback;

  public CounterAdapter(LayoutInflater layoutInflater, Callback callback) {
    super(layoutInflater.getContext(), -1);
    this.layoutInflater = layoutInflater;
    this.callback = callback;
  }

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    CounterViewHolder viewHolder;
    if (convertView == null) {
      convertView = layoutInflater.inflate(R.layout.counter_view, parent, false);
      viewHolder = new CounterViewHolder(convertView, callback);
      convertView.setTag(viewHolder);
    } else {
      viewHolder = (CounterViewHolder) convertView.getTag();
    }
    viewHolder.bindItem(getItem(position));
    return convertView;
  }

  void updateList(List<Counter> counters) {
    clear();
    addAll(counters);
    notifyDataSetChanged();
  }

  interface Callback {
    void onCounterClick(Counter counter);
  }
}

Potem mógłbyś wykorzystać swój adapter w Activity. Powiedzmy, że część onCreate() byłaby taka, jak poniżej.

counterAdapter = new CounterAdapter(getLayoutInflater(), new CounterAdapter.Callback() {
  @Override
  public void onCounterClick(Counter counter) {
    counterDao.updateCounter(counter.increment());        
  }
});    
counterDao.onCountersUpdate(new CounterDao.OnCountersUpdateListener() {
  @Override
  public void onUpdate(final List<Counter> counters) {
    runOnUiThread(new Runnable() {
      @Override
      public void run() {
        counterAdapter.updateList(counters);
      }
    });
  }
});

Pytanie skąd wziąć CounterDao. Tutaj niestety ciężko o łatwe pokazanie dobrej praktyki komuś, kto się dopiero uczy. Powyższy kod też ma ten problem, że po sobie nie sprząta. Można to na szczęście dosyć łatwo naprawić usuwając callback w onDestroy(). Inny kłopot jest taki, że powoli zaczyna robić się "callback hell". Czyli masz zagnieżdżony jeden callback w drugim np. w counterDao.onCountersUpdate(). Gdyby asynchronicznych opracji było więcej w tym całym ciągu (np. zapytanie internetowe), to byłoby jeszcze gorzej. Radzić sobie z tym można na wiele sposobów, ale to inna inszość.

Jedna rzecz, którą wczesniej przeoczyłem. Niepotrzebnie rzutujesz wyniki findViewById(). Od API 26 metoda jest generyczna.

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