Wie verwenden von Android-MVVM-Muster mit Fragmenten?
Ersten bitte ich, entschuldigt sich über mein nicht gutes Deutsch.
Entwickelt ich viele Jahre Java-SE-software, und ich verwendet, um die Verwendung des MVC-design-pattern. Jetzt habe ich das entwickeln von android-apps, und ich bin nicht glücklich mit dem argument, das sagt, dass android bereits nutzt ein MVC-Muster, mit dem die xml-Dateien handeln, wie der Blick.
Ich habe eine Menge Forschung im web, aber es scheint, dass es keine Einstimmigkeit über dieses Thema. Einige verwenden das MVC-Muster, die anderen auf das MVP-Muster, aber ich bin meiner Meinung nach, gibt es keine Einhelligkeit.
Vor kurzem kaufte ich mir ein Buch (Android Best Practices, von Godfrey Nolan, Onur Cinar und David Truxall), und in Kapitel zwei finden Sie das MVC, MVVM-und Dependency Injection-patterns erklärt. Nach dem Versuch, alle von Ihnen, ich denke, dass für meine apps und meine Arbeit-Modus das beste ist das MVVM-pattern.
Ich finde dieses Muster sehr einfach zu verwenden, bei der Programmierung mit Aktivitäten, aber ich bin verwirrt über wie man es benutzt, wenn Programmierung mit Fragmenten. Ich reproduzieren das Beispiel für das MVVM-pattern angewendet, um einfache "todo-app", heruntergeladen von der Webseite "Android Best Practices" - Buch.
Die Anzeigen (Aktivität)
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);
}
}
Das Modell
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);
}
}
Das 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("");
}
});
}
}
Das problem ist, dass wenn ich versuchen Sie es zum reproduzieren dieses Muster bei Verwendung von Fragmenten, bin ich mir nicht sicher, wie Sie Vorgehen. Kann ich eine view-Modell und ein Modell für jedes fragment oder nur für die Aktivität, enthält diese Fragmente?
Mit dem klassischen Ansatz zu fragmentieren (das fragment ist eine innere Klasse innerhalb der Aktivität), ist es einfach zu interagieren mit der Aktivität oder den Zugriff auf die fragment-manager ändert, wenn ich aber entkoppeln Sie den code und die Logik der mein Programm außerhalb der Aktivität, die ich gesehen habe, brauche ich sehr oft Verweise auf die Aktivität in meinem ViewModel (keine Verweise auf die Ansichten der Tätigkeit, sondern verweist auf die Tätigkeit selbst).
Oder stellen Sie sich beispielsweise vor, dass die Aktivität mit Fragmenten, ist das arbeiten mit Daten in der Absicht, nicht von einem Modell (Datenbank-oder rest-service). Dann fühle ich, dass ich nicht brauchen, ein Modell. Vielleicht kann ich das Modell erstellen, wenn ich die Absicht in der Tätigkeit, aber ich habe das Gefühl, dass dies nicht korrekt ist (die Ansicht soll nicht in relation mit dem Modell, nur das viewmodel...).
Kann jemand mir eine Erklärung über die Verwendung des MVVM-Muster mit android bei der Verwendung von Fragmenten?
Vielen Dank im Voraus.
InformationsquelleAutor R. Campos | 2014-03-08
Du musst angemeldet sein, um einen Kommentar abzugeben.
HINWEIS: Das folgende ist überholt, und ich würde nicht empfehlen, es nicht mehr. Vor allem, weil es schwierig zu testen, die Viewsmodel in diesem setup. Haben Sie einen Blick auf die Google-Architektur-Blueprints.
Alte Antwort:
Persönlich bevorzuge ich eine Alternative Einrichtung:
Das Modell
Ihrem Modell. Nicht geändert werden müssen (Schönheit mit MVVM 🙂 )
View (fragment)
Etwas anders. Die View (Fragment) hat eine Referenz auf das ViewModel ( Aktivität ) in meinem setup. Statt der Initialisierung delegieren wie:
Ich schlage vor, Sie verwenden ein bekanntes Android Muster:
Diese Weise, Ihre Anzeigen (Fragment) erzwingt, dass die Aktivität, an die es angeschlossen ist, implemets die ITaskListManager-Schnittstelle. Wenn das Fragment ist losgelöst von der Aktivität, sind einige default-Implementierung wird festgelegt, wie die Delegierten. Dies verhindert, dass man Fehler, wenn Sie eine Instanz eines fragments, das ist nicht an eine Aktivität (ja, das kann passieren).
Hier ist der komplette code für mein ViewFragment:
ViewModel (Aktivität)
Durch eine Aktivität, wie Ihr ViewModel ist fast das gleiche. Stattdessen müssen Sie nur sicherstellen, dass Sie erstellen das Modell hier, und daß Sie Ihre Ansicht (Fragment), um die Aktivität...
Extra
Hinzufügen von neuen Ansichten oder unterschiedlichen Ansichten umgegangen werden soll, die in der Aktivität. Das ist schön, da kann man jetzt hören für änderungen an der Konfiguration, und swap in einem speziellen Fragment für eine andere Ausrichtung...
InformationsquelleAutor Entreco
Ich bin ein Mitarbeiter von RoboBinding - Ein data-binding-Presentation Model(MVVM) - framework für die Android-Plattform. Ich biete mein Verständnis. MVVM wird Häufig verwendet, in der Microsoft community, was ist eigentlich stammen von Martin Fowler ' s Präsentation Modell. Die vereinfachte Darstellung des MVVM-Muster ist--Synchronisationsmechanismus(oder data binding)-->View Model-->Modell. Das Hauptmotiv und der Vorteil der Verwendung von MVVM ist das ViewModel wird Reine POJO, die Einheit Getestet(NICHT Android Unit-Tests, die dauert ewig.). In Android, eine mögliche Methode für MVVM ist: Anzeigen(Layout+Aktivität)---->Synchronisations-Mechanismus(oder data binding)-->ViewModel(Reine POJO)-->Modell(Business-Modell). Die Pfeil Richtungen zeigen auch die Abhängigkeiten. Sie instanziieren können Ihre Geschäftsmodelle in der View-Schicht und dann pass in die ViewModel, aber der Zugang flow ist immer zu ViewModel, und ViewModel zu Business-Modell. Es ist eine einfache Android MVVM-Beispiel-app unter RoboBinding. Und ich empfehle Ihnen zu Lesen Martin Fowler ' s original-Artikel über Präsentation Modell.
Anwenden, MVVM, es ist ein Synchronisations-Mechanismus-Modul, die Sie implementieren müssen, die kompliziert werden kann, wenn es keine third-party-lib. Wenn Sie nicht wollen, zu abhängig von einem third-party-lib, können Sie versuchen,MVP(Passiv Anzuzeigen). Aber beachten Sie, dass die Verwendung von Test-Double für Ansichten. Das Motiv der beiden Muster, die versuchen werden, Presenter-ViewModel oder nicht, abhängen(oder nicht direkt abhängig) Anzeigen, so dass Sie als Normale Einheit getestet(NICHT Android-Gerät Getestet).
InformationsquelleAutor Cheng
Ich mag die ersten OP ' s Ansatz und möchte lieber ein improvisiertes Vorgehen. Das problem mit dem @ - Entreco die Antwort ist, dass das ViewModel ist nicht länger ein POJO. Es ist von großem Vorteil, dass ein ViewModel als ein einfaches POJO, das macht das testen wirklich einfach. Dass es als eine Aktivität, die es vielleicht einen Tick mehr-framework abhängig, was in gewisser Weise wieder geht die Absicht des MVVM-isolation-Muster.
yup, die Abstraktion der view-Modell das Verhalten einer unabhängigen "einfache" Objekt ist ein entscheidender Vorteil, um das ganze MVVM-Ansatz, die sonst verloren gehen.
InformationsquelleAutor Kaushik Gopal
Können Sie diese Schritte für DataBinding in Fragmenten:
Ich habe gepostet-design und java-Klasse, die beide in Beispiel für die Bindung von Daten im Fragment.
Layout-XML -
Fragment-Klasse
Entwickler-Seite für databinding-details
InformationsquelleAutor Ramkailash