天天看點

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

本文解決了典型的任務: 在應用程式中存儲資料 - 使用Room 向使用者顯示資料 - 使用片段和recyclerview 存儲并使用自動更新資料 ViewModel 背景 Room提供了一個覆寫SQLite的抽象層,以便在利用SQLite的全部功能的同時進行流暢的資料庫通路。 該應用程式使用 Ro

本文解決了典型的任務:

在應用程式中存儲資料 - 使用Room

向使用者顯示資料 - 使用片段和recyclerview

存儲并使用自動更新資料ViewModel

背景

Room提供了一個覆寫SQLite的抽象層,以便在利用SQLite的全部功能的同時進行流暢的資料庫通路。該應用程式使用Room資料庫來擷取與該資料庫關聯的資料通路對象或DAO。然後,應用程式使用每個DAO從資料庫中擷取實體,并将對這些實體的任何更改儲存回資料庫。最後,應用程式使用實體來擷取和設定與資料庫中的表列對應的值。

在RecyclerView視窗小部件中,幾個不同的元件一起工作以顯示您的資料(對象清單)。使用者界面的整個容器是RecyclerView您添加到布局的對象。該RecyclerView由您提供的布局管理器提供的意見罷了本身。您可以使用我們的标準布局管理器(例如LinearLayoutManager或GridLayoutManager),或實作您自己的。

ViewModel是一個負責準備和管理活動或片段資料的類。它還處理Activity / Fragment與應用程式其餘部分的通信。換句話說,這意味着ViewModel如果其所有者因配置更改(例如旋轉)而被銷毀,則不會銷毀該檔案。所有者的新執行個體将重新連接配接到現有執行個體ViewModel。

使用代碼

開始吧!在Android studio中建立一個新項目(我使用的是版本3.2.1),或者您可以下載下傳源檔案并選擇:File-New-Import項目。我們将建構一個這樣的應用程式:

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

您可以根據需要在資料庫中添加和删除資料并将其顯示在螢幕上。

我們需要資料類DataItem:

隐藏   複制代碼

public class DataItem {

private long id;

private String name;

private String content;

private String details;

private String section;

}

它是類 - 我們存儲在資料庫中的資料。為了顯示這些資料,我們使用RecyclerView小部件。建立新片段:File-New-Fragment-Fragment(清單)。RecyclerView使用兩個XML檔案:一個檔案表示清單項,第二個檔案表示項的完整清單。進行一些更改fragment_item:添加CardView小部件并制作自定義Textview元素。也增加了build.gradle對Cardview小部件:

隐藏   複制代碼

//for recyclerview items

implementation 'com.android.support:cardview-v7:28.0.0'

Fragment_item.xml:

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

要Textview使用數字進行紅色循環,我們需要round_shape.xml:

隐藏   複制代碼

并将background我們的值設定TextView為round_shape.xml:

隐藏   複制代碼

android:background="@drawable/round_shape"

添加按鈕到fragment_list.xml:

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

讓我們轉到資料庫。這個應用程式側重于成分即的一個子集LiveData,ViewModel和Room。此圖顯示了此體系結構的基本形式。你可以在這裡閱讀更多相關資訊。

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

添加一些build.gradleapp子產品:

隐藏   複制代碼

//Room components

implementation "android.arch.persistence.room:runtime:1.1.1"

annotationProcessor "android.arch.persistence.room:compiler:1.1.1"

androidTestImplementation "android.arch.persistence.room:testing:1.1.1"

//Lifecycle components

implementation "android.arch.lifecycle:extensions:1.1.1"

annotationProcessor "android.arch.lifecycle:compiler:1.1.1"

要使DataItem該類對Room資料庫有意義,您需要對其進行注釋。注釋辨別此類的每個部分如何與資料庫中的條目相關。Room使用此資訊生成代碼。

@Entity(tableName ="data_item_table")

每個@Entity類代表一個表中的實體。注釋您的類聲明以訓示它是一個實體。如果希望它與類的名稱不同,請指定表的名稱。

@PrimaryKey

每個實體都需要一個主鍵。為了簡單起見,每個單詞都作為自己的主鍵。

@NonNull

表示參數,字段或方法傳回值永遠不會null。

@ColumnInfo(name ="name")

如果希望它與成員變量的名稱不同,請在表中指定列的名稱。

