Android MVP模式实战练习之一步一步打造一款简易便笺app(二)
来源:互联网 发布:淘宝数据公式pv uv 编辑:程序博客网 时间:2024/06/08 04:12
前情提要
上一篇 Android MVP模式实战练习之一步一步打造一款简易便笺app(一)
我们已经完成了主界面View和Presenter的编写,也设计好了数据Model的接口。如果你看完上一篇对V和P的设计还不是很清晰,没事,接下来我们要开始写编辑便笺界面的V和P,套路是一毛一样的,带你再走一遍。
编写编辑界面View和Presenter
跟之前一样,我们首先要创建一个EditActivity,并为其编写一个布局xml:
layout/edit_act:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:paddingTop="25dp"></android.support.v7.widget.Toolbar> </android.support.design.widget.AppBarLayout> <android.support.design.widget.CoordinatorLayout android:layout_width="match_parent" android:layout_height="match_parent"> <FrameLayout android:id="@+id/fragment_content" android:layout_width="match_parent" android:layout_height="match_parent"/> <android.support.design.widget.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="16dp" app:fabSize="normal" app:layout_anchor="@id/fragment_content" app:layout_anchorGravity="bottom|end" android:src="@drawable/save"/> </android.support.design.widget.CoordinatorLayout></LinearLayout>
基本跟MainActivity的布局一样,就不多说了。然后看看EditActivity是怎么写的:
EditActivity:
public class EditActivity extends AppCompatActivity { public static final String EXTRA_NOTE_ID = "NOTE_ID"; private Toolbar toolbar; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.edit_act); //5.0以上使布局延伸到状态栏的方法 View decorView = getWindow().getDecorView(); int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE; decorView.setSystemUiVisibility(option); getWindow().setStatusBarColor(Color.TRANSPARENT); //设置toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); toolbar.setTitle("编辑便笺"); setSupportActionBar(toolbar); ActionBar ab = getSupportActionBar(); ab.setDisplayHomeAsUpEnabled(true); String noteId = getIntent().getStringExtra(EXTRA_NOTE_ID); //为空则代表是新建的便笺 //创建fragment (V) EditFragment editFragment = (EditFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_content); if(editFragment == null){ editFragment = EditFragment.getInstence(); ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),editFragment,R.id.fragment_content); } //创建Presenter (P) EditPresenter presenter = new EditPresenter(Injection.provideRespository(this),editFragment,noteId); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case android.R.id.home: finish(); break; } return super.onOptionsItemSelected(item); }}
可以看到跟MainActivity的套路是差不多的,不过我们在创建编辑界面的V和P之前,通过 String noteId = getIntent().getStringExtra(EXTRA_NOTE_ID);
这么一行代码来获取了MainActivity传递过来的数据。我们打开MainFragment里看看,里面有这么两个方法:
@Override public void showAddNotesUi() { Intent i = new Intent(getActivity(), EditActivity.class); startActivity(i); } @Override public void showNoteDetailUi(String noteId) { Intent i = new Intent(getActivity(),EditActivity.class); i.putExtra(EditActivity.EXTRA_NOTE_ID,noteId); startActivity(i); }
这两个方法一个表示创建便笺,一个表示编辑便笺,他们最终都是打开了EditActivity,只不过后者多传递了一个id的参数,这个id是一个便笺NoteBean的唯一标识。
所以EditActivity里获取的noteId是可以为空的,最后可以看到我们把他作为参数传到了EditPresenter 的构造方法里。
接下来创建EditFragment,照旧,先完成它的初始布局。这次直接把布局xml和代码一起贴上:
layout/edit_frag:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#F2F2F2" android:orientation="vertical"> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="10dp" app:cardBackgroundColor="#FFFFFF" app:cardCornerRadius="2dp" app:cardElevation="4dp"> <EditText android:id="@+id/edit_title" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:hint="请输入标题" android:minHeight="40dp" android:padding="4dp" android:textSize="22sp" android:background="@null" /> </android.support.v7.widget.CardView> <android.support.v7.widget.CardView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="10dp" app:cardBackgroundColor="#FFFFFF" app:cardCornerRadius="2dp" app:cardElevation="4dp"> <EditText android:id="@+id/edit_content" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="top" android:hint="请输入内容" android:padding="4dp" android:textSize="18sp" android:background="@null" /> </android.support.v7.widget.CardView></LinearLayout>
EditFragment:
public class EditFragment extends Fragment { private EditText mTitle; private EditText mContent; private FloatingActionButton fab; public static EditFragment getInstence(){ return new EditFragment(); } @Override public void onResume() { super.onResume(); //todo:初始化 } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.edit_frag,container,false); mTitle = (EditText) v.findViewById(R.id.edit_title); mContent = (EditText) v.findViewById(R.id.edit_content); fab = (FloatingActionButton) getActivity().findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //todo:保存便笺 } }); setHasOptionsMenu(true); return v; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.edit_menu,menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.delete_note: //todo:删除便笺 break; } return true; }
menu/edit_menu:
<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/delete_note" android:title="删除" app:showAsAction="always|withText"/></menu>
哇,贼简单,预留的//todo也才3个。相信你过一遍就懂了,那么接下来我们要干什么?
接下来该给他设计V接口啦,创建EditContract类:
public class EditContract { interface View extends BaseView<Presenter>{ } interface Presenter extends BasePresenter{ }}
一样的,我们现在考虑考虑编辑界面有哪些跟显示有关的逻辑。
考虑好了,发现跟显示有关的并不多,可以拟出如下接口:
public class EditContract { interface View extends BaseView<Presenter>{ void showNoteList(); //显示便笺列表(即返回主界面) void setTitle(String title); void setContent(String content); void showError(); void showEmptyError(); boolean isActive(); } interface Presenter extends BasePresenter{ }}
注意一样要有boolean isActive();
它还是比较有用的。
接下来就让EditFragment成为这个V接口的实现类吧。具体代码如下:
EditFragment:
public class EditFragment extends Fragment implements EditContract.View { private EditContract.Presenter presenter; //View持有Presenter private EditText mTitle; private EditText mContent; private FloatingActionButton fab; //……………………省略已有代码 //以下为EditContract.View接口实现 @Override public void setPresenter(EditContract.Presenter presenter) { this.presenter = presenter; } @Override public void showNoteList() { getActivity().finish(); } public void setTitle(String title) { mTitle.setText(title+""); } @Override public void setContent(String content) { mContent.setText(content+""); } @Override public void showError() { Snackbar.make(getView(),"加载失败",Snackbar.LENGTH_LONG).show(); } @Override public void showEmptyError() { Snackbar.make(getView(),"标题和内容不能全空",Snackbar.LENGTH_LONG).show(); } @Override public boolean isActive() { return isAdded(); }}
还是简单的。我们的V可以说是基本搞好了。下面设计P接口。
一样的,首先观察下EditFragment里留下来的//todo:
- //todo:初始化
- //todo:保存便笺
- //todo:删除便笺
就这么3个,由此我们的P接口也就很好写了:
public class EditContract { interface View extends BaseView<Presenter>{ //…………省略 } interface Presenter extends BasePresenter{ void loadNote(); void saveNote(String title,String content); void deleteNote(); }}
接下来让EditFragment持有这个接口,把对应的方法放到对应的//todo里,代码如下:
public class EditFragment extends Fragment implements EditContract.View { private EditContract.Presenter presenter; //View持有Presenter private EditText mTitle; private EditText mContent; private FloatingActionButton fab; public static EditFragment getInstence(){ return new EditFragment(); } @Override public void onResume() { super.onResume(); presenter.start(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View v = inflater.inflate(R.layout.edit_frag,container,false); mTitle = (EditText) v.findViewById(R.id.edit_title); mContent = (EditText) v.findViewById(R.id.edit_content); fab = (FloatingActionButton) getActivity().findViewById(R.id.fab); fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { presenter.saveNote(mTitle.getText().toString(),mContent.getText().toString()); } }); setHasOptionsMenu(true); return v; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.edit_menu,menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()){ case R.id.delete_note: presenter.deleteNote(); break; } return true; } //以下为EditContract.View接口实现 @Override public void setPresenter(EditContract.Presenter presenter) { this.presenter = presenter; } //………………省略其他剩余的接口方法}
接下来创建P的实现类EditPresenter。先贴上完整代码:
public class EditPresenter implements EditContract.Presenter { private EditContract.View editView; //Presenter持有View private NotesRepository notesRepository; //MVP的Model,管理数据处理 private String loadNoteId; private boolean isNewNote; public EditPresenter(NotesRepository notesRepository, EditContract.View editView, @Nullable String noteId){ this.notesRepository = notesRepository; this.editView = editView; this.loadNoteId = noteId; if(TextUtils.isEmpty(loadNoteId)){ isNewNote = true; } this.editView.setPresenter(this);//重要!别落了 } //以下为EditContract.Presenter接口实现 @Override public void start() { loadNote(); } @Override public void loadNote() { if(isNewNote){ return; //新建的便笺不用加载 } notesRepository.getNote(loadNoteId, new NoteDataSource.LoadNoteCallback() { @Override public void loadSuccess(NoteBean note) { if(editView.isActive()){ editView.setTitle(note.title); editView.setContent(note.content); } } @Override public void loadFailed() { if(editView.isActive()){ editView.showError(); } } }); } @Override public void saveNote(String title, String content) { if(TextUtils.isEmpty(title) && TextUtils.isEmpty(content)){ editView.showEmptyError(); return; } if(isNewNote){ createNote(title,content); }else{ updateNote(loadNoteId,title,content); } } private void updateNote(String loadNoteId, String title, String content) { NoteBean bean = new NoteBean(loadNoteId,title,content,true); notesRepository.updateNote(bean); editView.showNoteList(); } private void createNote(String title, String content) { NoteBean bean = new NoteBean(title,content,true); notesRepository.saveNote(bean); editView.showNoteList(); } @Override public void deleteNote() { if(isNewNote){ editView.showNoteList(); //如果是新建的便笺,直接返回主界面即可 }else{ notesRepository.deleteNote(loadNoteId); editView.showNoteList(); } }}
可以看到,EditPresenter的构造方法除了要传入M和V以外,还需要传入一个String noteId
,然后我们通过判断它是否为空来确定是创建新便笺还是编辑已存在的便笺。在public void loadNote()
方法里,如果是创建新编辑的话,那没什么可加载的,直接return掉,否则就根据noteId去调用notesRepository.getNote
来获取这个便笺的数据,并在回调里做对应的处理。保存便笺时,即调用public void saveNote(String title, String content)
时,也会根据情况来选择创建一个便笺还是更新已存在的便笺。
那么恭喜,编辑界面的V和P也已经设计好了。现在我们只用关心数据M怎么写了,其他的都不用管了~
Model的编写
上一篇文章的开头就已经设计好了Model的接口,并使用的NotesRepository作为具体实现类。我们接下来呢,打算完成一个数据库+缓存的M。
数据库Model的编写:
首先我们先写好数据库的M,这个数据库呢我们就不依赖什么开源库了,直接使用系统的SQLiteOpenHelper,初学者们肯定都学过~正好可以练习练习是吧。
我们先创建一个NoteDbHelper类,继承于SQLiteOpenHelper,然后在里面设计好我们的表。
代码如下:
** * Created by ccy on 2017-07-13. * 数据库创建类 */public class NoteDbHelper extends SQLiteOpenHelper { private static final int VERSION = 1; //版本号 private static final String DATABASE_NAME = "Notes.db"; //数据库名称 public static final String TABLE_NAME = "notes"; public static final String COLUMN_ID = "entryid"; public static final String COLUMN_TITLE = "title"; public static final String COLUMN_CONTENT = "content"; public static final String COLUMN_IS_ACTIVE = "active"; private static final String SQL_CREATE_ENTRIES = //建表,该有的空格别忘敲 "CREATE TABLE "+TABLE_NAME+"("+ COLUMN_ID+" TEXT PRIMARY KEY,"+ COLUMN_TITLE+" TEXT,"+ COLUMN_CONTENT+" TEXT,"+ COLUMN_IS_ACTIVE+" INTEGER"+ ")"; public NoteDbHelper(Context context) { super(context, DATABASE_NAME , null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(SQL_CREATE_ENTRIES); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }}
easy,一共4列,跟NoteBean里的属性是一 一对应的,并将COLUMN_ID作为了主键。
然后,创建NotesLocalDataSource类,实现之前设计好的接口NoteDataSource。然后去实现这个接口里的所有方法。那么接下来就要看大家SQLite的知识掌握的怎么样啦:
/** * Created by ccy on 2017-07-13. * 数据库操作类 ( Model 实现类) * 单例 */public class NotesLocalDataSource implements NoteDataSource { private static NotesLocalDataSource INSTANCE = null; private NoteDbHelper dbHelper; private NotesLocalDataSource(Context context) { dbHelper = new NoteDbHelper(context); } public static NotesLocalDataSource getInstance(Context context) { if (INSTANCE == null) { INSTANCE = new NotesLocalDataSource(context); } return INSTANCE; } @Override public void getNote(String noteId, LoadNoteCallback callback) { NoteBean note = null; SQLiteDatabase db = dbHelper.getReadableDatabase(); String queryColums[] = new String[]{ NoteDbHelper.COLUMN_ID, NoteDbHelper.COLUMN_TITLE, NoteDbHelper.COLUMN_CONTENT, NoteDbHelper.COLUMN_IS_ACTIVE }; String selection = NoteDbHelper.COLUMN_ID + " LIKE ?"; String[] selectionArgs = {noteId}; Cursor cursor = null; try { cursor = db.query(NoteDbHelper.TABLE_NAME, queryColums, selection, selectionArgs, null, null, null); if (cursor != null && cursor.getCount() > 0) { cursor.moveToFirst(); String id = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_ID)); String title = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_TITLE)); String content = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_CONTENT)); boolean isActive = cursor.getInt(cursor.getColumnIndex(NoteDbHelper.COLUMN_IS_ACTIVE)) == 1; note = new NoteBean(id, title, content, isActive); } } catch (Exception e) { callback.loadFailed(); } finally { if (cursor != null) { cursor.close(); } db.close(); } if(note == null){ callback.loadFailed(); }else { callback.loadSuccess(note); } } @Override public void getNotes(LoadNotesCallback callback) { List<NoteBean> notes = new ArrayList<>(); SQLiteDatabase db = dbHelper.getReadableDatabase(); String queryColums[] = { NoteDbHelper.COLUMN_ID, NoteDbHelper.COLUMN_TITLE, NoteDbHelper.COLUMN_CONTENT, NoteDbHelper.COLUMN_IS_ACTIVE }; Cursor cursor = null; try { cursor = db.query(NoteDbHelper.TABLE_NAME, queryColums, null, null, null, null, null); if (cursor != null && cursor.getCount() > 0) { while (cursor.moveToNext()) { String id = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_ID)); String title = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_TITLE)); String content = cursor.getString(cursor.getColumnIndex(NoteDbHelper.COLUMN_CONTENT)); boolean isActive = cursor.getInt(cursor.getColumnIndex(NoteDbHelper.COLUMN_IS_ACTIVE)) == 1; NoteBean bean = new NoteBean(id, title, content, isActive); notes.add(bean); } } } catch (Exception e) { callback.loadFailed(); } finally { if (cursor != null) { cursor.close(); } db.close(); } callback.loadSucess(notes); } @Override public void saveNote(NoteBean note) { if(note == null){ return; } SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(NoteDbHelper.COLUMN_ID,note.id); values.put(NoteDbHelper.COLUMN_TITLE,note.title); values.put(NoteDbHelper.COLUMN_CONTENT,note.content); values.put(NoteDbHelper.COLUMN_IS_ACTIVE,note.isActive); db.insert(NoteDbHelper.TABLE_NAME,null,values); db.close(); } @Override public void updateNote(NoteBean note) { if(note == null){ return; } SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(NoteDbHelper.COLUMN_TITLE,note.title); values.put(NoteDbHelper.COLUMN_CONTENT,note.content); values.put(NoteDbHelper.COLUMN_IS_ACTIVE,note.isActive); String selection = NoteDbHelper.COLUMN_ID+" LIKE ?"; String[] selectionArgs = {note.id}; db.update(NoteDbHelper.TABLE_NAME,values,selection,selectionArgs); db.close(); } @Override public void markNote(NoteBean note, boolean isActive) { if(note == null){ return; } SQLiteDatabase db = dbHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put(NoteDbHelper.COLUMN_IS_ACTIVE,isActive); String selection = NoteDbHelper.COLUMN_ID+ " LIKE ?"; String[] selectionArgs = {note.id}; db.update(NoteDbHelper.TABLE_NAME,values,selection,selectionArgs); db.close(); } @Override public void clearCompleteNotes() { SQLiteDatabase db = dbHelper.getWritableDatabase(); String selection = NoteDbHelper.COLUMN_IS_ACTIVE+" LIKE ?"; String[] selectionArgs ={"0"}; //0即false db.delete(NoteDbHelper.TABLE_NAME,selection,selectionArgs); db.close(); } @Override public void deleteAllNotes() { SQLiteDatabase db = dbHelper.getWritableDatabase(); db.delete(NoteDbHelper.TABLE_NAME,null,null); db.close(); } @Override public void deleteNote(String noteId) { SQLiteDatabase db = dbHelper.getWritableDatabase(); String selection = NoteDbHelper.COLUMN_ID + " LIKE ?"; String[] selectionArgs = {noteId}; db.delete(NoteDbHelper.TABLE_NAME,selection,selectionArgs); db.close(); } @Override public void cacheEnable(boolean enable) { //无操作。 }}
上面这些呢都是SQLite的知识,我也没什么可说的,还望应该都已经掌握。不过我们可以看到public void cacheEnable(boolean enable)
是空的。是的,这个M只负责以数据库作为数据源,并没有缓存的概念,所以他的这个方法是空的。
那么以数据库的M已经写好了,其实这个时候,我们已经可以把它作为最终M设置给其他界面了,只不过他没有缓存概念(也就是说MainPresenter里loadNotes(boolean forceUpdate,boolean showLoadingUI);
这个方法的第一个参数没有了意义,但不影响使用)。
数据库+缓存的Model编写
我们目的是要去实现数据库+缓存的M,所以,我们接下来开始修改NotesRepository,让他持有我们上面写好的NotesLocalDataSource类,并使用一个Map < String,NoteBean > 作为缓存的角色。完整的代码如下所示:
public class NotesRepository implements NoteDataSource { private static NotesRepository INSTANCE = null; private Map<String,NoteBean> notesCache; //数据缓存,key为id private boolean cacheEnable = false; //是否可以从缓存中获取数据 private NoteDataSource notesLocalDataSource;//从数据库获取数据的Model private NotesRepository(NoteDataSource notesLocalDataSource){ this.notesLocalDataSource = notesLocalDataSource; } public static NotesRepository getInstence(NoteDataSource notesLocalDataSource){ if(INSTANCE == null){ INSTANCE = new NotesRepository(notesLocalDataSource); } return INSTANCE; } @Override public void getNote(final String noteId, final LoadNoteCallback callback) { if(notesCache != null && cacheEnable){ //直接从缓存中取 NoteBean bean =getNoteFromCacheById(noteId); if(bean != null){ callback.loadSuccess(notesCache.get(noteId)); return; } } notesLocalDataSource.getNote(noteId, new LoadNoteCallback() { @Override public void loadSuccess(NoteBean note) { if(notesCache == null){ notesCache = new LinkedHashMap<String, NoteBean>(); } notesCache.put(noteId,note); callback.loadSuccess(note); } @Override public void loadFailed() { callback.loadFailed(); } }); } /** * 尝试从缓存中获取note * @param noteId * @return */ private NoteBean getNoteFromCacheById(String noteId){ if(notesCache == null || notesCache.isEmpty()){ return null; }else{ return notesCache.get(noteId); } } @Override public void getNotes(final LoadNotesCallback callback) { if(notesCache != null && cacheEnable){ callback.loadSucess(new ArrayList<NoteBean>(notesCache.values())); return; } notesLocalDataSource.getNotes(new LoadNotesCallback() { @Override public void loadSucess(List<NoteBean> notes) { refreshCache(notes); callback.loadSucess(notes); } @Override public void loadFailed() { callback.loadFailed(); } }); } private void refreshCache(List<NoteBean> notes) { if(notesCache == null){ notesCache = new LinkedHashMap<>(); } notesCache.clear(); for (NoteBean bean : notes){ notesCache.put(bean.id,bean); } cacheEnable = true; } @Override public void saveNote(NoteBean note) { notesLocalDataSource.saveNote(note); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } notesCache.put(note.id,note); } @Override public void updateNote(NoteBean note) { notesLocalDataSource.updateNote(note); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } notesCache.put(note.id,note); } @Override public void markNote(NoteBean note, boolean isActive) { notesLocalDataSource.markNote(note,isActive); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } NoteBean newBean = new NoteBean(note.id,note.title,note.content,isActive); notesCache.put(note.id,newBean); } @Override public void clearCompleteNotes() { notesLocalDataSource.clearCompleteNotes(); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } Iterator<Map.Entry<String, NoteBean>> iterator = notesCache.entrySet().iterator(); while(iterator.hasNext()){ Map.Entry<String, NoteBean> next = iterator.next(); if(!next.getValue().isActive){ iterator.remove(); } } } @Override public void deleteAllNotes() { notesLocalDataSource.deleteAllNotes(); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } notesCache.clear(); } @Override public void deleteNote(String noteId) { notesLocalDataSource.deleteNote(noteId); if(notesCache == null){ notesCache = new LinkedHashMap<>(); } notesCache.remove(noteId); } @Override public void cacheEnable(boolean enable) { this.cacheEnable = enable; }}
在他的每个接口方法里,我们都会去调用NotesLocalDataSource对应的方法,同时也实现了缓存的对应操作。在public void getNote
和public void getNotes
方法里,我们先判断缓存是否可用的标志位“cacheEnable ”,如果是true,那么直接从缓存Map里取数据,如果是false,则调用持有的NotesLocalDataSource的对应方法。
好啦,这样数据库+缓存的M就写好啦,也代表着我们的项目已经完成了~~
你有得到收获吗?~如果现在让你去实现一个数据库+缓存+服务器的M你会写吗?我本来也是想打算再用bmob写个服务器的M的,不过这已经不算是我们MVP模式的事了~就没写啦。
MVP模式优点还是很明显哒,不过我们这项目太小了,也许大家看完只觉得代码多了许多,还麻烦了许多。。。不过等大家以后体会了当界面逻辑和业务逻辑像八宝粥一样混合在一起所带来的痛苦后,,,就知道mvp的好了~
其他补充
在给Presenter构造方法里传递Model时,我们使用的是如下方法: Injection.provideRespository(this)
他的具体实现是这样的:
/** * Created by ccy on 2017-07-13. * 提供NotesRespository */public class Injection { public static NotesRepository provideRespository(Context context){ return NotesRepository.getInstence(NotesLocalDataSource.getInstance(context)); }}
这样写的好处是方便统一管理,我们现在的NotesRespository是数据库+缓存,但是哪天来个新需求说要实现数据库+缓存+服务器的话,你就要重新写一个NotesRespository并替换之前的,这个时候你只用修改Injection里的provideRespository即可,而不用去找每个Activity一个一个的去替换。
在Activity里创建Fragment时,我们用的是如下工具方法: ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),mainFragment,R.id.fragment_content);
它的具体实现:
public class ActivityUtils { /** * 将fragment放置至指定布局id * @param fragmentManager * @param fragment * @param frameId 布局id */ public static void addFragmentToActivity (@NonNull FragmentManager fragmentManager, @NonNull Fragment fragment, int frameId) { FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.add(frameId, fragment); transaction.commit(); }}
源码地址https://github.com/CCY0122/MVP-Note_app
- Android MVP模式实战练习之一步一步打造一款简易便笺app(二)
- Android MVP模式实战练习之一步一步打造一款简易便笺app(一)
- 如何打造一款 android app
- 打造Android MVP模式 retrofit+rxjava(二)
- Android MVP模式实战
- Android MVP模式实战
- Android开源实战:使用MVP+Retrofit开发一款文字阅读APP
- 打造Android的MVP模式
- Android之mvp设计模式(二)
- Android 设计模式之二:MVP模式与MVC模式 .
- android 用mvp模式来架构自己的app+打造Recyclerview万能适配器
- 打造Android MVP模式(一)
- Android之MVP模式
- Android 之MVP模式
- 如何优雅的使用Retrofit、Rxjava、Butterknife、Material开发一款MVP模式的新闻+天气预报+妹子的Android app
- MVP+Databinding模式开发APP(二)
- 构建一款App之使用设计模式
- Android实战简易教程<二十九>基于Face++实现年龄识别APP(一))
- 白云苍狗,雨飘飘... ...
- 绝对定位与相对定位
- 驱动框架8——将驱动集成到内核中
- numpy学习笔记一(n维数组及元素级数组函数)
- VS2010项目文件被卸载的问题
- Android MVP模式实战练习之一步一步打造一款简易便笺app(二)
- vb.net 教程 4-9 二进制文件读写 1
- What is a Servlet?
- redis的数据结构基本操作
- 线程池(java.util.concurrent.ThreadPoolExecutor)的使用(一)
- 【面试常见问题】【C++】指针和引用的区别,有哪些不同点,细细道2
- Vue遇到的bug-01
- Java-德才论 (25)
- Ubuntu卸载软件之aptitude命令使用