好啦其實前一篇 Android MVP - Google Sample 解讀 截的圖就是為了接下來這一篇做準備。
TaskDetail
因為等下會有很多 code, 所以這裡先放個 index
- View 介面 BaseView
- Presenter 介面 BasePresenter
- Contract 介面
- Presenter 實作 TaskDetailPresenter
- View 實作 TaskDetailFragment
- Model 介面 TasksDataSource
- Model 實作 TasksRepository
不囉唆,看 code 囉。中間有省略一些,只保留有一些提示意味的部分。
View 介面 BaseView
public interface BaseView<T> {
void setPresenter(T presenter);
}
Presenter 介面 BasePresenter
public interface BasePresenter {
void start();
}
Contract 介面
public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
// ...
void hideTitle();
void showTitle(String title);
void showEditTask(String taskId);
void showTaskMarkedComplete();
void showTaskMarkedActive();
void showTaskDeleted();
}
interface Presenter extends BasePresenter {
void editTask();
void completeTask();
void activateTask();
void deleteTask();
}
}
這邊利用 TaskDetailContract
把 View
, Presenter
兩個綁起來,我覺得蠻漂亮的。一來可以直接看 Contract
便知道 view 跟 presenter 關係,有利於維護。二來名字簡單(待會可以在 TaskDetailPresenter
看到),畢竟最難的就是命名了。
Presenter 實作 TaskDetailPresenter
/**
* Listens to user actions from the UI ({@link TaskDetailFragment}), retrieves the data and updates
* the UI as required.
*/
public class TaskDetailPresenter implements TaskDetailContract.Presenter {
private final TasksRepository mTasksRepository;
private final TaskDetailContract.View mTaskDetailView;
@Nullable private String mTaskId;
/**
* 利用 constructor 來提供 view 跟 model
*/
public TaskDetailPresenter(@Nullable String taskId,
@NonNull TasksRepository tasksRepository,
@NonNull TaskDetailContract.View taskDetailView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
mTaskDetailView.setPresenter(this);
}
@Override public void start() {
openTask();
}
// ...
@Override public void deleteTask() {
if (Strings.isNullOrEmpty(mTaskId)) {
mTaskDetailView.showMissingTask();
return;
}
mTasksRepository.deleteTask(mTaskId);
mTaskDetailView.showTaskDeleted();
}
}
取得了 model 跟 view 後,就在各自的 function 中實作需要做的細節。
View 實作 TaskDetailFragment
請注意這裡還是有透過 Activity 把 Presenter 與 View (fragment) 串起來唷,但是代碼已經非常少了,也就是達到了我們最一開始想要的目的:讓 Activity 只管理 Android Life Cycle 的部分。
/**
* Displays task details screen.
*/
public class TaskDetailActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) {
// ...
// Create the presenter
new TaskDetailPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
taskDetailFragment);
}
}
接下來就是 TaskDetailFragment
去實作 TaskDetailContract.View
的細節。而且 view 本身只是在相對應的 UI 位置,拿 presenter 去做事。
public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {
private TaskDetailContract.Presenter mPresenter;
// ...
@Override public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_delete:
mPresenter.deleteTask();
return true;
}
return false;
}
@Override public void showCompletionStatus(final boolean complete) {
Preconditions.checkNotNull(mDetailCompleteStatus);
mDetailCompleteStatus.setChecked(complete);
mDetailCompleteStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
mPresenter.completeTask();
} else {
mPresenter.activateTask();
}
});
}
}
上面我偷偷用了 lambda ,相信各位大大應該還是可以理解的 :P
Model 介面 TasksDataSource
Model 實作 TasksRepository
public interface TasksDataSource {
interface LoadTasksCallback {
// ...
}
interface GetTaskCallback {
// ...
}
void getTasks(@NonNull LoadTasksCallback callback);
void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
// ...
void deleteAllTasks();
void deleteTask(@NonNull String taskId);
}
最後終於講到 Model 的部分了,不過我想這邊牽涉的範圍又更多了,還是繼續拆解到下一回合吧 :P
先引用一下延伸閱讀的一段話作結:
最后还剩下model层实现,项目中model层最大的特点是被赋予了数据获取的职责,与我们平常model层只定义实体对象截然不同,实例中,数据的获取、存储、数据状态变化都是model层的任务,presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样model、presenter、view都只处理各自的任务,此种实现确实是单一职责最好的诠释。 -- Android官方MVP架构示例项目解析
延伸閱讀