存儲在資料庫中的每個字段都必須是public或具有“getter”方法。此示例提供了一種geName()方法。

将我們的課程改為:

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

@Entity

public class DataItem {

@PrimaryKey(autoGenerate = true)

@NonNull

private long id;

private String name;

private String content;

private String details;

private String section;

@Ignore

public DataItem(String name, String content, String details, String section) {

this.name = name;

this.content = content;

this.details = details;

this.section = section;

}

public void setId(long id) {

this.id = id;

}

public long getId() {

return id;

}

public String getName() {

return name;

}

public String getContent() {

return content;

}

public String getDetails() {

return details;

}

public void setName(String name) {

this.name = name;

}

public void setContent(String content) {

this.content = content;

}

public void setDetails(String details) {

this.details = details;

}

public String getSection() {

return section;

}

public void setSection(String section) {

this.section = section;

}

public DataItem() {

this.name = "name";

this.content = "content";

this.details = "details";

this.section = "section";

}

}

在DAO(資料通路對象)中,指定SQL查詢并将它們與方法調用相關聯。編譯器檢查SQL并從便捷注釋生成查詢以查找常見查詢,例如@Insert, @Delete,@Query。本DAO必須是一個interface或abstract類。預設情況下,所有查詢都必須在單獨的線程上執行。好的,做我們自己的DAO interface。

隐藏   複制代碼

@Dao

public interface DataDAO {

//Insert one item

@Insert(onConflict = IGNORE)

void insertItem(DataItem item);

//Delete one item

@Delete

void deleteItem(DataItem person);

//Delete one item by id

@Query("DELETE FROM dataitem WHERE id = :itemId")

void deleteByItemId(long itemId);

//Get all items

@Query("SELECT * FROM DataItem")

LiveData> getAllData();

//Delete All

@Query("DELETE FROM DataItem")

void deleteAll();

}

資料更改時,通常需要執行某些操作,例如在UI中顯示更新的資料。這意味着您必須觀察資料,以便在更改時,您可以做出反應。LiveData,一個用于資料觀察的生命周期庫類,解決了這個問題。LiveData在方法描述中使用type的傳回值,并Room生成所有必需的代碼以更新LiveData資料庫的更新時間。

接下來,根據建立資料庫類RoomDatabase:

隐藏   複制代碼

@Database(entities = {DataItem.class}, version = 1, exportSchema = false)

public abstract class DataRoomDbase extends RoomDatabase {

private static DataRoomDbase INSTANCE;

public abstract DataDAO dataDAO();

public static DataRoomDbase getDatabase(Context context) {

if (INSTANCE == null) {

INSTANCE = Room.databaseBuilder(context.getApplicationContext(),

DataRoomDbase.class, DataRoomDbase.class.getName())

//if you want create db only in memory, not in file

//Room.inMemoryDatabaseBuilder

//(context.getApplicationContext(), DataRoomDbase.class)

.build();

}

return INSTANCE;

}

public static void destroyInstance() {

INSTANCE = null;

}

}

接下來,建立存儲庫。ARepository是抽象通路多個資料源的類。該Repository不是體系結構元件庫的一部分,但對于代碼的分離和架構的建議最佳實踐。一Repository類處理資料的操作。它為應用程式資料的應用程式的其餘部分提供了一個幹淨的API。像這樣:

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

public class DataRepository {

private DataDAO mDataDao;

private LiveData> mAllData;

public DataRepository(Application application) {

DataRoomDbase dataRoombase = DataRoomDbase.getDatabase(application);

this.mDataDao = dataRoombase.dataDAO();

this.mAllData = mDataDao.getAllData();

}

LiveData> getAllData() {

return mAllData;

}

//You must call this on a non-UI thread or your app will crash

public void insert(DataItem dataItem) {

new insertAsyncTask(mDataDao).execute(dataItem);

}

private static class insertAsyncTask extends AsyncTask {

private DataDAO mAsyncTaskDao;

insertAsyncTask(DataDAO dao) {

mAsyncTaskDao = dao;

}

@Override

protected Void doInBackground(final DataItem... params) {

mAsyncTaskDao.insertItem(params[0]);

return null;

}

}

public void deleteItem(DataItem dataItem) {

new deleteAsyncTask(mDataDao).execute(dataItem);

}

private static class deleteAsyncTask extends AsyncTask {

private DataDAO mAsyncTaskDao;

deleteAsyncTask(DataDAO dao) {

mAsyncTaskDao = dao;

}

@Override

protected Void doInBackground(final DataItem... params) {

mAsyncTaskDao.deleteItem(params[0]);

return null;

}

}

public void deleteItemById(Long idItem) {

new deleteByIdAsyncTask(mDataDao).execute(idItem);

}

private static class deleteByIdAsyncTask extends AsyncTask {

private DataDAO mAsyncTaskDao;

deleteByIdAsyncTask(DataDAO dao) {

mAsyncTaskDao = dao;

}

@Override

protected Void doInBackground(final Long... params) {

mAsyncTaskDao.deleteByItemId(params[0]);

return null;

}

}

}

