Android之官方MVP例子分析(第一篇tasks包)

本文深入解析MVP架构在TodoApp中的应用,涵盖Presenter、View、Model各组件的职责与交互,以及具体实现案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

官方大牛写的关于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);
    }

}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值