In this tutorial, we’ll be digging deep into the concept of ViewModel. We’ll be developing a Favourite Links Application in Android wherein we can save our favorite links onto the SQLite database. We’ll see how ViewModel helps us in building a robust app.

在本教程中,我们将深入研究ViewModel的概念。 我们将在Android中开发一个“收藏夹链接”应用程序,其中可以将我们喜欢的链接保存到SQLite数据库中。 我们将看到ViewModel如何帮助我们构建强大的应用程序。

Android ViewModel (Android ViewModel)

ViewModel is an Android architecture component. It is used as a data manager in the application.

ViewModel是一个Android体系结构组件。 它在应用程序中用作数据管理器。

Letting the Activity do the data handling isn’t a good idea. A ViewModel provides a way to create and retrieve objects. It typically stores the state of a view’s data and communicates with other components.

让Activity进行数据处理不是一个好主意。 ViewModel提供了一种创建和检索对象的方法。 它通常存储视图数据的状态并与其他组件通信。

It is handy when it comes to configuration changes and the Activity’s data gets erased. The configuration change doesn’t impact the ViewModel since it is not tied to the Activity completely. It is able to provide the data to the Activity again after it gets recreated.

当涉及到配置更改并且活动的数据被删除时,它很方便。 配置更改不会影响ViewModel,因为它没有完全绑定到Activity。 重新创建活动后,它可以再次将数据提供给活动。

Simple Rule: Don’t let your android classes handle everything. Especially not data.
简单规则:不要让您的android类处理所有事情。 特别是没有数据。

Android SQLite (Android SQLite)

We’ve discussed SQLite at depth in this tutorial. In that tutorial, we did all the SQLite handling in our Activity and made it a heavy class.

在本教程中,我们已经深入讨论了SQLite。 在该教程中,我们在Activity中完成了所有SQLite处理,并使其成为一个繁重的类。

Here we’ll do the SQLite querying in our ViewModel class.

在这里,我们将在ViewModel类中执行SQLite查询。

To recap: For adding data to SQLite database, we use a ContentValue which is like a data storage. We pass the data in the form of a key-value pair.

回顾一下:为了将数据添加到SQLite数据库,我们使用ContentValue,就像数据存储一样。 我们以键值对的形式传递数据。

To retrieve the data from the SQLite a Cursor object is used.

为了从SQLite检索数据,使用了一个Cursor对象。

In the following section, we’ll create an application that holds ListView records in an SQLite table.

在下一节中,我们将创建一个在SQLite表中保存ListView记录的应用程序。

You can add/delete your favorite web links to the ListView which would get updated in the database from the ViewModel.

您可以将喜爱的Web链接添加/删除到ListView ,该链接将从ViewModel在数据库中更新。

Android ViewModel教程项目结构 (Android ViewModel Tutorial Project Structure)

Add the following libraries in your app’s build.gradle file:

在应用程序的build.gradle文件中添加以下库:

implementation 'com.android.support:design:27.1.1'
implementation "android.arch.lifecycle:extensions:1.1.1"

Android ViewModel代码 (Android ViewModel Code)

The DB folder consists of the SQLite settings. The code for the DbSettings.java class is given below:

DB文件夹包含SQLite设置。 下面给出了DbSettings.java类的代码:

package com.journaldev.androidviewmodel.db;import android.provider.BaseColumns;public class DbSettings {public static final String DB_NAME = "favourites.db";public static final int DB_VERSION = 1;public class DBEntry implements BaseColumns {public static final String TABLE = "fav";public static final String COL_FAV_URL = "url";public static final String COL_FAV_DATE = "date";}
}

The code for the FavouritesDbHelper.java class is given below:

下面给出了FavouritesDbHelper.java类的代码:

