如何将Android MVVM模式与片段一起使用?
首先,我要求道歉我的英语不好 我已经开发了很多年的JavaSE软件,并且我曾经使用MVC设计模式。现在我开发了android应用程序,我不喜欢有人说android已经使用MVC模式,以xml文件作为视图 我在网上做了很多研究,但似乎在这个问题上没有达成一致意见。一些人使用MVC模式,另一些人使用MVP模式,但我认为,没有一致意见 最近我买了一本书(),在第二章中,您可以找到解释过的MVC、MVVM和依赖注入模式。在尝试了所有这些之后,我认为对于我的应用程序和我的工作模式来说,MVVM模式是最好的 我发现在使用活动编程时,这个模式非常容易使用,但我不知道在使用片段编程时如何使用它。我将重现应用于简单“todo应用程序”的MVVM模式示例,该示例从“Android最佳实践”一书的网站下载 视图(活动) 模型如何将Android MVVM模式与片段一起使用?,android,mvvm,android-fragments,Android,Mvvm,Android Fragments,首先,我要求道歉我的英语不好 我已经开发了很多年的JavaSE软件,并且我曾经使用MVC设计模式。现在我开发了android应用程序,我不喜欢有人说android已经使用MVC模式,以xml文件作为视图 我在网上做了很多研究,但似乎在这个问题上没有达成一致意见。一些人使用MVC模式,另一些人使用MVP模式,但我认为,没有一致意见 最近我买了一本书(),在第二章中,您可以找到解释过的MVC、MVVM和依赖注入模式。在尝试了所有这些之后,我认为对于我的应用程序和我的工作模式来说,MVVM模式是最好的
package com.example.mvvm;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
final class TodoModel
{
//The Model should contain no logic specific to the view - only
//logic necessary to provide a minimal API to the ViewModel.
private static final String DB_NAME = "tasks";
private static final String TABLE_NAME = "tasks";
private static final int DB_VERSION = 1;
private static final String DB_CREATE_QUERY = "CREATE TABLE " + TodoModel.TABLE_NAME + " (id integer primary key autoincrement, title text not null);";
private final SQLiteDatabase storage;
private final SQLiteOpenHelper helper;
public TodoModel(final Context ctx)
{
this.helper = new SQLiteOpenHelper(ctx, TodoModel.DB_NAME, null, TodoModel.DB_VERSION)
{
@Override
public void onCreate(final SQLiteDatabase db)
{
db.execSQL(TodoModel.DB_CREATE_QUERY);
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
final int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS " + TodoModel.TABLE_NAME);
this.onCreate(db);
}
};
this.storage = this.helper.getWritableDatabase();
}
/*Overrides are now done in the ViewModel. The Model only needs
*to add/delete, and the ViewModel can handle the specific needs of the View.
*/
public void addEntry(ContentValues data)
{
this.storage.insert(TodoModel.TABLE_NAME, null, data);
}
public void deleteEntry(final String field_params)
{
this.storage.delete(TodoModel.TABLE_NAME, field_params, null);
}
public Cursor findAll()
{
//Model only needs to return an accessor. The ViewModel will handle
//any logic accordingly.
return this.storage.query(TodoModel.TABLE_NAME, new String[]
{ "title" }, null, null, null, null, null);
}
}
package com.example.mvvm;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class TodoViewModel implements TodoActivity.TaskListManager
{
/*The ViewModel acts as a delegate between the ToDoActivity (View)
*and the ToDoProvider (Model).
* The ViewModel receives references from the View and uses them
* to update the UI.
*/
private TodoModel db_model;
private List<String> tasks;
private Context main_activity;
private ListView taskView;
private EditText newTask;
public TodoViewModel(Context app_context)
{
tasks = new ArrayList<String>();
main_activity = app_context;
db_model = new TodoModel(app_context);
}
//Overrides to handle View specifics and keep Model straightforward.
private void deleteTask(View view)
{
db_model.deleteEntry("title='" + ((TextView)view).getText().toString() + "'");
}
private void addTask(View view)
{
final ContentValues data = new ContentValues();
data.put("title", ((TextView)view).getText().toString());
db_model.addEntry(data);
}
private void deleteAll()
{
db_model.deleteEntry(null);
}
private List<String> getTasks()
{
final Cursor c = db_model.findAll();
tasks.clear();
if (c != null)
{
c.moveToFirst();
while (c.isAfterLast() == false)
{
tasks.add(c.getString(0));
c.moveToNext();
}
c.close();
}
return tasks;
}
private void renderTodos()
{
//The ViewModel handles rendering and changes to the view's
//data. The View simply provides a reference to its
//elements.
taskView.setAdapter(new ArrayAdapter<String>(main_activity,
android.R.layout.simple_list_item_1,
getTasks().toArray(new String[]
{})));
}
public void registerTaskList(ListView list)
{
this.taskView = list; //Keep reference for rendering later
if (list.getAdapter() == null) //Show items at startup
{
renderTodos();
}
list.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id)
{ //Tapping on any item in the list will delete that item from the database and re-render the list
deleteTask(view);
renderTodos();
}
});
}
public void registerTaskAdder(View button, EditText input)
{
this.newTask = input;
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(final View view)
{ //Add task to database, re-render list, and clear the input
addTask(newTask);
renderTodos();
newTask.setText("");
}
});
}
}
视图模型
package com.example.mvvm;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
final class TodoModel
{
//The Model should contain no logic specific to the view - only
//logic necessary to provide a minimal API to the ViewModel.
private static final String DB_NAME = "tasks";
private static final String TABLE_NAME = "tasks";
private static final int DB_VERSION = 1;
private static final String DB_CREATE_QUERY = "CREATE TABLE " + TodoModel.TABLE_NAME + " (id integer primary key autoincrement, title text not null);";
private final SQLiteDatabase storage;
private final SQLiteOpenHelper helper;
public TodoModel(final Context ctx)
{
this.helper = new SQLiteOpenHelper(ctx, TodoModel.DB_NAME, null, TodoModel.DB_VERSION)
{
@Override
public void onCreate(final SQLiteDatabase db)
{
db.execSQL(TodoModel.DB_CREATE_QUERY);
}
@Override
public void onUpgrade(final SQLiteDatabase db, final int oldVersion,
final int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS " + TodoModel.TABLE_NAME);
this.onCreate(db);
}
};
this.storage = this.helper.getWritableDatabase();
}
/*Overrides are now done in the ViewModel. The Model only needs
*to add/delete, and the ViewModel can handle the specific needs of the View.
*/
public void addEntry(ContentValues data)
{
this.storage.insert(TodoModel.TABLE_NAME, null, data);
}
public void deleteEntry(final String field_params)
{
this.storage.delete(TodoModel.TABLE_NAME, field_params, null);
}
public Cursor findAll()
{
//Model only needs to return an accessor. The ViewModel will handle
//any logic accordingly.
return this.storage.query(TodoModel.TABLE_NAME, new String[]
{ "title" }, null, null, null, null, null);
}
}
package com.example.mvvm;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class TodoViewModel implements TodoActivity.TaskListManager
{
/*The ViewModel acts as a delegate between the ToDoActivity (View)
*and the ToDoProvider (Model).
* The ViewModel receives references from the View and uses them
* to update the UI.
*/
private TodoModel db_model;
private List<String> tasks;
private Context main_activity;
private ListView taskView;
private EditText newTask;
public TodoViewModel(Context app_context)
{
tasks = new ArrayList<String>();
main_activity = app_context;
db_model = new TodoModel(app_context);
}
//Overrides to handle View specifics and keep Model straightforward.
private void deleteTask(View view)
{
db_model.deleteEntry("title='" + ((TextView)view).getText().toString() + "'");
}
private void addTask(View view)
{
final ContentValues data = new ContentValues();
data.put("title", ((TextView)view).getText().toString());
db_model.addEntry(data);
}
private void deleteAll()
{
db_model.deleteEntry(null);
}
private List<String> getTasks()
{
final Cursor c = db_model.findAll();
tasks.clear();
if (c != null)
{
c.moveToFirst();
while (c.isAfterLast() == false)
{
tasks.add(c.getString(0));
c.moveToNext();
}
c.close();
}
return tasks;
}
private void renderTodos()
{
//The ViewModel handles rendering and changes to the view's
//data. The View simply provides a reference to its
//elements.
taskView.setAdapter(new ArrayAdapter<String>(main_activity,
android.R.layout.simple_list_item_1,
getTasks().toArray(new String[]
{})));
}
public void registerTaskList(ListView list)
{
this.taskView = list; //Keep reference for rendering later
if (list.getAdapter() == null) //Show items at startup
{
renderTodos();
}
list.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
@Override
public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id)
{ //Tapping on any item in the list will delete that item from the database and re-render the list
deleteTask(view);
renderTodos();
}
});
}
public void registerTaskAdder(View button, EditText input)
{
this.newTask = input;
button.setOnClickListener(new View.OnClickListener()
{
@Override
public void onClick(final View view)
{ //Add task to database, re-render list, and clear the input
addTask(newTask);
renderTodos();
newTask.setText("");
}
});
}
}
package com.example.mvvm;
导入android.content.ContentValues;
导入android.content.Context;
导入android.database.Cursor;
导入android.view.view;
导入android.widget.AdapterView;
导入android.widget.ArrayAdapter;
导入android.widget.EditText;
导入android.widget.ListView;
导入android.widget.TextView;
导入java.util.ArrayList;
导入java.util.List;
公共类TodoViewModel实现TodoActivity.TaskListManager
{
/*ViewModel充当ToDoActivity(视图)之间的委托
*以及ToDoProvider(模型)。
*ViewModel从视图接收引用并使用它们
*更新用户界面。
*/
私有到模型db_模型;
私有列表任务;
私人背景主要活动;
私有ListView任务视图;
私人编辑任务;
public-to-oviewmodel(上下文应用程序\u上下文)
{
tasks=newarraylist();
主要活动=应用程序上下文;
db_模型=新的TodoModel(应用程序_上下文);
}
//覆盖以处理视图细节并保持模型直观。
私有void deleteTask(视图)
{
db_model.deleteEntry(“title=”+((TextView)视图).getText().toString()+”);
}
私有void addTask(视图)
{
最终ContentValues数据=新ContentValues();
data.put(“title”,((TextView)view.getText().toString());
db_模型。加法(数据);
}
私有void deleteAll()
{
db_model.deleteEntry(null);
}
私有列表getTasks()
{
最终游标c=db_model.findAll();
任务。清除();
如果(c!=null)
{
c、 moveToFirst();
while(c.isAfterLast()==false)
{
tasks.add(c.getString(0));
c、 moveToNext();
}
c、 close();
}
返回任务;
}
私有void rendertos()
{
//ViewModel处理视图的渲染和更改
//视图只是提供对其
//元素。
taskView.setAdapter(新阵列适配器)(主活动,
android.R.layout.simple\u list\u item\u 1,
getTasks().toArray(新字符串[])
{})));
}
公共无效注册表tasklist(列表视图列表)
{
this.taskView=list;//保留引用以供以后渲染
if(list.getAdapter()==null)//在启动时显示项目
{
rendertos();
}
list.setOnItemClickListener(新的AdapterView.OnItemClickListener()
{
@凌驾
public void onItemClick(最终适配器视图父视图、最终视图视图、最终整型位置、最终长id)
{//点击列表中的任何项目都将从数据库中删除该项目并重新呈现列表
删除任务(视图);
rendertos();
}
});
}
公共无效注册表taskadder(查看按钮,编辑文本输入)
{
this.newTask=输入;
setOnClickListener(新视图.OnClickListener()
{
@凌驾
公共void onClick(最终视图)
{//将任务添加到数据库,重新呈现列表,然后清除输入
addTask(newTask);
rendertos();
newTask.setText(“”);
}
});
}
}
问题是,当我试图在使用片段时重现这种模式时,我不知道如何继续。我可以为每个片段设置一个视图模型和一个模型,还是仅为包含这些片段的活动设置一个模型
使用经典的片段方法(片段是活动中的一个内部类),很容易与活动交互,或者访问片段管理器进行更改,但是如果我将代码解耦,并将程序的逻辑放在活动之外,我发现我需要经常在ViewModel中引用活动(不是对活动视图的引用,而是对活动本身的引用)
或者,例如,假设带有片段的活动正在处理从意图而不是从模型(数据库或rest服务)接收的数据。然后,我觉得我不需要模型。也许我可以在活动中接收意图时创建模型,但我觉得这不正确(视图不应该与模型有关系,只有viewmodel…)
有谁能给我解释一下,当使用片段时,如何在android上使用MVVM模式
提前感谢。注意:以下内容已经过时,我不再推荐它。主要是因为在此设置中很难测试Viewsmodel。请查看Google架构蓝图 旧答案: 就个人而言,我更喜欢另一种设置:
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
try {
delegate = (ITaskListManager) activity;
} catch (ClassCastException ignore) {
throw new IllegalStateException("Activity " + activity + " must implement ITaskListManager");
}
}
@Override
public void onDetach() {
delegate = sDummyDelegate;
super.onDetach();
}
public class ViewFragment extends Fragment {
private ListView taskView;
private Button btNewTask;
private EditText etNewTask;
private ITaskListManager delegate;
/**
* Dummy delegate to avoid nullpointers when
* the fragment is not attached to an activity
*/
private final ITaskListManager sDummyDelegate = new ITaskListManager() {
@Override
public void registerTaskList(final ListView list) {
}
@Override
public void registerTaskAdder(final View button, final EditText input) {
}
};
/*
* The View handles UI setup only. All event logic and delegation
* is handled by the ViewModel.
*/
public static interface ITaskListManager {
// Through this interface the event logic is
// passed off to the ViewModel.
void registerTaskList(ListView list);
void registerTaskAdder(View button, EditText input);
}
@Override
public void onAttach(final Activity activity) {
super.onAttach(activity);
try {
delegate = (ITaskListManager) activity;
} catch (ClassCastException ignore) {
throw new IllegalStateException("Activity " + activity + " must implement ITaskListManager");
}
}
@Override
public void onDetach() {
delegate = sDummyDelegate;
super.onDetach();
}
@Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_view_model, container, false);
taskView = (ListView) view.findViewById(R.id.tasklist);
btNewTask = (Button) view.findViewById(R.id.btNewTask);
etNewTask = (EditText) view.findViewById(R.id.etNewTask);
delegate.registerTaskList(taskView);
delegate.registerTaskAdder(btNewTask, etNewTask);
return view;
}
}
public class ViewModelActivity extends ActionBarActivity implements ITaskListManager {
@Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_model);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction().add(R.id.container, new ViewFragment()).commit();
}
initViewModel();
}
@Override
public boolean onCreateOptionsMenu(final Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.view_model, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(final MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private Model db_model;
private List<String> tasks;
private ListView taskView;
private EditText newTask;
/**
* Initialize the ViewModel
*/
private void initViewModel() {
tasks = new ArrayList<String>();
db_model = new Model(this);
}
private void deleteTask(final View view) {
db_model.deleteEntry("title='" + ((TextView) view).getText().toString() + "'");
}
private void addTask(final View view) {
final ContentValues data = new ContentValues();
data.put("title", ((TextView) view).getText().toString());
db_model.addEntry(data);
}
private void deleteAll() {
db_model.deleteEntry(null);
}
private List<String> getTasks() {
final Cursor c = db_model.findAll();
tasks.clear();
if (c != null) {
c.moveToFirst();
while (c.isAfterLast() == false) {
tasks.add(c.getString(0));
c.moveToNext();
}
c.close();
}
return tasks;
}
private void renderTodos() {
// The ViewModel handles rendering and changes to the view's
// data. The View simply provides a reference to its
// elements.
taskView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getTasks().toArray(new String[] {})));
}
@Override
public void registerTaskList(final ListView list) {
taskView = list; // Keep reference for rendering later
if (list.getAdapter() == null) // Show items at startup
{
renderTodos();
}
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(final AdapterView<?> parent, final View view, final int position, final long id) { // Tapping on any
// item in the list
// will delete that
// item from the
// database and
// re-render the list
deleteTask(view);
renderTodos();
}
});
}
@Override
public void registerTaskAdder(final View button, final EditText input) {
newTask = input;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(final View view) { // Add task to database, re-render list, and clear the input
addTask(newTask);
renderTodos();
newTask.setText("");
}
});
}
}
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto">
<data class=".UserBinding">
<variable name="user" type="com.darxstudios.databind.example.User"/>
</data>
<RelativeLayout
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivityFragment">
<TextView android:text='@{user.firstName+" "+user.lastName}' android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="New Button"
android:id="@+id/button"
android:layout_below="@+id/textView"
android:layout_toEndOf="@+id/textView"
android:layout_marginStart="40dp"
android:layout_marginTop="160dp" />
</RelativeLayout>
</layout>
public class MainActivityFragment extends Fragment {
public MainActivityFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final User user = new User();
user.setFirstName("Michael");
user.setLastName("Cameron");
UserBinding binding = DataBindingUtil.inflate(inflater,R.layout.fragment_main, container, false);
binding.setUser(user);
View view = binding.getRoot();
final Button button = (Button) view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
user.setFirstName("@Darx");
user.setLastName("Val");
}
});
return view;
}
}