如何使用Android MVVM模式与片段?

前端之家收集整理的这篇文章主要介绍了如何使用Android MVVM模式与片段?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
首先我要求道歉,我不是很好的英语.

我开发了很多年的Java SE软件,而我曾经用过MVC设计模式.现在我开发Android应用程序,我不满意的说,android已经使用一个MVC模式,xml文件作为视图.

我在网上做了很多研究,但是似乎并没有一致的这个话题.有些使用MVC模式,其他的是MVP模式,但我是我的意见,没有一致意见.

最近我买了一本书(Android Best Practices,from Godfrey Nolan,Onur Cinar and David Truxall),在第二章中,您可以找到MVC,MVVM和依赖注入模式.尝试所有这些后,我认为对于我的应用程序和我的工作模式,最好的是MVVM模式.

我发现这种模式在使用活动进行编程时非常容易使用,但是我在使用片段编程时如何使用它非常困惑.我将再现从“Android最佳实践”一书的网站下载的简单的“todo app”MVVM模式的例子.

视图(活动)

package com.example.mvvm;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

public class TodoActivity extends Activity
{
    public static final String APP_TAG = "com.logicdrop.todos";

    private ListView taskView;
    private Button btNewTask;
    private EditText etNewTask;
    private TaskListManager delegate;

    /*The View handles UI setup only. All event logic and delegation
     *is handled by the viewmodel.
     */

    public static interface TaskListManager
    {
        //Through this interface the event logic is
        //passed off to the viewmodel.
        void registerTaskList(ListView list);
        void registerTaskAdder(View button,EditText input);
    }

    @Override
    protected void onStop()
    {
        super.onStop();
    }

    @Override
    protected void onStart()
    {
        super.onStart();
    }

    @Override
    public void onCreate(final Bundle bundle)
    {
        super.onCreate(bundle);

        this.setContentView(R.layout.main);

        this.delegate = new Todoviewmodel(this);
        this.taskView = (ListView) this.findViewById(R.id.tasklist);
        this.btNewTask = (Button) this.findViewById(R.id.btNewTask);
        this.etNewTask = (EditText) this.findViewById(R.id.etNewTask);
        this.delegate.registerTaskList(taskView);
        this.delegate.registerTaskAdder(btNewTask,etNewTask);
    }
   }

该模型

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,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);
    }
   }

viewmodel

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("");
            }
        });
    }
   }

问题是当我尝试在使用片段时再现这种模式时,我不知道如何进行.我可以为每个片段有一个视图模型和一个模型,还是仅包含这些片段的活动?

使用经典的片段化方法(片段是活动中的内部类),它很容易与活动进行交互,或访问片段管理器来进行更改,但是如果我将代码解耦,并放置我的逻辑程序外面的活动,我已经看到,我需要经常引用我的viewmodel中的活动(不涉及活动的意见,但参考活动本身).

或者,例如,假设具有片段的活动正在处理从意图而不是来自模型(数据库或休息服务)的数据.然后,我觉得我不需要一个模型.也许我可以在活动中收到意图时创建模型,但我觉得这是不正确的(视图不应该与模型有关,只有viewmodel …).

任何人都可以提供关于如何在使用片段时使用MVVM模式的解释?

提前致谢.

解决方法

就我个人而言,我更喜欢替代设置:

该模型

你的模特儿不需要改变(使用MVVM的美丽:))

视图(片段)

稍微不一样. View(Fragment)在我的设置中引用了viewmodel(Activity).而不是初始化您的代理,如:

// Old way -> I don't like it
this.delegate = new Todoviewmodel(this);

我建议你使用一个着名的Android模式:

@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();
}

这样,您的View(Fragment)强制其附加的活动实现了ITaskListManager接口.当Fragment从Activity中分离出来时,一些默认实现被设置为委托.当您具有未附加到活动的片段的实例(是的,可能会发生)时,可以避免获取错误.

以下是我的ViewFragment的完整代码

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;
    }
}

viewmodel(活动)

使用活动作为您的viewmodel几乎是一样的.相反,您只需要确保您在此处创建模型,并将您的View(Fragment)添加到活动中…

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,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 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,and clear the input
                addTask(newTask);
                renderTodos();
                newTask.setText("");
            }
        });
    }
}

额外

应在活动中处理添加新视图或不同视图.这是很好的,因为你现在可以监听配置更改,并在一个特殊的片段交换为不同的方向…

原文链接:https://www.f2er.com/android/311761.html

猜你在找的Android相关文章