官方大牛写的关于MVP的例子,应用名称叫做todoapp(大牛不喜欢驼峰
0、作者是大牛,包名是按照页面名字(比如添加任务页)去命名的(没有用那种按照组件去分package),厉害,谁让我比较烂,所以大家都很牛,这也是大型App常见的命名方式
1、先看下最外层的两个interface
public interface BasePresenter {
void start(); //一个开始的方法
}
public interface BaseView<T> {
void setPresenter(T presenter); //设置Presenter
}
1-1、tasks包是首页的所有逻辑哦,也是本篇下面会介绍的就是这些文件哦(有class、interface、enum)
2、继续我们第一篇tasks包下的interface
/**
* This specifies the contract between the view and the presenter.
* 大牛特别用一个Contract,然后将View和Presenter作为内部接口(默认public static)
*/
public interface TasksContract { //非要把Presenter和View放在一个interface里啊
/**
* View下全部是根据业务逻辑,页面中的View应该怎么显示的方法(当前首页View要根据当前首页的业务逻辑怎么显示)
* 比如加载Task时,View要先有loading
* 比如从数据库加载完Task到内存后,View要展示Task List
* 比如要新建一个Task,View上就是要展示EditTask页给用户
* 要查看Task的详细情况,View上就是展示Task详情页给用户
* 标记Task为活动状态时,View上要给用户一个提示
* 标记Task为完成状态时,View上也要给用户一个提示
* 当业务上没有Task时,View也要展示一个空白图片给用户
* 当加载Task出错时,View上也要给用户一个出错的图片给用户
* 根据过滤标签,View展示对应的Task,比如已完成、正在活动的过滤标签
* 当过滤Task没有时,View上有个容错,提示用户没有已完成Task、或者没有正在活动的Task,这些展示的都是个图片提示
* 当用户添加Task成功后,View上也要有个提示,告诉用户你成功了
* View上还有一个判断Task是否为活动状态(其实是View是否加载完成,不是指Task真正活动的意思)
* 当用户要选择过滤标签时,View上要弹出一个二级菜单给用户
*/
interface View extends BaseView<Presenter> { //View接口也不少方法,当然别忘了,还有个setPresenter(Presenter p),在BaseView
//意思是若作为一个Tasks首页的页面,应该具备哪些能力
void setLoadingIndicator(boolean active); //展示加载Loading
void showTasks(List<Task> tasks); //加载完Task,需要展示所有的Task,传入一个包含Task的线性表
void showAddTask(); //意思是首页可以是添加的任务的入口,那就是打开新增Task页面
void showTaskDetailsUi(String taskId); //根据任务id,打开Task详情页
void showTaskMarkedComplete();//显示Task标记成功的Toast
void showTaskMarkedActive(); //展示Task标记为Active状态成功的Toast
void showCompletedTasksCleared(); //展示一个已完成Task被标记为Cleared的提示,这里是个SnackBar
void showLoadingTasksError(); //展示加载Task时出错的View
void showNoTasks(); //展示没有Task时的view
void showActiveFilterLabel(); //展示过滤为已活动Task的标签
void showCompletedFilterLabel(); //展示过滤为已完成Task的标签
void showAllFilterLabel(); //展示过滤为所有Task的标签
void showNoActiveTasks(); //展示选择过滤后,没有正活动Task时的View
void showNoCompletedTasks(); //展示选择过滤后,没有已完成Task的View
void showSuccessfullySavedMessage(); //展示保存任务成功时的View,一个SnackBar
boolean isActive(); //判断是否是活动状态,貌似是Fragment是否为Attached状态
void showFilteringPopUpMenu(); //过滤Task时要展示的二级菜单
}
/**
* Presenter下全部是业务逻辑(就是我写的业务case啊)支持的功能,比如要新建Task、设置过滤类型、删除标记为完成的任务、标记Task为完成(当前首页所有的业务逻辑)
* 标记Task为活动、查看Task详情、加载Task到页面
*/
interface Presenter extends BasePresenter { //BasePresenter里面就一个start()方法
void result(int requestCode, int resultCode); //结果,打开第二个组件,关掉后,第二个组件传递过来值,然后会回调到该方法,这是个回调方法
void loadTasks(boolean forceUpdate); //加载Task,支持是否强制更新,在加载Task的业务逻辑上,View上会有很多变化
//加载未完成时,要展示loading、加载完成后,loading隐藏,显示Task,如果有提示,就弹出toast
//这些全部放到View上去处理
void addNewTask(); //添加一个新的Task,业务逻辑上是要打开编辑Task页
void openTaskDetails(@NonNull Task requestedTask); //打开Task详情页,具体的业务是点击RecyclerView中的Item
void completeTask(@NonNull Task completedTask); //把一个Task标记为已完成状态
void activateTask(@NonNull Task activeTask); //把一个Task标记为活动状态
void clearCompletedTasks(); //删除列表中标记的已完成的Task
void setFiltering(TasksFilterType requestType); //支持设置过滤的类型,应该是Task在筛选时可以设定条件
TasksFilterType getFiltering(); //得到过滤Task的分类类型
}
}
3、接着是Activity
/**
* TasksRepository就是M(Model)了,TasksPresenter当然是P(Presenter)了,TasksFragment就是V(View) ,我分析的牛逼到位吗?
* 我这个13装的尼玛真像…………
*/
public class TasksActivity extends AppCompatActivity {
private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY";
private DrawerLayout mDrawerLayout; // 当前Activity的根布局
private TasksPresenter mTasksPresenter; //这里要拿到P,看来要在Activity中创建P了,而M是在P中创建的,V也是在Activity中创建的
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tasks_act); //设置Layout
// Set up the toolbar
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
ab.setHomeAsUpIndicator(R.drawable.ic_menu); //设置左上角指示器
ab.setDisplayHomeAsUpEnabled(true); //设置显示home键?妈蛋?
// Set up the navigation drawer.
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark);
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
if (navigationView != null) {
setupDrawerContent(navigationView);
}
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); //通过一个占位View的id找Fragment
if (tasksFragment == null) { //上面找不到Fragment的话,就创建哈哈
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity( //把Fragment交给Activity管理,并放到占位的FrameLayout上
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// Create the presenter ,在这里将TasksFragment传过去了,在这里将fragment、tasksRepository以及和presenter绑定在一起,当然还创建一个TasksRepository
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment); //创建一个P,把M也创建了,作为V的Fragment就更不用说了
// Load previously saved state, if available.
if (savedInstanceState != null) { //这里只是保留了Activity意外被干掉后,存储的一个filter
TasksFilterType currentFiltering =
(TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);
mTasksPresenter.setFiltering(currentFiltering); //然后传到p里,设置为崩溃前保留的FILTER
}
}
/**
* 进程被意外干掉、或者Activity Task中 Activity被意外干掉时,回调的方法
*
* @param outState
*/
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putSerializable(CURRENT_FILTERING_KEY, mTasksPresenter.getFiltering()); //我去,序列化,把对象干到文件流里,从P里取filter,
super.onSaveInstanceState(outState); //还是调用一下基类的onSaveInstanceState方法
}
/**
* 左上角item的监听器事件
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { //判断item
case android.R.id.home: //如果是那个首页的item(就左上角那个)
// Open the navigation drawer when the home icon is selected from the toolbar.
mDrawerLayout.openDrawer(GravityCompat.START); //打开左侧抽屉
return true;
}
return super.onOptionsItemSelected(item); //没明白这个返回值是干鸡毛的
}
/**
* 初始化左侧边栏的Item
* @param navigationView
*/
private void setupDrawerContent(NavigationView navigationView) {
navigationView.setNavigationItemSelectedListener( //给navigationView设置监听器对象
new NavigationView.OnNavigationItemSelectedListener() { //这边直接new一个匿名监听器对象
@Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
switch (menuItem.getItemId()) { //开始判断点击的哪个Item了
case R.id.list_navigation_menu_item: //如果点击menu_item ,什么也不做
// Do nothing, we're already on that screen
break;
case R.id.statistics_navigation_menu_item: //如果点击statistics的item
Intent intent =
new Intent(TasksActivity.this, StatisticsActivity.class);
startActivity(intent); //跳转到StatisticsActivity
break;
default:
break;
}
// Close the navigation drawer when an item is selected.
menuItem.setChecked(true); //这是更新view的状态吗?麻痹
mDrawerLayout.closeDrawers(); //选中item后,关闭左侧边栏
return true; //返回true,代表事件消费了吧?谁知道呢?
}
});
}
/**
* 单元测试都写的这么严谨,服了老大了。
* @return
*/
@VisibleForTesting
public IdlingResource getCountingIdlingResource() {
return EspressoIdlingResource.getIdlingResource();
}
}
3、其中一个枚举类
/**
* Used with the filter spinner in the tasks list.
* 作用在Tasks列表下的过滤下拉框
* 亲爱的枚举类
*/
public enum TasksFilterType {
/**
* Do not filter tasks.
* 不要过滤Tasks
*/
ALL_TASKS, //每个作为TaskFilterType的对象, 在枚举中的的index为 0
/**
* Filters only the active (not completed yet) tasks.
* 过滤为只有活动状态的Tasks
*/
ACTIVE_TASKS, //index == 1
/**
* Filters only the completed tasks.
* 过滤为只有完成状态的Tasks
*/
COMPLETED_TASKS //在枚举中,最后一个对象是不用加逗号的 index == 2
}
4、一个自定义View
/**
* Extends {@link SwipeRefreshLayout} to support non-direct descendant scrolling views.
* <p>
* {@link SwipeRefreshLayout} works as expected when a scroll view is a direct child: it triggers
* the refresh only when the view is on top. This class adds a way (@link #setScrollUpChild} to
* define which view controls this behavior.
* 我去,这个控件,我明显不熟啊,我好垃圾啊
*/
public class ScrollChildSwipeRefreshLayout extends SwipeRefreshLayout {
private View mScrollUpChild;
public ScrollChildSwipeRefreshLayout(Context context) {
super(context);
}
public ScrollChildSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean canChildScrollUp() {
if (mScrollUpChild != null) {
return ViewCompat.canScrollVertically(mScrollUpChild, -1);
}
return super.canChildScrollUp();
}
public void setScrollUpChild(View view) {
mScrollUpChild = view;
}
}
5、Presenter实现类
/**
* Listens to user actions from the UI ({@link TasksFragment}), retrieves the data and updates the
* UI as required. 从TasksFragment的UI监听用户行为,检索数据并根据需要更新UI
*/
public class TasksPresenter implements TasksContract.Presenter { //Tasks Presenter的实现类
private final TasksRepository mTasksRepository; //Model
private final TasksContract.View mTasksView; //View
private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS; //Task标签,默认为ALL_TASKS
private boolean mFirstLoad = true; //标志位,标记是否为第一次加载, 默认为true
/**
* 构造方法,在TasksActivity下进行的初始化
* @param tasksRepository
* @param tasksView
*/
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); //传入的M,即Repository
mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); //传入的V,即Fragment
mTasksView.setPresenter(this); //这里将Presenter传到Fragment中,牛逼,因为是先创建的Fragment,所以这里靠谱,调用TasksFragment的实例方法
}
/**
* 这个方法在Fragment下调用,即V下调用,目的就是加载Task,调用了loadTasks(false)方法
*/
@Override
public void start() {
loadTasks(false);
}
/**
* 在TasksActivity下打开的组件,即Activity后,关掉后,会调用onActivityResult,然后会调用该result()方法
* @param requestCode
* @param resultCode
*/
@Override
public void result(int requestCode, int resultCode) {
// If a task was successfully added, show snackbar
// 如果 一个 Task 成功添加, 展示一个SnackBar
// 先判断requestCode是否为REQUEST_ADD_TASK,接着判断resultCode是否为RESULT_OK,均为True时
if (AddEditTaskActivity.REQUEST_ADD_TASK == requestCode && Activity.RESULT_OK == resultCode) {
mTasksView.showSuccessfullySavedMessage(); //调用V的展示成功保存Task的SnackBar
}
}
/**
* 加载Task的实现方法
* @param forceUpdate 是否强制刷新
*/
@Override
public void loadTasks(boolean forceUpdate) {
// Simplification for sample: a network reload will be forced on first load.
loadTasks(forceUpdate || mFirstLoad, true); //如果要求强制刷新或者firstLoad,另外要求展示加载Task时的Ui
mFirstLoad = false; //将首次加载Task标志位 赋值为 false
}
/**
* @param forceUpdate Pass in true to refresh the data in the {@link TasksDataSource}
* @param showLoadingUI Pass in true to display a loading icon in the UI
*/
private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {
if (showLoadingUI) { //如果需要展示Loading UI
mTasksView.setLoadingIndicator(true); //设置加载Ui
}
if (forceUpdate) { //如果需要强制刷新
mTasksRepository.refreshTasks(); //调用Repository的刷新Task
}
// The network request might be handled in a different thread so make sure Espresso knows
// that the app is busy until the response is handled.
EspressoIdlingResource.increment(); // App is busy until further notice 这是Ui自动化测试部分
// 获取任务,new了一个数据源对象,传给getTasks()方法,数据源对象,是个你匿名对象
mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
List<Task> tasksToShow = new ArrayList<Task>(); //又拿一个线性表,用来过滤传进来的tasks
// This callback may be called twice, once for the cache and once for loading
// the data from the server API, so we check before decrementing, otherwise
// it throws "Counter has been corrupted!" exception.
if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {
EspressoIdlingResource.decrement(); // Set app as idle. //自动化Ui部分
}
// We filter the tasks based on the requestType
for (Task task : tasks) { //哈哈遍历所有传进来的List的所有Task
switch (mCurrentFiltering) { //根据不同的过滤标签
case ALL_TASKS:
tasksToShow.add(task); //加入到前面创建的要展示Task的List里面
break;
case ACTIVE_TASKS: //活动任务
if (task.isActive()) { //判断Task是否为活动状态
tasksToShow.add(task); //如果是,加入到要展示Task的List里面
}
break;
case COMPLETED_TASKS: //已完成任务
if (task.isCompleted()) { //判断Task是否为完成状态
tasksToShow.add(task); //如果是,加入到要展示到线性表里
}
break;
default:
tasksToShow.add(task); //如果没有过滤标签,直接全部加入 tasksToShow里面,关键是这个枚举,有可能是空的吗?
break;
}
}
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) { //这牛逼,还要判断Fragment有没有加入到Activity中,大神牛逼
return; //方法在这里中断,肯定是有目的,如果Fragment没有加入Activity中的话,直接break
}
if (showLoadingUI) { //如果展示过加载View
mTasksView.setLoadingIndicator(false); //这里把加载的View gone掉
}
processTasks(tasksToShow); //把要展示的Task的List传到processTasks方法里
}
/**
* Task数据如果没有获取到,回调到方法
*/
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (!mTasksView.isActive()) {
return; //如果Fragment没有加进来,直接中断方法
}
mTasksView.showLoadingTasksError(); //在V ,Fragment中展示加载Task错误的View
}
});
}
private void processTasks(List<Task> tasks) { //把要展示的Tasks传过来
if (tasks.isEmpty()) { //牛币,上来就判断tasks有没有元素
// Show a message indicating there are no tasks for that filter type.
processEmptyTasks(); //要是一个元素也没有,就调用processEmptyTasks
} else { //要是有元素的话
// Show the list of tasks
mTasksView.showTasks(tasks); //交给Fragment展示所有Task
// Set the filter label's text.
showFilterLabel(); //展示Fragment下的标签
}
}
private void showFilterLabel() { //根据当前选中的filter,会给你展示不同的标签View
switch (mCurrentFiltering) { //当前的过滤条件
case ACTIVE_TASKS: //活动Task
mTasksView.showActiveFilterLabel(); //调用V的,即Fragment下的showActiveFilterLabel()
break;
case COMPLETED_TASKS: //已完成Task
mTasksView.showCompletedFilterLabel(); //调用V的,即Fragment下的showCompletedFilterLabel()
break;
default:
mTasksView.showAllFilterLabel(); //默认展示所有Task的标签
break;
}
}
private void processEmptyTasks() { //我草,连空任务时,都写的这么严谨?对啊,不同的任务类型展示不同的View
switch (mCurrentFiltering) { //根据不同的filter,展示不同的View,作者咋这么牛逼
case ACTIVE_TASKS:
mTasksView.showNoActiveTasks(); //没有活动的Task时,展示中间的View
break;
case COMPLETED_TASKS:
mTasksView.showNoCompletedTasks(); //没有完成的Task时,展示中间的View
break;
default:
mTasksView.showNoTasks(); //没有Task时,展示中间的View
break;
}
}
/**
* 业务逻辑,添加一条新的Task,怎么添加呢?该业务逻辑,会让TasksFragment去打开编辑页,即View上的操作
*/
@Override
public void addNewTask() {
mTasksView.showAddTask();
}
/**
* 打开Task详情页
* @param requestedTask 传入要打开的Task对象
*/
@Override
public void openTaskDetails(@NonNull Task requestedTask) {
checkNotNull(requestedTask, "requestedTask cannot be null!"); //先判断是否为null
mTasksView.showTaskDetailsUi(requestedTask.getId()); //拿Task的id,然后打开到Task详情页,同样是调用的Fragment下的showTaskDetailsUi方法
}
/**
* 已完成任务
* @param completedTask 传入要完成的Task
*/
@Override
public void completeTask(@NonNull Task completedTask) {
checkNotNull(completedTask, "completedTask cannot be null!");
mTasksRepository.completeTask(completedTask); //去Model里标记为已完成Task
mTasksView.showTaskMarkedComplete(); //展示已经标记Task为completed的提示
loadTasks(false, false); //不知道为啥要在这里调用这个方法
}
/**
* 活动Task
* @param activeTask 要变为活动Task的Task对象
*/
@Override
public void activateTask(@NonNull Task activeTask) {
checkNotNull(activeTask, "activeTask cannot be null!"); //先检查不能为空
mTasksRepository.activateTask(activeTask); //将Task仓库中的Task先修改为活动状态,让我们进去看看
mTasksView.showTaskMarkedActive(); //展示一个已经标记的Toast
loadTasks(false, false); //加载Task,刷新View
}
/**
* 清空已经处于Completed状态的Task
*/
@Override
public void clearCompletedTasks() {
mTasksRepository.clearCompletedTasks(); //去仓库中清空Completed的Task,让我们进去看看都做了什么
mTasksView.showCompletedTasksCleared();
loadTasks(false, false);
}
/**
* Sets the current task filtering type. 设置当前Task的过滤类型
*
* @param requestType Can be {@link TasksFilterType#ALL_TASKS},
* {@link TasksFilterType#COMPLETED_TASKS}, or
* {@link TasksFilterType#ACTIVE_TASKS}
*/
@Override
public void setFiltering(TasksFilterType requestType) {
mCurrentFiltering = requestType;
}
@Override
public TasksFilterType getFiltering() {
return mCurrentFiltering;
}
}
6、Fragment
/**
* Display a grid of {@link Task}s. User can choose to view all, active or completed tasks.
* 这个Fragment是TaskActivity下真正的V,实现了最重要的interface View,即TasksContract.View,里面都是TasksFragment的本身、或者与其他组件通信后的逻辑交互(主要是View的变化)
*/
public class TasksFragment extends Fragment implements TasksContract.View { //任务列表,最最重要的Fragment
private TasksContract.Presenter mPresenter; //V中也要拿到Presenter嘛,虽然TasksFragment作为V已经传递给P了
private TasksAdapter mListAdapter; //适配器。。。,没办法TasksFragment就是个RecyclerView
private View mNoTasksView; //没数据时展示的View
private ImageView mNoTaskIcon; //没数据时展示的Icon
private TextView mNoTaskMainView; //没有Task时的主要View
private TextView mNoTaskAddView; //没有数据时,展示添加的View
private LinearLayout mTasksView;
private TextView mFilteringLabelView;
public TasksFragment() { //必须有一个空的public构造方法,谁用Fragment谁知道。。。。
// Requires empty public constructor
}
public static TasksFragment newInstance() { //返回一个Fragment对象,尼玛。。。
return new TasksFragment();
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mListAdapter = new TasksAdapter(new ArrayList<Task>(0), mItemListener); //初始化TasksAdapter
//初始化的由数组组成的线性表,初始化容量为0,
}
@Override
public void onResume() {
super.onResume();
mPresenter.start(); //看来在Fragment建立完后,就会调用Presenter的start()方法,这里面又调用了onRefresh,所以每次初始化的时候,你都看见执行下拉刷新一次
}
/**
* 这个方法用于初始化在TasksFragment下的Presenter
* @param presenter
*/
@Override
public void setPresenter(@NonNull TasksContract.Presenter presenter) {
mPresenter = checkNotNull(presenter); //只要presenter不为null,直接赋值给mPresenter
}
/**
* startActivityForResult调用后,启动一个组件,会再调用该方法,有一个请求码、一个结果码,还有Intent,这里有存疑,用的太少了,这个方法
* @param requestCode
* @param resultCode
* @param data
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
mPresenter.result(requestCode, resultCode);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.tasks_frag, container, false);
// Set up tasks view
ListView listView = (ListView) root.findViewById(R.id.tasks_list);
listView.setAdapter(mListAdapter);
mFilteringLabelView = (TextView) root.findViewById(R.id.filteringLabel);
mTasksView = (LinearLayout) root.findViewById(R.id.tasksLL);
// Set up no tasks view
mNoTasksView = root.findViewById(R.id.noTasks);
mNoTaskIcon = (ImageView) root.findViewById(R.id.noTasksIcon);
mNoTaskMainView = (TextView) root.findViewById(R.id.noTasksMain);
mNoTaskAddView = (TextView) root.findViewById(R.id.noTasksAdd);
mNoTaskAddView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showAddTask(); //这是没有Task的时候,点击空白View的事件处理
}
});
// Set up floating action button 就是首页那个红个的plus
FloatingActionButton fab =
(FloatingActionButton) getActivity().findViewById(R.id.fab_add_task);
fab.setImageResource(R.drawable.ic_add); //在这里加了图标
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPresenter.addNewTask(); //加了事件,同样,P中的addNewTasks()方法最终也是调用Fragment的showAddTask(),即V的showAddTask
}
});
// Set up progress indicator 这是下拉刷新的控件
final ScrollChildSwipeRefreshLayout swipeRefreshLayout =
(ScrollChildSwipeRefreshLayout) root.findViewById(R.id.refresh_layout);
swipeRefreshLayout.setColorSchemeColors(
ContextCompat.getColor(getActivity(), R.color.colorPrimary),
ContextCompat.getColor(getActivity(), R.color.colorAccent),
ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark)
);
// Set the scrolling view in the custom SwipeRefreshLayout.
swipeRefreshLayout.setScrollUpChild(listView);
swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
mPresenter.loadTasks(false); //对下拉刷新的事件处理,可见是调用loadTasks()方法,貌似不是强制刷新
}
});
setHasOptionsMenu(true); //莫非这里是true,才显示右上角的OptionMenu?Fragment下还有这个逻辑哈?醉了
return root; //返回inflate的布局
}
/**
* 这里是Options点击事件的处理,尼玛,爽了
* @param item
* @return
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_clear: //清除按钮的处理
mPresenter.clearCompletedTasks(); //前去清空所有标记为Completed的Task
break;
case R.id.menu_filter: //过滤按钮的处理
showFilteringPopUpMenu(); //因为还有一集菜单,所以这里用了这个
break;
case R.id.menu_refresh: //刷新按钮的处理
mPresenter.loadTasks(true); //强制刷新展示的Task
break;
}
return true;
}
/**
* 尼玛,TasksActivity右上角的Item,就在这里创建的,原来只在Activity下有,没想到Fragment下也有
* @param menu
* @param inflater
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.tasks_fragment_menu, menu);
}
/**
* 创建的二级菜单
*/
@Override
public void showFilteringPopUpMenu() {
PopupMenu popup = new PopupMenu(getContext(), getActivity().findViewById(R.id.menu_filter));
popup.getMenuInflater().inflate(R.menu.filter_tasks, popup.getMenu()); //解析二级菜单
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) { //二级菜单的事件处理
switch (item.getItemId()) {
case R.id.active:
mPresenter.setFiltering(TasksFilterType.ACTIVE_TASKS);
break;
case R.id.completed:
mPresenter.setFiltering(TasksFilterType.COMPLETED_TASKS);
break;
default:
mPresenter.setFiltering(TasksFilterType.ALL_TASKS);
break;
}
mPresenter.loadTasks(false);
return true;
}
});
popup.show();
}
/**
* Listener for clicks on tasks in the ListView.
* ListView每一个Item的点击事件,写的太牛逼了把,这个最后还是传递到Adapter中了
* 牛逼
*/
TaskItemListener mItemListener = new TaskItemListener() {
@Override
public void onTaskClick(Task clickedTask) {
mPresenter.openTaskDetails(clickedTask); //打开到任务详情页
}
@Override
public void onCompleteTaskClick(Task completedTask) {
mPresenter.completeTask(completedTask); //标记为完成任务
}
@Override
public void onActivateTaskClick(Task activatedTask) {
mPresenter.activateTask(activatedTask); //标记为活动任务
}
};
@Override
public void setLoadingIndicator(final boolean active) { //标志位,代表是否活动
if (getView() == null) { //要是Fragment下的rootView是null,直接return,中断方法
return;
}
final SwipeRefreshLayout srl =
(SwipeRefreshLayout) getView().findViewById(R.id.refresh_layout); //这不是下拉刷新嘛,要干啥
// Make sure setRefreshing() is called after the layout is done with everything else. //在布局已经完成后,确保刷新被嗲用
srl.post(new Runnable() { //靠,还用个post方法,发出一个Runnable,确保setRefreshing()方法在布局完成后被调用过
@Override
public void run() {
srl.setRefreshing(active); //醉了,还是待用了下拉刷新
}
});
}
@Override
public void showTasks(List<Task> tasks) {
mListAdapter.replaceData(tasks); //哈哈,把Tasks交给ListView的Adapter,替换数据,此时ListView会刷新
mTasksView.setVisibility(View.VISIBLE); //我去要显示整个ListView了
mNoTasksView.setVisibility(View.GONE); //显示任务的时候,当然要gone掉没有任务的View了
}
/**
* 没有标记为活动的任务时,调用该方法,里面肯定是处理View的哈,将View显示出来
* 牛13,用同一个布局玩出了花样,没有任务时、没有活动任务时,没有完成任务时,
*/
@Override
public void showNoActiveTasks() {
showNoTasksViews(
getResources().getString(R.string.no_tasks_active),
R.drawable.ic_check_circle_24dp,
false
);
}
/**
* 没有任务时,View的处理情况
*/
@Override
public void showNoTasks() {
showNoTasksViews(
getResources().getString(R.string.no_tasks_all),
R.drawable.ic_assignment_turned_in_24dp,
true //我竟然帮大神改了个bug,大神这里写的是false。。。
);
}
/**
* 没有已经完成任务时,View的处理情况
*/
@Override
public void showNoCompletedTasks() {
showNoTasksViews(
getResources().getString(R.string.no_tasks_completed),
R.drawable.ic_verified_user_24dp,
false
);
}
/**
* 保存任务成功时,调用的方法
*/
@Override
public void showSuccessfullySavedMessage() {
showMessage(getString(R.string.successfully_saved_task_message));
}
/**
* 这就是各种情况下,没有Task时,调用的方法
* @param mainText 要显示的文案
* @param iconRes 要显示的图标
* @param showAddView 是否显示新增Task的View的标志位
*/
private void showNoTasksViews(String mainText, int iconRes, boolean showAddView) {
mTasksView.setVisibility(View.GONE);
mNoTasksView.setVisibility(View.VISIBLE);
mNoTaskMainView.setText(mainText);
mNoTaskIcon.setImageDrawable(getResources().getDrawable(iconRes));
mNoTaskAddView.setVisibility(showAddView ? View.VISIBLE : View.GONE);
}
/**
* 展示仅有活动Task时的文案label
*/
@Override
public void showActiveFilterLabel() {
mFilteringLabelView.setText(getResources().getString(R.string.label_active));
}
/**
* 展示仅有已完成Task时的文案label
*/
@Override
public void showCompletedFilterLabel() {
mFilteringLabelView.setText(getResources().getString(R.string.label_completed));
}
/**
* 展示即有活动Task,又有已完成Task时的label
*/
@Override
public void showAllFilterLabel() {
mFilteringLabelView.setText(getResources().getString(R.string.label_all));
}
/**
* 点击无任务时的View和红色的Plus按钮,均可跳转到新建Task的页面
*/
@Override
public void showAddTask() {
Intent intent = new Intent(getContext(), AddEditTaskActivity.class);
startActivityForResult(intent, AddEditTaskActivity.REQUEST_ADD_TASK); //果然使用了startActivityForResult方法,当然会回调onActivityResult了
}
/**
* 点击已经存在的Task,跳抓到Task的详情页,大牛巧妙的使用show,确实点击后会展示TaskDetailsUi,牛比
*
* @param taskId 要Task的id,看来Task的id很重要
*/
@Override
public void showTaskDetailsUi(String taskId) {
// in it's own Activity, since it makes more sense that way and it gives us the flexibility
// to show some Intent stubbing.
Intent intent = new Intent(getContext(), TaskDetailActivity.class);
intent.putExtra(TaskDetailActivity.EXTRA_TASK_ID, taskId); //Intent把Task的id传过去了
startActivity(intent);
}
/**
* 展示一个Task被标记为Complete的提示,这里是个SnackBar
*/
@Override
public void showTaskMarkedComplete() {
showMessage(getString(R.string.task_marked_complete));
}
/**
* 展示一个Task标记为Active的提示,这里是个SnackBar
*/
@Override
public void showTaskMarkedActive() {
showMessage(getString(R.string.task_marked_active));
}
/**
* 展示一个已完成Task被标记为Cleared的提示,这里是个SnackBar
*/
@Override
public void showCompletedTasksCleared() {
showMessage(getString(R.string.completed_tasks_cleared));
}
/**
* 展示一个错误的提示
*/
@Override
public void showLoadingTasksError() {
showMessage(getString(R.string.loading_tasks_error));
}
/**
* 嘿嘿,所有提示统一的处理,就是SnackBar
* @param message SnackBar中改变的 内容
*/
private void showMessage(String message) {
Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).show();
}
/**
* 判断是否是活跃状态? 判断Fragment的View是否已经加载好?醉了哦
* @return
*/
@Override
public boolean isActive() {
return isAdded();
}
/**
* private的静态内部类来了,是Adapter,ListView的Adapter,ListView在TasksFragment下,所以,Adapter内部类也放在这里
*/
private static class TasksAdapter extends BaseAdapter {
private List<Task> mTasks; //线性表,持有Task
private TaskItemListener mItemListener; //每一条Task的监听器
/**
* 构造方法
* @param tasks 线性表,里面每个元素为Task,
* @param itemListener TaskItem 监听器
*/
public TasksAdapter(List<Task> tasks, TaskItemListener itemListener) {
setList(tasks);
mItemListener = itemListener;
}
/**
* 替换数据
* @param tasks Task线性表,要新替换的数据
*/
public void replaceData(List<Task> tasks) {
setList(tasks); //设置为新替换的数据
notifyDataSetChanged(); //通知数据已经发生改变,观察者模式,这时候RecyclerView要重绘了吧
}
/**
* 设置数据
* @param tasks 传入的线性表,持有Task
*/
private void setList(List<Task> tasks) {
mTasks = checkNotNull(tasks); //赋值给mTasks
}
/**
*
* @return 返回线性表数量,有多少个元素,即多少个Task
*/
@Override
public int getCount() {
return mTasks.size();
}
/**
* 还好封装好的,不然就是越界的命
* @param i 元素下标
* @return 返回指定索引的元素,即Task
*/
@Override
public Task getItem(int i) {
return mTasks.get(i);
}
/**
* 获取每一个Item的id
* @param i 传入的Item下标
* @return 返回的id
*/
@Override
public long getItemId(int i) {
return i;
}
/**
* 重要方法
* @param i 显示是item的位置
* @param view 嘿嘿,显示是每一个Item的View
* @param viewGroup 是啥?还不知道,郁闷!
* @return
*/
@Override
public View getView(int i, View view, ViewGroup viewGroup) {
View rowView = view; //先做赋值
if (rowView == null) { //如果rowView为null,开始解析一个View
LayoutInflater inflater = LayoutInflater.from(viewGroup.getContext()); //先拿到布局解析器对象,即LayoutInflater
rowView = inflater.inflate(R.layout.task_item, viewGroup, false); //解析一条Item的View
}
final Task task = getItem(i); //嘿嘿,按位置,获得对应的Task
TextView titleTV = (TextView) rowView.findViewById(R.id.title); //获取标题View
titleTV.setText(task.getTitleForList()); //设置标题
CheckBox completeCB = (CheckBox) rowView.findViewById(R.id.complete); //获取checkBox
// Active/completed task UI
completeCB.setChecked(task.isCompleted()); //初始化每一个Item的checkBox状态,根据Task的状态决定,真牛逼
if (task.isCompleted()) { //如果Task已经完成
rowView.setBackgroundDrawable(viewGroup.getContext() //改变item的背景色
.getResources().getDrawable(R.drawable.list_completed_touch_feedback)); //这牛逼
} else { //如果Task仍是活动状态
rowView.setBackgroundDrawable(viewGroup.getContext()
.getResources().getDrawable(R.drawable.touch_feedback)); //改变为活动时的Item颜色
}
completeCB.setOnClickListener(new View.OnClickListener() { //checkBox的监听器
@Override
public void onClick(View v) {
if (!task.isCompleted()) { //每当点击checkBox时,当Task是未完成时
mItemListener.onCompleteTaskClick(task); //调用onCompleteTaskClick方法
} else {
mItemListener.onActivateTaskClick(task); //每当点击checkBox时,当Task已经完成,调用onActivateTaskClick方法
}
}
});
rowView.setOnClickListener(new View.OnClickListener() { //每一个Item的点击监听器
@Override
public void onClick(View view) {
mItemListener.onTaskClick(task); //直接调用onTaskClick()
}
}); //大神这牛逼,把item的所有事件,包括整条Item的交互、单个checkBox的交互,全部放到mItemListener里面,牛逼
return rowView;
}
}
/**
* 这就是Item交互的interface,大神牛逼,牛逼,牛逼,三个牛逼
*/
public interface TaskItemListener {
void onTaskClick(Task clickedTask);
void onCompleteTaskClick(Task completedTask);
void onActivateTaskClick(Task activatedTask);
}
}