package com.journaldev.androidviewmodel.db;import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;public class FavouritesDBHelper extends SQLiteOpenHelper {public FavouritesDBHelper(Context context) {super(context, DbSettings.DB_NAME, null, DbSettings.DB_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {String createTable = "CREATE TABLE " + DbSettings.DBEntry.TABLE + " ( " +DbSettings.DBEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +DbSettings.DBEntry.COL_FAV_URL + " TEXT NOT NULL, " +DbSettings.DBEntry.COL_FAV_DATE + " INTEGER NOT NULL);";db.execSQL(createTable);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS " + DbSettings.DBEntry.TABLE);onCreate(db);}}

Our database consists of three columns – ID, URL, DATE.

我们的数据库由三列组成-ID,URL,DATE。

The code for Favourites.java class is given below:

下面给出了Favourites.java类的代码:

package com.journaldev.androidviewmodel;public class Favourites {public long mId;public String mUrl;public long mDate;public Favourites(long id, String name, long date) {mId = id;mUrl = name;mDate = date;}public Favourites(Favourites favourites) {mId = favourites.mId;mUrl = favourites.mUrl;mDate = favourites.mDate;}}

The code for the FavouritesViewModel.java class is given below:

下面给出了FavouritesViewModel.java类的代码:

package com.journaldev.androidviewmodel;import android.app.Application;
import android.arch.lifecycle.AndroidViewModel;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;import com.journaldev.androidviewmodel.db.DbSettings;
import com.journaldev.androidviewmodel.db.FavouritesDBHelper;import java.util.ArrayList;
import java.util.Date;
import java.util.List;public class FavouritesViewModel extends AndroidViewModel {private FavouritesDBHelper mFavHelper;private ArrayList<Favourites> mFavs;FavouritesViewModel(Application application) {super(application);mFavHelper = new FavouritesDBHelper(application);}public List<Favourites> getFavs() {if (mFavs == null) {mFavs = new ArrayList<>();createDummyList();loadFavs();}ArrayList<Favourites> clonedFavs = new ArrayList<>(mFavs.size());for (int i = 0; i < mFavs.size(); i++) {clonedFavs.add(new Favourites(mFavs.get(i)));}return clonedFavs;}public void createDummyList() {addFav("https://www.journaldev.com", (new Date()).getTime());addFav("https://www.medium.com", (new Date()).getTime());addFav("https://www.reddit.com", (new Date()).getTime());addFav("https://www.github.com", (new Date()).getTime());addFav("https://www.hackerrank.com", (new Date()).getTime());addFav("https://www.developers.android.com", (new Date()).getTime());}private void loadFavs() {mFavs.clear();SQLiteDatabase db = mFavHelper.getReadableDatabase();Cursor cursor = db.query(DbSettings.DBEntry.TABLE,new String[]{DbSettings.DBEntry._ID,DbSettings.DBEntry.COL_FAV_URL,DbSettings.DBEntry.COL_FAV_DATE},null, null, null, null, null);while (cursor.moveToNext()) {int idxId = cursor.getColumnIndex(DbSettings.DBEntry._ID);int idxUrl = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_URL);int idxDate = cursor.getColumnIndex(DbSettings.DBEntry.COL_FAV_DATE);mFavs.add(new Favourites(cursor.getLong(idxId), cursor.getString(idxUrl), cursor.getLong(idxDate)));}cursor.close();db.close();}public Favourites addFav(String url, long date) {Log.d("API123", "addFav " + url);SQLiteDatabase db = mFavHelper.getWritableDatabase();ContentValues values = new ContentValues();values.put(DbSettings.DBEntry.COL_FAV_URL, url);values.put(DbSettings.DBEntry.COL_FAV_DATE, date);long id = db.insertWithOnConflict(DbSettings.DBEntry.TABLE,null,values,SQLiteDatabase.CONFLICT_REPLACE);db.close();Favourites fav = new Favourites(id, url, date);mFavs.add(fav);return new Favourites(fav);}public void removeFav(long id) {SQLiteDatabase db = mFavHelper.getWritableDatabase();db.delete(DbSettings.DBEntry.TABLE,DbSettings.DBEntry._ID + " = ?",new String[]{Long.toString(id)});db.close();int index = -1;for (int i = 0; i < mFavs.size(); i++) {Favourites favourites = mFavs.get(i);if (favourites.mId == id) {index = i;}}if (index != -1) {mFavs.remove(index);}}}

In the above code, we add, remove and load the data from the SQLite Database.

在上面的代码中,我们从SQLite数据库中添加,删除和加载数据。

In the getFavs() method we do a deep clone of the ArrayList.

getFavs()方法中,我们对ArrayList进行了深层克隆。

The ViewModel is the data manager for our View. It supplies the data on demand.

ViewModel是我们View的数据管理器。 它按需提供数据。

The addFav() and removeFav() methods would get called from our Activity class for two operations:

从我们的Activity类中调用addFav()和removeFav()方法进行两项操作:

  • Change the SQLite DB更改SQLite数据库
  • Change the data supplied to the ListView through ArrayList.更改通过ArrayList提供给ListView的数据。

Thus every time the view requires something, our ViewModel has to do two calls. We'll see in a later tutorial, how using LiveData optimizes this.

因此,每次视图需要某些内容时,我们的ViewModel必须执行两次调用。 我们将在以后的教程中看到如何使用LiveData对此进行优化。

The code for the activity_main.xml layout is given below:

下面给出了activity_main.xml布局的代码:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"android:id="@+id/rlLayout"android:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:id="@+id/listView"android:layout_width="match_parent"android:layout_height="match_parent" /><android.support.design.widget.FloatingActionButtonandroid:id="@+id/fab"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:src="@android:drawable/ic_input_add"android:layout_margin="16dp" /></RelativeLayout>

The code for the list_item_row.xml is given below:

list_item_row.xml的代码如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:padding="10dp"><LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true"android:orientation="vertical"><TextViewandroid:id="@+id/tvUrl"android:layout_width="wrap_content"android:layout_height="wrap_content"android:autoLink="web"android:padding="8dp"android:textColor="@android:color/black"android:textSize="20sp" /><TextViewandroid:id="@+id/tvDate"android:layout_width="wrap_content"android:layout_height="wrap_content" /></LinearLayout><ImageButtonandroid:id="@+id/btnDelete"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentEnd="true"android:layout_alignParentRight="true"android:layout_centerVertical="true"android:onClick="deleteFav"android:src="@android:drawable/ic_menu_delete" />
</RelativeLayout>

The code for the MainActivity.java class is given below:

MainActivity.java类的代码如下:

package com.journaldev.androidviewmodel;import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.DialogInterface;
import android.support.annotation.NonNull;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;import java.util.Date;
import java.util.List;public class MainActivity extends AppCompatActivity {private FavouritesAdapter mFavAdapter;private FavouritesViewModel mFavViewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ListView listView = findViewById(R.id.listView);FloatingActionButton fab = findViewById(R.id.fab);mFavViewModel = ViewModelProviders.of(this).get(FavouritesViewModel.class);List<Favourites> favourites = mFavViewModel.getFavs();mFavAdapter = new FavouritesAdapter(this, R.layout.list_item_row, favourites);listView.setAdapter(mFavAdapter);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {final EditText inUrl = new EditText(MainActivity.this);AlertDialog dialog = new AlertDialog.Builder(MainActivity.this).setTitle("New favourite").setMessage("Add a url link below").setView(inUrl).setPositiveButton("Add", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {String url = String.valueOf(inUrl.getText());long date = (new Date()).getTime();// VM AND VIEWmFavAdapter.add(mFavViewModel.addFav(url, date));}}).setNegativeButton("Cancel", null).create();dialog.show();}});}public void deleteFav(View view) {View parent = (View) view.getParent();int position = (int) parent.getTag(R.id.POS);Favourites favourites = mFavAdapter.getItem(position);mFavViewModel.removeFav(favourites.mId);mFavAdapter.remove(favourites);}public class FavouritesAdapter extends ArrayAdapter<Favourites> {private class ViewHolder {TextView tvUrl;TextView tvDate;}public FavouritesAdapter(Context context, int layoutResourceId, List<Favourites> todos) {super(context, layoutResourceId, todos);}@Override@NonNullpublic View getView(int position, View convertView, ViewGroup parent) {Favourites favourites = getItem(position);ViewHolder viewHolder;if (convertView == null) {viewHolder = new ViewHolder();LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);convertView = vi.inflate(R.layout.list_item_row, parent, false);viewHolder.tvUrl = convertView.findViewById(R.id.tvUrl);viewHolder.tvDate = convertView.findViewById(R.id.tvDate);convertView.setTag(R.id.VH, viewHolder);} else {viewHolder = (ViewHolder) convertView.getTag(R.id.VH);}viewHolder.tvUrl.setText(favourites.mUrl);viewHolder.tvDate.setText((new Date(favourites.mDate).toString()));convertView.setTag(R.id.POS, position);return convertView;}}
}

In the above code, we've used an ArrayAdapter class for our ListView.

在上面的代码中,我们为ListView使用了ArrayAdapter类。

In our FloatingActionButton, we create an AlertDialog with an EditText to add a new URL in our ListView.

在FloatingActionButton中 ,我们创建一个带有EditText的AlertDialog ,以在ListView中添加新的URL。

ViewModel adds it to the SQLite and ArrayList.

ViewModel将其添加到SQLite和ArrayList。

There's an ImageButton defined for each list row in the XML. The android:onClick method over there, invokes the method deleteFav().

在XML中为每个列表行定义了一个ImageButton。 那里的android:onClick方法调用方法deleteFav()

In the deleteFav we trigger two calls - One to the ViewModel to delete the record from SQLite and the other to the ArrayList. This is expensive. We'll see in a later tutorial how LiveData optimizes this.

deleteFav我们触发两个调用-一个调用ViewModel从SQLite删除记录,另一个调用ArrayList。 这很贵。 我们将在以后的教程中看到LiveData如何对此进行优化。

Clicking the tvUrl would launch the URL directly into the browser since we'd set the android:autoLink="true"

单击tvUrl会将URL直接启动到浏览器中,因为我们将android:autoLink="true"

Did you know?你知道吗?
Adapters that extend the ArrayAdapter class must pass the layout resource through the constructors. Else it can lead to duplicate rows in the ListView.
扩展ArrayAdapter类的适配器必须通过构造函数传递布局资源。 否则,可能导致ListView中的行重复。

We've assigned id tags to the View Holder and views in the ListView Adapter.

我们已将ID标记分配给View Holder和ListView Adapter中的视图。

The tag ids are defined in a file tags.xml in the values folder:

标签id在values文件夹中的文件tags.xml中定义:

The output of the above application in action is given below:

上面应用程序的输出如下:

This brings an end to this tutorial. ViewModel is a vital of part of Android MVVM. You can download the final AndroidViewModel project from the link below:

本教程到此结束。 ViewModel是Android MVVM的重要组成部分。 您可以从下面的链接下载最终的AndroidViewModel项目:

AndroidViewModel ProjectAndroidViewModel项目
GitHub Repository.GitHub存储库中签出Android Studio项目代码。

翻译自: https://www.journaldev.com/21081/android-viewmodel

Android ViewModel相关推荐

  1. 安卓Android ViewModel 超简单实例

    安卓Android ViewModel 超简单实例 文章目录 安卓Android ViewModel 超简单实例 前言 使用步骤 1.引入库 2.继承ViewModel 并定义一个对象 3.到处去用 ...

  2. Android ViewModel详解

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/119828016 本文出自[赵彦军的博客] 文章目录 ViewModel简介 Vie ...

  3. android 旋转屏幕 view的调整,Android ViewModel在屏幕旋转时重新创建

    是的@tomwyr,这是一个来自android框架的bug . Bug details 该修复程序在28.0.0-alpha3和AndroidX 1.0.0-alpha3中可用 但如果您现在不想更新到 ...

  4. Android ViewModel使用

    文章目录 Jetpack ViewModel 概述 添加依赖库 ViewModel的生命周期 基本使用 AndroidViewModel Fragment之间共享数据 ViewModel与onSave ...

  5. Android - ViewModel LiveData

    文章目录 1. 关于ViewModel和LiveData 2. 示例效果 3. 创建ViewModel类 4. 新建两个Fragment 5. Fragment添加到MainActivity中 6. ...

  6. Android ViewModel+liveData+lifecycle+databinding打造MVVM

    Google JetPack 最近google推出JetPack官方架构组件,最近有时间在网上看了很多类似的文章,对ViewModel和lifeCycle有了大概的了解,谷歌意在使用这些组件完成代码解 ...

  7. Android ViewModel 的作用

    监听activity和fragment的销毁事件,在视图销毁时会调用onCleared(),可以用来清除缓存数据和取消异步请求,避免内存泄漏,防止回调null视图对象. class MainActiv ...

  8. Kotlin 风险高、RxJava 已过时,Android 原生开发现状分析!

    当你好不容易学会了某个框架或者工具,觉得它很好用的时候,它或许就要过时了. 英文:The State of Native Android Development 作者:Vasiliy Zukanov, ...

  9. Android LiveData

    In this tutorial, we'll be discussing the LiveData architectural component in our Android Applicatio ...

最新文章

  1. 爬虫准备工作1-Java写入字符串到txt文档
  2. 京东涉足快递业加剧行业动乱
  3. 菜鸟学习笔记2,$(document).ready()使用讨论
  4. 设计模式(一)预备知识
  5. 搭建Java开发环境及相关工具
  6. matlab自带python_在matlab中直接在python中使用sklearn
  7. Linux安装nextcloud教程,WSL下安装nextcloud
  8. JSP中request内置对象
  9. 通信中的“交织”技术
  10. 华为手机图标怎么变小_华为手机怎么设置图标由大变小
  11. WGS84 与 北京54 坐标系互转
  12. 常用的项目管理工具有哪些?
  13. win10 中 ,python 创建虚拟化环境
  14. python算方差_python 求方差python函数每日一讲 - divmod数字处理函数
  15. 内部收益率计算公式用计算机,内部收益率计算器
  16. JavaScript Web APIs
  17. Day794.如何用协程来优化多线程业务 -Java 性能调优实战
  18. tcp连接之半连接攻击和全连接攻击总结
  19. 问题 B: 加油站(贪心+模拟)
  20. HFSS学习笔记—11.环形定向耦合器分析

热门文章

  1. php访问方法外变量
  2. vs2010上opencv配置
  3. IOS一些常用的越狱渠道
  4. [转载] python3 字符串比较函数_python3 字符串操作相关函数
  5. [转载] 用 C++ 和 Java 写算法,差别大吗?
  6. 异步复位同步释放机制-系统完美稳定
  7. 从零配置webpack(react+less+typescript+mobx)
  8. go基础_defer
  9. django mariadb filter month 失败
  10. 构造函数_析构函数_深拷贝与浅拷贝