Komponenty architektury Android: Tworzenie aplikacji przy użyciu Room, LiveData i ViewModel

Autor: John Stephens
Data Utworzenia: 26 Styczeń 2021
Data Aktualizacji: 17 Móc 2024
Anonim
Dagger2 & Kotlin - Jak zyskać na użyciu Dependency Injection?  - Kotlin&Android #39
Wideo: Dagger2 & Kotlin - Jak zyskać na użyciu Dependency Injection? - Kotlin&Android #39

Zawartość


Z perspektywy użytkownika ta aplikacja będzie się składać z dwóch działań:

  • Główna aktywność. Ten ekran wyświetla listę zakupów użytkownika w RecylerView i zawiera ikonę paska akcji, która przenosi użytkownika z pierwszego ekranu do drugiego.
  • NewItemActivity. Ten ekran będzie zawierał tekst EditText, w którym użytkownik może wprowadzać elementy, oraz przycisk „Zapisz”, który pozwala mu zapisać każdy element na liście zakupów. Nowe elementy zostaną automatycznie dodane do RecylerView.

Na pierwszy rzut oka wydaje się to dość prostą aplikacją, ale za kulisami będziemy używać wielu komponentów architektury Android, aby zapewnić tę funkcjonalność w sposób solidny, łatwy w utrzymaniu i zgodny ze wszystkimi najnowszymi najlepszymi praktykami architektury Android.

Będziemy badać każdy komponent Architektury bardziej szczegółowo, jak je wdrażamy, ale na razie uzyskamy ogólny przegląd tego, jak wszystkie te komponenty pasują do siebie:


  • ViewModel. Za każdym razem, gdy zmienia się konfiguracja urządzenia, Android zareaguje, niszcząc bieżącą Aktywność lub Fragment, a następnie odtwarzając ją z nową konfiguracją. Jeśli przechowujesz dane aplikacji w komponencie interfejsu użytkownika, możesz potencjalnie utracić te dane za każdym razem, gdy zmienia się konfiguracja, w tym za każdym razem, gdy użytkownik przechodzi między trybem pionowym a poziomym. Aby nasze dane były oddzielone od interfejsu użytkownika aplikacji, będziemy przechowywać je w ViewModel, zaprojektowanym tak, aby trwał przy zmianach konfiguracji.
  • Repozytorium. Ta klasa zarządza co najmniej jednym źródłem danych, w tym usługami internetowymi, pamięciami podręcznymi i bazami danych Room.
  • Baza danych pokoi. Pokój zapewnia warstwę abstrakcji nad SQLite, pomagając w tworzeniu i utrzymywaniu lokalnej bazy danych SQLite, bez konieczność napisania tony kodu typu „kocioł”. Jeśli masz wcześniejsze doświadczenie z SQLite, Room zarządza wszystkimi zadaniami, które zwykle wykonywałbyś w klasie SQLiteOpenHelper.
  • DAO (Data Access Object). Zawiera metody, których będziesz używać, aby uzyskać dostęp do bazy danych pokoju.
  • Jednostka. Jest to klasa z adnotacjami, która reprezentuje tabelę w bazie danych. W naszej aplikacji każdy element dodawany przez użytkownika do listy zakupów będzie reprezentowany jako instancja klasy encji.

W naszej aplikacji przełoży się to na następujące klasy:


Tworzenie Obserwowalnych z uwzględnieniem cyklu życia, z LiveData

The Dane na żywo obserwowalna klasa posiadacza danych to Komponent architektury Androida, który zasługuje na specjalną wzmiankę, ponieważ pozwoli nam automatycznie aktualizować nasz RecylerView, gdy pojawią się nowe dane, tj. za każdym razem, gdy użytkownik doda nowy element do swojej listy zakupów.

Zamiast pisać logikę przetwarzania nowych danych, możemy dostosować nasze metody DAO, aby zwracały obiekty LiveData. Obiekty LiveData obsługują wzorzec obserwatora, dzięki czemu możemy używać tych obiektów do tworzenia relacji obserwator / obserwowalny, w których odpowiedni obserwator jest powiadamiany o zmianach w danych bazowych. W ten sposób możemy zapewnić, że nasza aplikacja zostanie powiadomiona, gdy pojawi się nowy element, który należy dodać do RecylerView.

Usługa LiveData ma również funkcję rozpoznawania cyklu życia, dzięki czemu można jej używać bez obawy o wycieki pamięci lub awarie aplikacji, które mogą wystąpić podczas próby aktualizacji nieaktywnego komponentu. LiveData automatycznie zatrzyma się i wznowi obserwację w zależności od stanu obserwowanej aktywności, fragmentu lub usługi, więc zawsze powiadamia komponenty, gdy znajdują się w aktywnym stanie cyklu życia (ROZPOCZĘCIE lub WZNOWANIU).