根據ViewModel類建立新類:

隐藏   複制代碼

public class DataViewModel extends AndroidViewModel {

private DataRepository mDataRepository;

private LiveData> mListLiveData;

public DataViewModel(@NonNull Application application) {

super(application);

mDataRepository = new DataRepository((application));

mListLiveData = mDataRepository.getAllData();

}

public LiveData> getAllData() {

return mListLiveData;

}

public void insertItem(DataItem dataItem) {

mDataRepository.insert(dataItem);

}

public void deleteItem(DataItem dataItem) {

mDataRepository.deleteItem(dataItem);

}

public void deleteItemById(Long idItem) {

mDataRepository.deleteItemById(idItem);

}

}

AViewModel以生命周期方式儲存應用程式的UI資料,以便在配置更改後繼續存在。将應用程式的UI資料與您的Activity和Fragment類分開可以讓您更好地遵循單一責任原則:您的活動和片段負責将資料繪制到螢幕,同時您ViewModel可以負責儲存和處理UI所需的所有資料。

在ViewModel,LiveData用于UI将使用或顯示的可變資料。使用LiveData有幾個好處:

您可以将觀察者放在資料上(而不是輪詢更改),隻在資料實際更改時更新UI。

的Repository和UI完全被分離ViewModel。沒有資料庫調用ViewModel,使代碼更易于測試。

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

public class MainActivity extends AppCompatActivity

implements ListFragment.OnListFragmentInteractionListener,

AlertDialogFragment.AlertDialogListener {

private DataViewModel mDataViewModel;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mDataViewModel = ViewModelProviders.of(this).get(DataViewModel.class);

getSupportFragmentManager()

.beginTransaction()

.replace(R.id.main_layout, new ListFragment())

.addToBackStack("list")

.commit();

}

@Override

public void onListClickItem(DataItem dataItem) {

Toast.makeText(this, dataItem.getDetails(), Toast.LENGTH_SHORT).show();

}

@Override

public void onListFragmentDeleteItemById(long idItem) {

Bundle bundle = new Bundle();

bundle.putLong(ID_LONG, idItem);

AlertDialogFragment alertDialogFragment = new AlertDialogFragment();

alertDialogFragment.setArguments(bundle);

alertDialogFragment.show(getSupportFragmentManager(), "Allert");

}

@Override

public void onDialogPositiveClick(DialogFragment dialog, long idItem) {

mDataViewModel.deleteItemById(idItem);

Toast.makeText(this, getString(R.string.message_delete), Toast.LENGTH_SHORT).show();

}

@Override

public void onDialogNegativeClick(DialogFragment dialog) {

Toast.makeText(this, getString(R.string.message_cancel), Toast.LENGTH_SHORT).show();

}

}

用于ViewModelProviders将您ViewModel與UI控制器關聯。當您的應用首次啟動時,ViewModelProviders将建立ViewModel。當活動被銷毀時,例如,通過配置更改,ViewModel持續存在。重新建立活動時,ViewModelProviders傳回現有的活動ViewModel。

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

public class ListFragment extends Fragment {

private DataViewModel viewModel;

private List mDataItemList;

private ListRecyclerViewAdapter mListAdapter;

private OnListFragmentInteractionListener mListener;

public void setListData(List dataItemList) {

//if data changed, set new list to adapter of recyclerview

if (mDataItemList == null) {

mDataItemList = new ArrayList<>();

}

mDataItemList.clear();

mDataItemList.addAll(dataItemList);

if (mListAdapter != null) {

mListAdapter.setListData(dataItemList);

}

}

@Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,

Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fragment_list, container, false);

Context context = view.getContext();

//set recyclerview

RecyclerView recyclerView = view.findViewById(R.id.list);

recyclerView.setLayoutManager(new LinearLayoutManager(context));

mListAdapter = new ListRecyclerViewAdapter(mListener);

if (mDataItemList != null) {

mListAdapter.setListData(mDataItemList);

}

recyclerView.setAdapter(mListAdapter);

//Add new item to db

Button addButton = (Button) view.findViewById(R.id.add_button);

addButton.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

viewModel.insertItem(new DataItem());

}

});

return view;

}

@Override

public void onActivityCreated(@Nullable Bundle savedInstanceState) {

super.onActivityCreated(savedInstanceState);

//get viewmodel

viewModel = ViewModelProviders.of(this).get(DataViewModel.class);

//bind to Livedata

viewModel.getAllData().observe(this, new Observer>() {

@Override

public void onChanged(@Nullable List dataItems) {

if (dataItems != null) {

setListData(dataItems);

}

}

});

}

@Override

public void onAttach(Context context) {

super.onAttach(context);

if (context instanceof OnListFragmentInteractionListener) {

mListener = (OnListFragmentInteractionListener) context;

} else {

throw new RuntimeException(context.toString()

+ "must implement OnListFragmentInteractionListener");

}

}

@Override

public void onDetach() {

super.onDetach();

mListener = null;

}

public interface OnListFragmentInteractionListener {

//onClick items of list

void onListClickItem(DataItem dataItem);

//onClick delete item from list

void onListFragmentDeleteItemById(long idItem);

}

}

另外,要顯示警告對話框,當您想要從資料庫中删除資料時,我建立了一個新類 -AlertDialogFragment(如果需要,請閱讀此内容)。這可以防止記憶體洩漏,并以一種能夠在配置更改後幸存的方式使用生命周期。

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...
android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

隐藏   收縮

android根據資料庫生成view,如何使用Room和Recyclerview以及ViewModel在Android中使用資料庫...

   複制代碼

public class AlertDialogFragment extends DialogFragment {

AlertDialogListener mListener;

public static String ID_LONG = "ID_LONG";

long id_data;

public interface AlertDialogListener {

void onDialogPositiveClick(DialogFragment dialog, long idItem);

void onDialogNegativeClick(DialogFragment dialog);

}

@Override

@NonNull

public Dialog onCreateDialog(Bundle savedInstanceState) {

Activity activity = getActivity();

Bundle bundle = getArguments();

if (bundle != null && activity != null) {

//get data from bundle

id_data = bundle.getLong(ID_LONG);

//Use the Builder class for convenient dialog construction

AlertDialog.Builder builder = new AlertDialog.Builder(activity);

builder.setMessage(R.string.message_dialog)

.setPositiveButton(R.string.message_yes, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

//set listener for yes button

mListener.onDialogPositiveClick(AlertDialogFragment.this, id_data);

}

})

.setNegativeButton(R.string.message_no, new DialogInterface.OnClickListener() {

public void onClick(DialogInterface dialog, int id) {

//User cancelled the dialog

mListener.onDialogNegativeClick(AlertDialogFragment.this);

}

});

//Create the AlertDialog object and return it

return builder.create();

}

//if no bundle - show error

AlertDialog.Builder builder = new AlertDialog.Builder(activity)

.setNegativeButton(R.string.message_error, new DialogInterface.OnClickListener() {

@Override

public void onClick(DialogInterface dialog, int which) {

}

});

return builder.create();

}

//Override the Fragment.onAttach() method to instantiate the DialogListener

@Override

public void onAttach(Context context) {

super.onAttach(context);

//Verify that the host activity implements the callback interface

try {

//Instantiate the DialogListener so we can send events to the host

mListener = (AlertDialogListener) context;

} catch (ClassCastException e) {

//The activity doesn't implement the interface, throw exception

throw new ClassCastException(context.toString()

+ "must implement AlertDialogListener");

}

}

}

我希望這篇簡單的文章可以幫到你。您可以輕松改進此應用程式。我喜歡開發應用程式,是以你可以在這裡嘗試其中的一些。