Wreszcie, chociaż nie będziemy go badać w tym artykule, jeśli jesteś fanem popularnej biblioteki RxJava, możesz użyć RxJava zamiast LiveData - po prostu upewnij się, że niszczysz swoje strumienie, gdy nie są już potrzebne! Alternatywnie możesz użyć LiveData obok RxJava, dodając LiveDataReactiveStreams do swojego projektu.

Tworzenie projektu komponentu architektury Android

Room, ViewModel, DAO, LiveData - mamy wiele do omówienia, więc stwórz projekt na Androida za pomocą szablonu „Pusta aktywność” i zacznijmy!

Dodawanie bibliotek Room, ViewModel i LiveData

Otwórz plik build.gradle na poziomie modułu i dodaj wszystkie biblioteki komponentów architektury, których będziemy używać w tym projekcie:

zależności {implement fileTree (reż: libs, include:) implementacja com.android.support:appcompat-v7:28.0.0 implementacja com.android.support.constraint: układ ograniczeń: 1.1.3 testImplementation junit: junit: 4.12 androidTestImplementation com .android.support.test: runner: 1.0.2 androidTestImplementation com.android.support.test.espresso: espresso-core: implementacja 3.0.2 com.android.support:design:28.0.0 // Pokój // wdrożenie "android .arch.persistence.room: runtime: 1.1.1 „adnotationProcessor” android.arch.persistence.room:compiler:1.1.1 „androidTestImplementation” android.arch.persistence.room:testing:1.1.1 "// ViewModel i LiveData // implementacja „android.arch.lifecycle: extensions: 1.1.1„ adnotationProcessor ”android.arch.lifecycle: compiler: 1.1.1”}

Tworzenie encji elementu

Każdy element na liście zakupów użytkownika będzie reprezentowany przez encję, dlatego musimy utworzyć klasę encji i użyć adnotacji, aby Room wiedział, które części tej klasy odpowiadają encji w bazie danych:

  • Utwórz nową klasę Java o nazwie „Element”.
  • Oznacz tę klasę jako byt za pomocą adnotacji @Entity:

@Entity public class Item {}

  • Następnie musimy nadać tabeli nazwę w bazie danych, używając właściwości „tableName”. Jeśli nie podasz nazwy, Room domyślnie użyje nazwy klasy:

@Entity (tableName = "item_table") klasa publiczna pozycja {}

  • Każda jednostka musi zdefiniować co najmniej jedno pole jako swój klucz podstawowy, używając adnotacji @PrimaryKey. Aby wszystko było prostsze, każdy element w naszej bazie danych będzie służył jako własny klucz podstawowy:

@Entity (tableName = "item_table") klasa publiczna Przedmiot {@PrimaryKey}

  • Następnie musimy użyć adnotacji @NonNull, aby wyjaśnić, że nasza wartość zwrotna nigdy nie może być pusta:

@Entity (tableName = "item_table") public class Item {@PrimaryKey @NonNull}

  • Domyślnie Room używa nazw pól jako nazw kolumn. Alternatywnie możesz podać inną nazwę kolumny, używając adnotacji @ColumnInfo:

@Entity (tableName = "item_table") public class Item {@PrimaryKey @NonNull @ColumnInfo (name = "item")}

  • Następnie dodaj konstruktor, który przyjmuje jako argument „item”:

prywatny ciąg mItem; element publiczny (element @NonNull String) {this.mItem = item;}

  • Wreszcie, musimy stworzyć metodę „gettera” dla naszej klasy jednostek; Używam „getItem”. Twoja ukończona klasa encji powinna wyglądać mniej więcej tak:

import android.arch.persistence.room.ColumnInfo; import android.arch.persistence.room.Entity; import android.support.annotation.NonNull; import android.arch.persistence.room.PrimaryKey; @Entity (tableName = "item_table") public class Item {@NonNull @PrimaryKey @ColumnInfo (name = "item") private String mItem; public item (@NonNull String item) {this.mItem = item;} public String getItem () {return this.mItem;}}

Wstawianie elementów do bazy danych: tworzenie DAO

Obiekt Data Access Object (DAO) to klasa z adnotacjami, która zapewnia abstrakcyjny dostęp do naszej bazy danych Room poprzez mapowanie zapytań SQL na nasze metody Java.

Jest kilka wygodnych zapytań, których możesz użyć w klasie DAO, ale w tym artykule skupimy się na @Insert. Gdy adnotujesz metodę za pomocą @Insert, Room generuje implementację, która wstawia wszystkie parametry do bazy danych w jednej transakcji - o ile wszystkie parametry są adnotacjami @Entity.

Będziemy również używać @Query, która jest metodą, która pozwala nam wykonywać operacje odczytu / zapisu w bazie danych. Chociaż nie będziemy go badać w tym artykule, możesz również użyć @Query do filtrowania danych, przekazując parametry metody do zapytań.

Wreszcie, ponieważ chcemy, aby interfejs naszej aplikacji był aktualizowany automatycznie, gdy pojawią się nowe dane, w opisie metody @Query będziemy używać typu zwracanej wartości LiveData.

Aby utworzyć DAO:

  • Utwórz nowy interfejs o nazwie „Itemexe” (wybierając „Nowy> Klasa Java”, a następnie otwierając menu rozwijane „Rodzaj” i wybierając „Interfejs”).
  • Otwórz ItemDAO i zaznacz ten interfejs jako DAO, używając adnotacji @do:

import android.arch.persistence.room.exe; Publiczny interfejs @soiretoe {

  • Utwórz metodę o nazwie „getItemList ()” i opatrz ją adnotacją za pomocą zapytania SQL, które pobierze wszystkie elementy z „tabeli_pozycji”.

// Zdobądź wszystkie przedmioty // @Query („SELECT * from item_table”) <>> getItemList ();

  • Następnie musimy dostosować opis metody @Query, aby wynik był zwracany jako obiekt LiveData:

@Query („SELECT * from item_table”) // Dodaj typ wartości „LiveData” do naszego opisu metody // LiveData<>> getItemList ();

  • Naszym kolejnym zadaniem jest zadeklarowanie metody wstawienia elementu za pomocą adnotacji @Insert:

@Insert void insert (Element pozycji);

  • W naszej metodzie @Insert musimy określić strategię konfliktu, aby nasza aplikacja wiedziała, jak obsługiwać zduplikowane dane, na przykład, jeśli użytkownik spróbuje dwukrotnie dodać „Jabłka” do swojej listy zakupów. Użyję OnConflictStrategy.REPLACE, który zastąpi starszą część danych nowszą, zduplikowaną częścią danych, ale alternatywnie możesz użyć OnConflictStrategy.ABORT, OnConflictStrategy.FAIL, OnConflictStrategy.IGNORE lub OnConflictBACKategy.ROL.

@Insert (onConflict = OnConflictStrategy.REPLACE) void insert (Element pozycji);

Po wykonaniu wszystkich powyższych kroków gotowy kod powinien wyglądać mniej więcej tak:

import android.arch.lifecycle.LiveData; import android.arch.persistence.room.exe; import android.arch.persistence.room.Insert; import android.arch.persistence.room.OnConflictStrategy; import android.arch.persistence.room.Query; import java.util.List; Publiczny interfejs @soh. Itemexe {// Adnotuj metodę insert () // @Insert (onConflict = OnConflictStrategy.REPLACE) void insert (pozycja elementu); @Query („SELECT * from item_table”) <>> getItemList (); }

Jeśli używasz RxJava, metoda Room @Query obsługuje również zwracane wartości Observable, Publisher i Flowble.

SQLite upraszcza: tworzenie bazy danych za pomocą Room

Room to biblioteka mapowania obiektów SQLite, która pozwala przechowywać dane lokalnie na urządzeniu użytkownika, dzięki czemu mogą uzyskać do nich dostęp bez względu na stan swojego połączenia internetowego. Nawet jeśli użytkownik robi mieć aktywne połączenie z Internetem, lokalnie przechowując dane, dzięki czemu aplikacja nie marnuje przepustowości lub baterii urządzenia, wielokrotnie pobierając te same dane.

Praca z SQLite może być złożona, ale Room korzysta z DAO w celu wysyłania zapytań do bazy danych SQLite, co znacznie zmniejsza złożoność pracy z surowymi tabelami SQL.

Zobaczmy, jak łatwo jest utworzyć bazę danych pokoi:

  • Utwórz nową klasę Java o nazwie „ItemRoomDatabase”.
  • Otwórz tę klasę i oznacz ją jako bazę danych Pokoju, używając adnotacji @Database:

import android.arch.persistence.room.Database; @Database public class ItemRoomDatabase {}

  • Następnie musimy zadeklarować podmiot należący do tej bazy danych, którym w tym przypadku jest klasa Item. W tej deklaracji określam również, że jest to wersja 1 naszej bazy danych i że nie chcemy eksportować schematu bazy danych:

import android.arch.persistence.room.Database; // Lista podmiotów powiązanych z tą bazą danych // @Database (podmioty = {Item.class}, wersja = 1, exportSchema = false) klasa publiczna ItemRoomDatabase {}

  • Następnie zadeklaruj, że ItemRoomDatabase jest abstrakcyjny i że rozszerza RoomDatabase:

import android.arch.persistence.room.Database; import android.arch.persistence.room.RoomDatabase; @Database (podmioty = {Item.class}, wersja = 1, exportSchema = false) publiczna klasa abstrakcyjna ItemRoomDatabase rozszerza RoomDatabase {}

  • Następnie musimy odwołać się do naszego DAO; którym w tym przypadku jest klasa Itemdę:

import android.arch.persistence.room.Database; import android.arch.persistence.room.RoomDatabase; @Database (podmioty = {Element.klasa}, wersja = 1, exportSchema = fałsz) publiczna klasa abstrakcyjna ItemRoomDatabase rozszerza RoomDatabase {public abstract Itemexe itemexe (); }

  • Wreszcie możemy utworzyć nasz obiekt RoomDatabase, który nazwiemy „item_list_database”:

import android.content.Context; import android.arch.persistence.room.Database; import android.arch.persistence.room.Room; import android.arch.persistence.room.RoomDatabase; @Database (podmioty = {Item.class}, wersja = 1, exportSchema = false) publiczna klasa abstrakcyjna ItemRoomDatabase rozszerza RoomDatabase {prywatny statyczny ItemRoomDatabase INSTANCE; public abstract Item static ItemRoomDatabase getDatabase (końcowy kontekst kontekstowy) {if (INSTANCE == null) {synchronized (ItemRoomDatabase.class) {if (INSTANCE == null) {// Uzyskaj instancję bazy danych // INSTANCE = Room.databaseBuilder (kontekst. getApplicationContext (), ItemRoomDatabase.class, „item_list_database”) .build (); }}} zwróć INSTANCE; }}

Nigdy więcej SQLiteOpenHelper: Tworzenie repozytorium

Repozytorium to klasa, która obsługuje operacje na danych i zarządza zadaniami, które zwykle wykonywałbyś w osobnej klasie SQLiteOpenHelper.

Room może zarządzać danymi z wielu źródeł, ale w naszej aplikacji będziemy pobierać dane z jednej bazy danych Room:

  • Utwórz nową klasę Java o nazwie „ItemRepository”.
  • Dodaj zmienne składowe dla naszych DAO i LiveData:

import android.arch.lifecycle.LiveData; import java.util.List; klasa publiczna ItemRepository {private Itemresent myItemsashi; prywatne LiveData<>> itemsList; }

  • Następnie dodaj konstruktor, który pobiera uchwyt do bazy danych i inicjuje zmienne składowe:

ItemRepository (aplikacja aplikacji) {ItemRoomDatabase database = ItemRoomDatabase.getDatabase (aplikacja); myItemsexe = database.itemexe (); itemsList = myItemsexe.getItemList (); }}

  • Musimy teraz dodać metodę opakowania, która zwraca elementy jako LiveData:

Dane na żywo<>> getAllItems () {return itemsList; }

  • Aby upewnić się, że nie blokujemy najważniejszego głównego wątku interfejsu użytkownika Androida, wywołam metodę insert () w wątku innym niż interfejs użytkownika, używając AsyncTask:

public void insert (Item item) {new newAsyncTask (myItemsexe) .execute (item); }

  • Zaimplementuj insertAsyncTask jako klasę wewnętrzną, a Twój ukończony kod powinien wyglądać mniej więcej tak:

import android.app.Application; import android.arch.lifecycle.LiveData; import android.os.AsyncTask; import java.util.List; klasa publiczna ItemRepository {private Itemresent myItemsashi; prywatne LiveData<>> itemsList; Dane na żywo<>> getAllItems () {return itemsList; } public void insert (Item item) {// Uruchom na wątku w tle // new newAsyncTask (myItemsexe) .execute (item); } prywatna klasa statyczna newAsyncTask rozszerza AsyncTask {private Itemresent myAsyncexe; newAsyncTask (Itemexe dao) {myAsyncexe = dao; } @Override chroniony Void doInBackground (pozycja końcowa ... parametry) {myAsyncresent.insert (parametry); zwraca null; }} ItemRepository (aplikacja aplikacji) {ItemRoomDatabase database = ItemRoomDatabase.getDatabase (aplikacja); myItemsexe = database.itemexe (); itemsList = myItemsexe.getItemList (); }}

Przetrwanie zmian w konfiguracji: tworzenie ViewModel

Następnie musimy utworzyć klasę ViewModel, która będzie zawierać i zarządzać wszystkimi danymi związanymi z interfejsem użytkownika aplikacji oraz zapewnia warstwę komunikacyjną między repozytorium a interfejsem aplikacji.

Obiekty ViewModel są zaprojektowane tak, aby przetrwać zmiany konfiguracji, więc nawet jeśli Android zniszczy, a następnie odtworzy Twoją Aktywność lub Fragment, dane ViewModel będą natychmiast dostępne dla następnej instancji tej Aktywności lub Fragmentu.

Ponieważ system zachowuje instancję ViewModel w całym cyklu życia aplikacji, ważne jest, aby Twój ViewModel nie zawierał żadnych bezpośrednich odniesień do działań, fragmentów lub widoków, ponieważ może to spowodować wyciek po zmianie konfiguracji.

Aby nasze obiekty LiveData mogły przetrwać zmiany konfiguracji Androida, przechowujemy je w naszym ViewModel.

Zaimplementujmy wszystkie powyższe, w klasie ViewModel:

  • Utwórz nową klasę „ItemViewModel”.
  • Upewnij się, że ItemViewModel rozszerza AndroidViewModel:

import android.arch.lifecycle.AndroidViewModel; klasa publiczna ItemViewModel rozszerza AndroidViewModel {}

  • Następnie dodaj prywatne zmienne składowe, aby zawierały odwołanie do repozytorium i listy elementów:

import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.LiveData; import java.util.List; klasa publiczna ItemViewModel rozszerza AndroidViewModel {private ItemRepository myRepository; prywatne LiveData<>> allItems; }

  • Dodaj konstruktor, który pobiera odwołanie do repozytorium i pobiera jego listę elementów:

public ItemViewModel (aplikacja) {super (aplikacja); myRepository = new ItemRepository (aplikacja); allItems = myRepository.getAllItems (); }

  • Aby włączyć składnik LiveData do naszej aplikacji, musimy dołączyć typ pola „LiveData” do naszego ViewModel. Dodam również metodę „getter”, która pobierze naszą listę przedmiotów:

Dane na żywo<>> getAllItems () {return allItems; }

  • Na koniec utwórz metodę wrapper insert (), która wywołuje metodę insert () Repozytorium:

public void insert (Item item) {myRepository.insert (item); }

Twój gotowy kod powinien wyglądać mniej więcej tak:

import android.app.Application; import android.arch.lifecycle.AndroidViewModel; import android.arch.lifecycle.LiveData; import java.util.List; klasa publiczna ItemViewModel rozszerza AndroidViewModel {private ItemRepository myRepository; prywatne LiveData<>> myAllItems; public ItemViewModel (aplikacja) {super (aplikacja); myRepository = new ItemRepository (aplikacja); myAllItems = myRepository.getAllItems (); } Dane na żywo<>> getAllItems () {return myAllItems; } public void insert (item item) {myRepository.insert (item); }}

Wyświetlanie naszych danych architektury Androida: Tworzenie RecyclerView

W tym momencie wdrożyliśmy wszystkie komponenty architektury Androida, które musimy przechowywać lokalnie, ale jeśli użytkownik kiedykolwiek zamierza widzieć dowolne z tych danych, musimy utworzyć interfejs użytkownika.

Wyświetlę dane jako nieskończenie przewijany RecylerView, więc nie ma ograniczeń co do liczby elementów, które użytkownik może dodać do swojej listy zakupów! Aby powstrzymać ten artykuł przed wymknięciem się spod kontroli, zamierzam przejrzeć proces wdrażania RecyclerView, ale jeśli chcesz uzyskać więcej informacji, zapoznaj się z naszym artykułem „Jak korzystać z widoków recyklera”.

Utwórz nowy plik res / layout o nazwie „item_layout” i dodaj:

Następnie utwórz nowy plik res / layout o nazwie „my_recylerview” i zaimplementuj komponent RecylerView:

Otwórz plik activity_main.xml i dodaj RecylerView do swojego układu, używając etykietka. Gdy tu jestem, dodaję też kilka innych elementów interfejsu:

// Dołącz RecylerView //

Następnym krokiem jest utworzenie adaptera, który rozszerza klasę RecylerView.Adapter:

import android.content.Context; import android.view.LayoutInflater; import android.support.v7.widget.RecyclerView; import android.widget.TextView; import android.view.View; import android.view.ViewGroup; import java.util.List; klasa publiczna ItemListAdapter rozszerza RecyclerView.Adapter {class ItemViewHolder rozszerza RecyclerView.ViewHolder {prywatny końcowy TextView textView; private ItemViewHolder (View itemView) {super (itemView); textView = itemView.findViewById (R.id.textView); }} prywatna lista moje rzeczy; prywatny końcowy LayoutInflater myInflater; ItemListAdapter (kontekst kontekstowy) {myInflater = LayoutInflater.from (kontekst); } @Override public ItemViewHolder onCreateViewHolder (nadrzędny ViewGroup, int viewType) {View itemView = myInflater.inflate (R.layout.item_layout, parent, false); zwraca nowy ItemViewHolder (itemView); } @Override public void onBindViewHolder (uchwyt ItemViewHolder, pozycja int) {Element current = myItems.get (pozycja); holder.textView.setText (current.getItem ()); } @Override public int getItemCount () {if (myItems! = Null) return myItems.size (); w przeciwnym razie zwraca 0; } void setItems (List items) {myItems = items; replaceDataSetChanged (); }}

Teraz wykonaliśmy całą pracę w tle, otwórz MainActivity i dodaj RecylerView do metody onCreate () aplikacji:

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; klasa publiczna MainActivity rozszerza AppCompatActivity {@Override chroniony void onCreate (pakiet saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_main); RecyclerView myRecyclerView = findViewById (R.id.recyclerview); final ItemListAdapter myAdapter = nowy ItemListAdapter (this); myRecyclerView.setAdapter (myAdapter); myRecyclerView.setLayoutManager (nowy LinearLayoutManager (this)); }}

Budowanie interfejsu użytkownika: ostatnie poprawki

Chociaż dostarczanie podstawowej funkcjonalności naszej aplikacji nie jest konieczne, stosuję kilka stylów w interfejsie użytkownika. Otwórz plik styles.xml i dodaj następujące elementy:

Zastosuj te style do swojej aplikacji, otwierając manifest i zmieniając atrybuty motywu android: theme, aby odwoływały się do tych nowych stylów:

Testowanie twojego projektu

Zainstaluj ten projekt na smartfonie lub tablecie z Androidem lub Android Virtual Device (AVD). RecylerView powinien się załadować, ale jest pewien haczyk: nie ma żadnych danych do wyświetlenia!

Następnym krokiem jest utworzenie działania, w którym użytkownik może dodawać elementy do bazy danych Pokoju.

Tworzenie wprowadzania danych Działanie

Zacznij od utworzenia nowego działania o nazwie NewItemActivity i odpowiedniego pliku zasobów układu „activity_add_item.xml”.

W pliku „activity_add_item.xml” dodam tekst EditText, w którym użytkownik może wpisywać różne elementy, oraz przycisk „Zapisz”, aby mogli zapisać każdy element na swojej liście zakupów.

To daje nam następujący układ:

Następnie otwórz swoją klasę NewItemActivity i powiedz jej, aby napompowała układ „activity_add_item”:

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; klasa publiczna NewItemActivity rozszerza AppCompatActivity {@Override public void onCreate (pakiet saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_add_item); }}

Nie zapomnij dodać NewItemActivity do manifestu:

Dodawanie nawigacji: Tworzenie ikony paska akcji

Następnie dodam ikonę paska akcji, która pozwoli użytkownikowi nawigować z MainActivity do NewItemActivity.

Ikony paska działań definiuje się w pliku zasobów menu, który znajduje się w katalogu „res / menu”. Jeśli twój projekt nie zawiera katalogu „menu”, musisz go utworzyć:

  • Kliknij z wciśniętym klawiszem Control w folder „res” projektu i wybierz „Nowy> Katalog zasobów Androida”.
  • Otwórz menu „Typ zasobu” i wybierz „menu”.
  • „Nazwa katalogu” powinna zostać automatycznie zaktualizowana do „menu”, ale jeśli tak nie jest, musisz zmienić nazwę ręcznie. Kliknij OK."

Możesz teraz utworzyć plik zasobów menu:

  • Kliknij z wciśniętym klawiszem Control w folder „menu” projektu i wybierz „Nowy> Plik zasobów menu”.
  • Nazwij ten plik „my_menu”.
  • Kliknij OK."
  • Otwórz plik „my_menu.xml” i dodaj następujące elementy:

To menu odwołuje się do ciągu „add_item”, więc otwórz plik res / wartości / strings.xml swojego projektu i utwórz ten zasób:

RoomLiveData Demo Dodaj Przedmiot

Następnie musimy utworzyć ikonę „add_item” na pasku akcji:

  • Wybierz „Plik> Nowy> Zasób obrazu” z paska narzędzi Android Studio.
  • Ustaw menu rozwijane „Typ ikony” na „Pasek akcji i ikony kart”.
  • Kliknij przycisk „Clip Art”.
  • Wybierz rozkładaną; Używam „dodaj krąg”.

  • Kliknij OK."
  • Aby upewnić się, że ikona paska akcji wyróżnia się, otwórz menu „Motyw” i wybierz „HOLO_DARK”.
  • Nazwij tę ikonę „add_icon”.
  • „Kliknij„ Dalej ”, a następnie„ Zakończ ”.

Teraz stworzyliśmy ikonę paska akcji, wystarczy dodać ją do naszej MainActivity i powiedzieć naszej aplikacji, aby uruchamiała NewItemActivity za każdym razem, gdy użytkownik wchodzi w interakcję z tą ikoną:

import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Menu; import android.view.MenuItem; klasa publiczna MainActivity rozszerza AppCompatActivity {public static final int REQUEST_CODE = 1; @Override chronione void onCreate (pakiet saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_main); RecyclerView myRecyclerView = findViewById (R.id.recyclerview); final ItemListAdapter myAdapter = nowy ItemListAdapter (this); myRecyclerView.setAdapter (myAdapter); myRecyclerView.setLayoutManager (nowy LinearLayoutManager (this)); } @Override public boolean onCreateOptionsMenu (menu Menu) {getMenuInflater (). Inflate (R.menu.my_menu, menu); zwróć prawdę; } @Override public boolean onOptionsItemSelected (Element menuItem) {switch (item.getItemId ()) {case R.id.add_item: Intent intent = new Intent (MainActivity.this, NewItemActivity.class); startActivityForResult (intent, REQUEST_CODE); } return false; }}

Testowanie projektu: weź dwa!

Zainstaluj zaktualizowany projekt na urządzeniu z Androidem lub AVD i powinieneś zobaczyć ikonę paska akcji, która po dotknięciu uruchamia NewItemActivity. Obecnie możesz pisać w EditText, ale bez względu na to, ile razy naciśniesz przycisk „Zapisz”, żadne nowe elementy nie zostaną dodane do bazowej bazy danych.

W tej sekcji zamierzamy pobrać dane użytkownika z EditText, dodać je do naszej bazy danych Pokoju, a następnie wyświetlić ten nowy element w naszym RecylerView.

Pobieranie danych wejściowych użytkownika

Zacznijmy od najłatwiejszego zadania: zaktualizowania naszego NewItemActivity, aby za każdym razem, gdy użytkownik stuknie przycisk „Zapisz”, zawartość EditText zostanie umieszczona w Intencji i wysłana do naszej MainActivity.

Oto ukończona klasa NewItemActivity:

import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.content.Intent; import android.text.TextUtils; import android.view.View; klasa publiczna NewItemActivity rozszerza AppCompatActivity {private EditText myEditText; public static final String EXTRA = ".REPLY"; @Override public void onCreate (pakiet saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_add_item); myEditText = findViewById (R.id.add_item); ostatni przycisk Button = findViewById (R.id.save_item); button.setOnClickListener (nowy View.OnClickListener () {public void onClick (Wyświetl widok) {Intent reply = new Intent (); if (TextUtils.isEmpty (myEditText.getText ())) {setResult (RESULT_CANCELED, odpowiedź);} else {String item = myEditText.getText (). ToString (); reply.putExtra (EXTRA, item); setResult (RESULT_OK, odpowiedz);} finish ();}}); }}

Aktualizacja bazy danych pokoju

Za każdym razem, gdy użytkownik stuknie „Zapisz”, musimy dodać jego dane wejściowe do bazy danych, a następnie użyć LiveData, aby zaktualizować nasz RecylerView.

Aby rozpocząć, otwórz klasę MainActivity i dodaj ViewModel do metody onCreate ():

myItemViewModel = ViewModelProviders.of (this) .get (ItemViewModel.class);

Pamiętaj, że system może wywoływać metodę onCreate () wiele razy w całym cyklu życia działania, na przykład po zmianie konfiguracji. Za każdym razem, gdy działanie zostanie zniszczone, a następnie ponownie utworzone, otrzyma tę samą instancję myItemViewModel. Ponieważ ViewModel jest przywracany automatycznie, nie musimy dodawać żadnej logiki do obsługi zmian konfiguracji.

Następnie musimy pobrać dane użytkownika, dodając wywołanie zwrotne onActivityResult () dla NewItemActivity:

public void onActivityResult (int requestCode, int resultCode, Intent data) {super.onActivityResult (requestCode, resultCode, dane);

Jeśli działanie zwróci RESULT_OK, wówczas wywołamy metodę insert () i wstawimy zwracany element do bazy danych:

if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {Element item = nowy element (data.getStringExtra (NewItemActivity.EXTRA)); myItemViewModel.insert (item); }

Teraz użytkownik może dodawać nowe elementy do bazy danych, musimy upewnić się, że nasze aktualizacje RecylerView odzwierciedlają te zmiany.

Ponieważ używamy LiveData, każdy dodatek do bazy danych spowoduje wywołanie zwrotne onChanged (). Możemy użyć tego wywołania zwrotnego, aby powiedzieć naszej aplikacji, jak zareagować na te zmiany bazy danych:

@Override public void onChanged (@Nullable final List items) {// Zaktualizuj interfejs użytkownika // myAdapter.setItems (items); }

Następnie musimy utworzyć relację Observer / Observable, dołączając obiekt Observer do obiektu LiveData przy użyciu metody observ ():

myItemViewModel.getAllItems (). observ (to, nowy Observer<>>() {

W tym momencie obiekt Observer subskrybuje obiekt LiveData i będzie otrzymywać powiadomienie za każdym razem, gdy zmieni się dane bazowe.

Ponieważ LiveData ma świadomość cyklu życia, wywoła funkcję zwrotną onChanged () tylko wtedy, gdy działanie jest w stanie aktywnym, więc nie musimy używać metody onStop (), aby powiedzieć naszej aplikacji, aby przestała obserwować dane.

Po zaimplementowaniu tego wszystkiego w MainActivity kod powinien wyglądać mniej więcej tak:

import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.os.Bundle; import android.content.Intent; import android.view.Menu; import android.view.MenuItem; import android.arch.lifecycle.Observer; import android.arch.lifecycle.ViewModelProviders; import java.util.List; import android.support.annotation.Nullable; klasa publiczna MainActivity rozszerza AppCompatActivity {public static final int REQUEST_CODE = 1; private ItemViewModel myItemViewModel; @Override chronione void onCreate (pakiet saveInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_main); Pasek narzędzi myToolbar = (Pasek narzędzi) findViewById (R.id.toolbar); setSupportActionBar (myToolbar); RecyclerView myRecyclerView = findViewById (R.id.recyclerview); final ItemListAdapter myAdapter = nowy ItemListAdapter (this); myRecyclerView.setAdapter (myAdapter); myRecyclerView.setLayoutManager (nowy LinearLayoutManager (this)); // Pobierz ViewModel // myItemViewModel = ViewModelProviders.of (this) .get (ItemViewModel.class); // Obserwuj LiveData i dostarczaj powiadomienie za każdym razem, gdy dane się zmienią // myItemViewModel.getAllItems (). Zaobserwuj (to, nowy obserwator<>> () {@Override public void onChanged (@Nullable final List items) {// Zaktualizuj interfejs użytkownika // myAdapter.setItems (items); }}); } @Override public boolean onCreateOptionsMenu (menu Menu) {getMenuInflater (). Inflate (R.menu.my_menu, menu); zwróć prawdę; } @Override public boolean onOptionsItemSelected (Element menuItem) {switch (item.getItemId ()) {case R.id.add_item: Intent intent = new Intent (MainActivity.this, NewItemActivity.class); startActivityForResult (intent, REQUEST_CODE); } return false; } public void onActivityResult (int requestCode, int resultCode, Intent data) {super.onActivityResult (requestCode, resultCode, dane); if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {Element item = nowy element (data.getStringExtra (NewItemActivity.EXTRA)); myItemViewModel.insert (item); }}}

Testowanie ukończonej aplikacji

Powinieneś być teraz w stanie dodawać elementy do bazy danych Pokoju i widzieć, jak pojawiają się automatycznie w RecylerView. Przetestujmy to:

  • Zainstaluj zaktualizowany projekt na smartfonie, tablecie lub AVD z Androidem.
  • Stuknij ikonę paska akcji.
  • Wprowadź element do EditText.
  • Stuknij „Zapisz”, aby wrócić do MainActivity, gdzie RecylerView powinien zostać zaktualizowany, aby uwzględnić Twój nowy element.
  • Opłucz i powtórz, aby dodać więcej pozycji do listy.

Aby sprawdzić, czy Twoje dane nie znikają między sesjami, zamknij aplikację, a następnie uruchom ją ponownie; aplikacja powinna zostać uruchomiona z zapełnioną listą.

Powinieneś także przetestować, w jaki sposób aplikacja obsługuje zduplikowane dane. Ponieważ używamy strategii konfliktu REPLACE, przy każdej próbie dwukrotnego wprowadzenia tego samego elementu aplikacja usunie oryginalny fragment danych, a następnie zastąpi go „nowymi” danymi. Spróbuj dodać element, który już istnieje na liście, aby sprawdzić, czy aplikacja naprawdę zastępuje starą nową.

Możesz także pobrać ukończoną aplikację z GitHub.

Podsumowując

W tym artykule zapoznaliśmy się z kilkoma kluczowymi komponentami architektury systemu Android i zobaczyliśmy, w jaki sposób mogą pomóc rozwiązać wiele problemów często spotykanych przez programistów Androida, w tym trwałość danych, zarządzanie cyklem życia i unikanie wycieków pamięci.

Czy są jakieś inne typowe problemy, które chciałbyś zobaczyć w adresie komponentów architektury Androida? Daj nam znać w komentarzach poniżej!

Od Man in the High Catle po Bocha, itnieje wiele do polubienia na temat Amazon Prime Video i jego oferty. Co więcej, możez włączyć lub zmienić napiy do Amazon Prime Video oraz zmienić język program...

Pixel 4 treść jet dotarczana przez MNML Cae, twórcę najcieńzej obudowy telefonu na świecie. Zaozczędź 25% na obudowie Pixel 4 lub Pixel 4 XL, korzytając z kodu rabatowego AAPixel4....

Fascynujące Artykuły