DiyCode开源项目 TopicActivity 分析

  1.首先看看TopActivity效果。

  

  2.TopicActivity是一个继承BaseActivity的。前面分析过BaseActivity了。主要有一个标题栏,有返回的图标。

  3.贴一下TopicActivity源代码。

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-09 19:39:57
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode.activity;

import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.view.View;

import com.gcssloop.diycode.R;
import com.gcssloop.diycode.base.app.BaseActivity;
import com.gcssloop.diycode.base.app.ViewHolder;
import com.gcssloop.diycode.fragment.NodeTopicListFragment;

/**
 * 查看不同分类的 Topic
 */
public class TopicActivity extends BaseActivity {
    private static String Key_Node_ID = "Key_Node_ID";
    private static String Key_Node_Name = "Key_Node_Name";

    public static void newInstance(Context context, int nodeId, String nodeName) {
        Intent intent = new Intent(context, TopicActivity.class);
        intent.putExtra(Key_Node_ID, nodeId);
        intent.putExtra(Key_Node_Name, nodeName);
        context.startActivity(intent);
    }

    @Override protected int getLayoutId() {
        return R.layout.activity_fragment;
    }

    @Override protected void initViews(ViewHolder holder, View root) {
        Intent intent = getIntent();
        int NodeId = intent.getIntExtra(Key_Node_ID, 0);
        String NodeName = intent.getStringExtra(Key_Node_Name);
        setTitle(NodeName);

        NodeTopicListFragment fragment = NodeTopicListFragment.newInstance(NodeId);
        FragmentManager fragmentManager = getSupportFragmentManager();
        FragmentTransaction transaction = fragmentManager.beginTransaction();
        transaction.add(R.id.fragment, fragment);
        transaction.commit();
    }
}

  4.首先有一个静态函数newInstance,主要就是方便外部调用打开这个Activity吧。

  

  5.实现在BaseActivity中定义的抽象函数getLayoutId()==>主要作用就是得到一个布局的id。

    这个布局activity_fragment源码贴一下吧。

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_topic"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.gcssloop.diycode.activity.TopicActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

  预览效果就是两个部分,一个是一个Toolbar,大概占了1/8,剩下的是一个FrameLayout

 6.实现在BaseActivity中定义的抽象函数initViews(ViewHolder holder,view root)==>主要是加载一些视图。

  首先是得到外部传过来的NodeName,将ActionBar的标题通过函数setTitle来设置。

  

  这里用了一个NodeTopicListFragment,不知道是什么东西?==>原来自己定义的一个碎片。

  贴一下NodeTopicListFragment的定义。

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-09 19:30:45
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode.fragment;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;

import com.gcssloop.diycode.fragment.base.SimpleRefreshRecyclerFragment;
import com.gcssloop.diycode.fragment.provider.TopicProvider;
import com.gcssloop.diycode_sdk.api.topic.bean.Topic;
import com.gcssloop.diycode_sdk.api.topic.event.GetTopicsListEvent;
import com.gcssloop.recyclerview.adapter.multitype.HeaderFooterAdapter;

/**
 * 分类 topic 列表
 */
public class NodeTopicListFragment extends SimpleRefreshRecyclerFragment<Topic, GetTopicsListEvent> {
    private static String Key_Node_ID = "Key_Node_ID";
    private int mNodeId = 0;

    public static NodeTopicListFragment newInstance(int nodeId) {
        Bundle args = new Bundle();
        args.putInt(Key_Node_ID, nodeId);
        NodeTopicListFragment fragment = new NodeTopicListFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override public void initData(HeaderFooterAdapter adapter) {
        mNodeId = getArguments().getInt(Key_Node_ID, 0);
        loadMore();
    }

    @Override
    protected void setAdapterRegister(Context context, RecyclerView recyclerView,
                                      HeaderFooterAdapter adapter) {
        adapter.register(Topic.class, new TopicProvider(getContext()));
    }

    @NonNull @Override protected String request(int offset, int limit) {
        return mDiycode.getTopicsList(null, mNodeId, offset, limit);
    }
}

    6.1但是这个NodeTopicListFragment继承了一个SimpleRefreshRecyclerFragment<Topic,GetTopicListEvent>又不知道什么意思呢!

        贴一下SimpleRefreshRecyclerFragment代码吧。 

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-09 21:16:47
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode.fragment.base;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;

import com.gcssloop.recyclerview.layoutmanager.SpeedyLinearLayoutManager;
import com.gcssloop.diycode_sdk.api.base.event.BaseEvent;
import com.gcssloop.recyclerview.adapter.multitype.HeaderFooterAdapter;

import java.util.List;

public abstract class SimpleRefreshRecyclerFragment<T, Event extends BaseEvent<List<T>>> extends
        RefreshRecyclerFragment<T, Event> {

    @NonNull @Override protected RecyclerView.LayoutManager getRecyclerViewLayoutManager() {
        return new SpeedyLinearLayoutManager(getContext());
    }

    @Override protected void onRefresh(Event event, HeaderFooterAdapter adapter) {
        adapter.clearDatas();
        adapter.addDatas(event.getBean());
        toast("刷新成功");
    }

    @Override protected void onLoadMore(Event event, HeaderFooterAdapter adapter) {
        adapter.addDatas(event.getBean());
    }

    @Override protected void onError(Event event, String postType) {
        if (postType.equals(POST_LOAD_MORE)) {
            toast("加载更多失败");
        } else if (postType.equals(POST_REFRESH)) {
            toast("刷新数据失败");
        }
    }
}

          6.1.1代码中第一段都无法理解:public abstract class SimpleRefreshRecyclerFragment<T,Event extends BaseEvent<List<T>>>  extends RefreshRecyclerFragment<T,Event>。

            首先理解一下这个BaseEvent。

                        /**

               * 所有 Event 的基类
               * <p>
               * T 为对应的实体类
               * <p>
               * HTTP Status
               * -1 - 可能是网络未连接
              * 200, 201 - 请求成功,或执行成功
               * 400 - 参数不符合 API 的要求、或者数据格式验证没有通过,请配合 Response Body 里面的 error 信息确定问题。
              * 401 - 用户认证失败,或缺少认证信息,比如 access_token 过期,或没传,可以尝试用 refresh_token 方式获得新的 access_token。
               * 403 - 当前用户对资源没有操作权限
              * 404 - 资源不存在。
               * 500 - 服务器异常
               */

         然后它继承了一个RefreshRecycleFragment<T,Event>,这个也是在这个项目中的一个类,贴一下这个Fragment代码吧。        

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-09 21:16:47
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode.fragment.base;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v4.util.ArrayMap;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.View;

import com.gcssloop.diycode.R;
import com.gcssloop.diycode.base.app.ViewHolder;
import com.gcssloop.diycode.fragment.bean.Footer;
import com.gcssloop.diycode.fragment.provider.FooterProvider;
import com.gcssloop.diycode_sdk.api.base.event.BaseEvent;
import com.gcssloop.recyclerview.adapter.multitype.HeaderFooterAdapter;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import java.util.List;

import static android.support.v7.recyclerview.R.styleable.RecyclerView;

/**
 * 具有下拉刷新和上拉加载的 Fragment
 */
public abstract class RefreshRecyclerFragment<T, Event extends BaseEvent<List<T>>> extends
        BaseFragment {
    // 请求状态 - 下拉刷新 还是 加载更多
    public static final String POST_LOAD_MORE = "load_more";
    public static final String POST_REFRESH = "refresh";
    private ArrayMap<String, String> mPostTypes = new ArrayMap<>();    // 请求类型

    // 当前状态
    private static final int STATE_NORMAL = 0;      // 正常
    private static final int STATE_NO_MORE = 1;     // 正在
    private static final int STATE_LOADING = 2;     // 加载
    private static final int STATE_REFRESH = 3;     // 刷新
    private int mState = STATE_NORMAL;

    // 分页加载
    protected int pageIndex = 0;                      // 当面页码
    protected int pageCount = 20;                     // 每页个数

    // View
    private SwipeRefreshLayout mRefreshLayout;
    protected RecyclerView mRecyclerView;

    // 状态
    private boolean refreshEnable = true;           // 是否允许刷新
    private boolean loadMoreEnable = true;          // 是否允许加载

    // 适配器
    protected HeaderFooterAdapter mAdapter;
    protected FooterProvider mFooterProvider;

    protected boolean isFirstAddFooter = true;

    @Override
    protected int getLayoutId() {
        return R.layout.fragment_refresh_recycler;
    }

    @Override
    protected void initViews(ViewHolder holder, View root) {
        // 适配器
        mAdapter = new HeaderFooterAdapter();
        mFooterProvider = new FooterProvider(getContext()) {
            @Override
            public void needLoadMore() {
                if (isFirstAddFooter) {
                    isFirstAddFooter = false;
                    return;
                }
                loadMore();
            }
        };
        mFooterProvider.setFooterNormal();
        mAdapter.registerFooter(new Footer(), mFooterProvider);
        // refreshLayout
        mRefreshLayout = holder.get(R.id.refresh_layout);
        mRefreshLayout.setProgressViewOffset(false, -20, 80);
        mRefreshLayout.setColorSchemeColors(getResources().getColor(R.color.diy_red));
        mRefreshLayout.setEnabled(true);
        // RecyclerView
        mRecyclerView = holder.get(R.id.recycler_view);
        mRecyclerView.setHasFixedSize(true);
        mRecyclerView.setAdapter(mAdapter);
        mRecyclerView.setLayoutManager(getRecyclerViewLayoutManager());
        setAdapterRegister(getContext(), mRecyclerView, mAdapter);
        // 监听 RefreshLayout 下拉刷新
        mRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                refresh();
            }
        });
        initData(mAdapter);
    }

    protected void refresh() {
        if (!refreshEnable) return;
        pageIndex = 0;
        String uuid = request(pageIndex * pageCount, pageCount);
        mPostTypes.put(uuid, POST_REFRESH);
        pageIndex++;
        mState = STATE_REFRESH;
    }

    protected void loadMore() {
        if (!loadMoreEnable) return;
        if (mState == STATE_NO_MORE) return;
        String uuid = request(pageIndex * pageCount, pageCount);
        mPostTypes.put(uuid, POST_LOAD_MORE);
        pageIndex++;
        mState = STATE_LOADING;
        mFooterProvider.setFooterLoading();
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onResultEvent(Event event) {
        String postType = mPostTypes.get(event.getUUID());
        if (event.isOk()) {
            if (postType.equals(POST_LOAD_MORE)) {
                onLoadMore(event);
            } else if (postType.equals(POST_REFRESH)) {
                onRefresh(event);
            }
        } else {
            onError(event);
        }
        mPostTypes.remove(event.getUUID());
    }

    protected void onRefresh(Event event) {
        mState = STATE_NORMAL;
        mRefreshLayout.setRefreshing(false);
        onRefresh(event, mAdapter);
    }

    protected void onLoadMore(Event event) {
        if (event.getBean().size() < pageCount) {
            mState = STATE_NO_MORE;
            mFooterProvider.setFooterNormal();
        } else {
            mState = STATE_NORMAL;
            mFooterProvider.setFooterNormal();
        }
        onLoadMore(event, mAdapter);
    }

    protected void onError(Event event) {
        mState = STATE_NORMAL;  // 状态重置为正常,以便可以重试,否则进入异常状态后无法再变为正常状态
        String postType = mPostTypes.get(event.getUUID());
        if (postType.equals(POST_LOAD_MORE)) {
            mFooterProvider.setFooterError(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    pageIndex--;
                    loadMore();
                }
            });
        } else if (postType.equals(POST_REFRESH)) {
            mRefreshLayout.setRefreshing(false);
            mFooterProvider.setFooterNormal();
        }
        onError(event, postType);
    }

    public void setRefreshEnable(boolean refreshEnable) {
        this.refreshEnable = refreshEnable;
        mRefreshLayout.setEnabled(refreshEnable);
    }

    public void setLoadMoreEnable(boolean loadMoreEnable) {
        this.loadMoreEnable = loadMoreEnable;
    }

    public void quickToTop() {
        mRecyclerView.smoothScrollToPosition(0);
    }

    @Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

    //--- 需要继承类处理的部分 ----------------------------------------------------------------------

    /**
     * 加载数初始化数据,可以从缓存或者其他地方加载,
     * 如果没有初始数据,一般调用 loadMore() 即可。
     *
     * @param adapter 适配器
     */
    public abstract void initData(HeaderFooterAdapter adapter);

    /**
     * 为 RecyclerView 的 Adapter 注册数据类型
     * 例如: adapter.register(Bean.class, new BeanProvider(getContext()));
     *
     * @param context      上下文
     * @param recyclerView RecyclerView
     * @param adapter      Adapter
     */
    protected abstract void setAdapterRegister(Context context, RecyclerView recyclerView,
                                               HeaderFooterAdapter adapter);

    /**
     * 获取 RecyclerView 的 LayoutManager
     * 例如: return new LinerLayoutManager(context);
     *
     * @return LayoutManager
     */
    @NonNull protected abstract RecyclerView.LayoutManager getRecyclerViewLayoutManager();

    /**
     * 请求数据,并返回请求的 uuid
     * 例如:return mDiycode.getTopicsList(null, mNodeId, offset, limit);
     *
     * @param offset 偏移量
     * @param limit  请求数量
     * @return uuid
     */
    @NonNull protected abstract String request(int offset, int limit);

    /**
     * 数据刷新成功的回调,由于不同页面可能要对数据进行处理,例如重新排序,清理掉一些无效数据等,所以由子类自己实现,
     * 如果不需要特殊处理,一般像下面这样写就行:
     * adapter.clearDatas();
     * adapter.addDatas(event.geiBean());
     *
     * @param event   Event
     * @param adapter Adapter
     */
    protected abstract void onRefresh(Event event, HeaderFooterAdapter adapter);

    /**
     * 数据加载成功时调用,如果不需要对数据进行特殊处理,这样写就行:
     * adapter.addDatas(event.getBean());
     *
     * @param event   Event
     * @param adapter Adapter
     */
    protected abstract void onLoadMore(Event event, HeaderFooterAdapter adapter);

    /**
     * 数据加载错误时调用,你可以在这里获取错误类型并进行处理,如果不需要特殊处理,弹出一个 toast 提醒用户即可。
     * if (postType.equals(POST_LOAD_MORE)) {
     * toast("加载更多失败");
     * } else if (postType.equals(POST_REFRESH)) {
     * toast("刷新数据失败");
     * }
     *
     * @param event
     * @param postType
     */
    protected abstract void onError(Event event, String postType);
}

            然后这个RefreshRecylerFragment<T,Event extends BaseEvent<List<T>>>继承了一个BaseFragment,也是自己写的一个类。贴一下。

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-09 21:16:47
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode.fragment.base;

import android.os.Bundle;
import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;

import com.gcssloop.diycode.base.app.ViewHolder;
import com.gcssloop.diycode.utils.Config;
import com.gcssloop.diycode.utils.DataCache;
import com.gcssloop.diycode_sdk.api.Diycode;

/**
 * 提供基础内容和生命周期控制
 */
public abstract class BaseFragment extends Fragment {
    private ViewHolder mViewHolder;     // View 管理
    // 数据
    protected Config mConfig;         // 配置(状态信息)
    protected Diycode mDiycode;       // 在线(服务器)
    protected DataCache mDataCache;   // 缓存(本地)

    @Override public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mConfig = Config.getSingleInstance();
        mDiycode = Diycode.getSingleInstance();
        mDataCache = new DataCache(getContext());
    }

    @LayoutRes
    protected abstract int getLayoutId();

    public ViewHolder getViewHolder() {
        return mViewHolder;
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        mViewHolder = new ViewHolder(inflater, container, getLayoutId());
        return mViewHolder.getRootView();
    }

    protected abstract void initViews(ViewHolder holder, View root);

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        initViews(mViewHolder, mViewHolder.getRootView());
    }

    protected void toast(String text) {
        Toast.makeText(getContext(), text, Toast.LENGTH_SHORT).show();
    }
}

            BaseFragment中定义了4个变量。

            这里有3个重要的类,Config定义在Utils中,DataCache也定义在Utils中,都是自己写的类,比较复杂,先不贴了。

            然后在BaseFragement中还有一个onCreate函数,定义了一些初始化的东西。

      

            然后定义了一个抽象函数,用来获得布局资源的id。

            然后一个onCreateView函数,用来获得mViewHolder的。

            然后又定义了一个抽象函数初始化ViewHolder的。在继承者中必须实现的。

            然后执行到onActivityCreated,在 执行initViews()函数。

            然后这里面定义一个toast函数。

            好了,现在又回到RefreshRecyclerFragment中了。==>其实它的作用就是具有下拉刷新和上拉加载的Fragment。和数据一点耦合也没有。因为是继承BaseFragment的,只要实现BaseFragment的的两个抽象方法,用来处理数据,其实就是获得数据,然后利用这个可以下拉刷新和上拉加载的Fragment来处理即可。

          然后这里面有两个适配器。

            

            ==>主要作用就是刷新的一些视图,比价刷新成功,加载失败,加载完毕等。

            然后实现了BaseFragment中的抽象方法==>getLayoutId(),加载的布局文件为:R.layout.fragment_refresh_recycler==>代码如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycler_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

    </android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>

            这里面有一个SwipeRefreshLayout其实就是那个下拉刷新的图标,id为:refresh_layout。           

            然后实现了BaseFragment中的抽象方法==>initViews(ViewHolder holder,View root)从适配器中获得数据。然后在RefreshRecyclerFragment修改了刷新图标的颜色和偏移量。

            这里有布局中id叫做recycler_view的RecyclerView非常关键,如果没有setAdapter,那么所有数据都无法显示了。mAdapter事先注册了一个mFooterProvider数据的。

            然后实现了两个抽象方法:

        

            需要在继承者中实现。然后是监听RefreshLayout下拉刷新:      

          如何刷新呢?

          

          request函数也是一个抽象函数,在继承者中具体实现。

          然后是加载更多==>loadMore()        

          好像都是先一个request函数,返回一个字段,然后将这个字段放在本地的一个ArrayMap中,这个是请求类型,第二个字段是请求类型是“load_more”或者“refresh”。

             这里使用了一个注解:@Subscribe(threadMode=ThreadMode.MAIN)==>表示该函数在主线程即UI线程中执行。              这里是返回事件,即成功刷新,或者成功加载,或者刷新失败,加载失败后的结果。 

            其实这里也只是过渡,其实还是没有处理数据,还是交给继承者来处理。真正的刷新和加载要和mAdapter挂钩的呢。

           这里有一个onError方法,先处理一些状态,判断是刷新还是加载更多,真正的出错还要交给继承者来处理。        
              然后是设置一些基本方法,包括能否刷新,能否加载更多,能否到返回到顶部。              然后是进入onStart()和onStop()环节了。    

              不太了解EventBus?

        ==>EventBus是一个Android事件发布/订阅框架,通过解耦发布者和订阅者简化Android事件传递,这里的事件可以理解为消息。事件传递可以用于Android四大组件间通讯,也可以用于异步线程和主线程间通讯等。传统的事件传递方式包括:Handler,BroadcastReceiver,Interface回调,相比之下EventBus的特点是代码简洁,使用简单,并将事件发布和订阅充分解耦。

        然后是需要继承类处理的部分                  第一个抽象函数是加载初始化数据。                  第二个抽象函数是注册数据类型。          

                   第三个抽象函数获取RecyclerView的LayoutManager。

                  第四个抽象函数是:请求数据了,返回请求的uuid。        

                      第五个抽象函数是onRefresh==>数据刷新成功后的回调。

                          第六个抽象函数是onLoadMore==>数据加载成功是的回调。

    

                      第七个抽象函数是:onError==>数据加载错误时调           

            6.1.2.回到SimpleRefreshRecyclerFragment。其实这三个类是一层接一层            

              ==>BaseFragment->RefreshRecycleFragment->SimpleRefreshRecyclerFragment。

              这个SimpleRefreshRecycleFragment也只是一个抽象类,所以虽然继承了RefreshRecycleFragment,但是并没有时间所有7个抽象方法,只实现了4个。

            6.1.3.第一个是getRecyclerViewLayoutManager==>             这个SpeedyLinearLayoutManager是什么鬼?自己定义的一个类,附上源代码吧!

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-10 20:16:42
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.recyclerview.layoutmanager;

import android.content.Context;
import android.graphics.PointF;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.LinearSmoothScroller;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.util.DisplayMetrics;

/**
 * 支持快速返回的 LinerLayoutManager
 */
public class SpeedyLinearLayoutManager extends LinearLayoutManager {

    private static final float MILLISECONDS_PER_INCH = 6f; //default is 25f (bigger = slower)

    public SpeedyLinearLayoutManager(Context context) {
        super(context);
    }

    public SpeedyLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public SpeedyLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int
            defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int
            position) {

        final LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView
                .getContext()) {

            @Override
            public PointF computeScrollVectorForPosition(int targetPosition) {
                return SpeedyLinearLayoutManager.this.computeScrollVectorForPosition
                        (targetPosition);
            }

            @Override
            protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
            }
        };

        linearSmoothScroller.setTargetPosition(position);
        startSmoothScroll(linearSmoothScroller);
    }
}


                其实是自定义的LinearLayoutManager,这又是啥? ==>就是一个线性布局管理器,这里用到快速到顶部。

            这里复写3个构造函数,在Override一个smoothScrollToPosition函数即可。这是一个支持快速返回的LinearLayoutManager。            如下图是Override的smoothSrollToPosition函数。 

                  这里有一个LinearSmoothScroller,作用就是决定视图滑动的一些属性。这里有一个MILLISECONDS_PER_INCH决定了滑动的速度。如果设置得很大的话,滑动的就比较慢,如果设置的值很小,滑动的速度就很快。

             6.1.4.然后回到SimpleRefreshRecyclerFragment中,第二个Override的函数是onRefresh(),

                    adapter首先清空,然后添加数据。

                这里先了解一下HeaderFooterAdapter。源代码如下:        

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-04-08 16:14:18
 *
 * GitHub: https://github.com/GcsSloop
 * WeiBo: http://weibo.com/GcsSloop
 * WebSite: http://www.gcssloop.com
 */

package com.gcssloop.recyclerview.adapter.multitype;

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;

import com.gcssloop.recyclerview.adapter.base.RecyclerViewHolder;

import java.util.ArrayList;
import java.util.List;

/**
 * 带有头部和底部的适配器
 * 使用步骤:
 * 1. 创建实体类 Bean
 * 2. 创建对应的 provider 并继承自 BaseViewProvider, 在对应的 provider 的 onBindView 里面处理内容
 * 3. 使用 adapter.register(bean, provider.class) 来将数据实体和 provider 对应起来
 * 4. 注册 Header 或者 Footer
 * 5. 将数据 data 使用 ArrayList<Object> 类型存储起来, 使用 adapter.addDatas(data) 添加数据
 * 6. 大功告成
 */
public class HeaderFooterAdapter extends RecyclerView.Adapter<RecyclerViewHolder>
        implements TypePool {
    private List<Object> mItems = new ArrayList<>();
    private MultiTypePool mTypePool;

    private boolean hasHeader = false;
    private boolean hasFooter = false;

    public HeaderFooterAdapter() {
        mTypePool = new MultiTypePool();
    }

    @Override
    public int getItemCount() {
        assert mItems != null;
        return mItems.size();
    }

    @Override
    public int getItemViewType(int position) {
        assert mItems != null;
        Object item = mItems.get(position);
        int index = mTypePool.indexOf(item.getClass());
        if (index >= 0) {
            return index;
        }
        return mTypePool.indexOf(item.getClass());
    }

    @Override
    public void register(@NonNull Class<?> clazz, @NonNull BaseViewProvider provider) {
        mTypePool.register(clazz, provider);
    }

    public void registerHeader(@NonNull Object object, @NonNull BaseViewProvider provider) {
        if (hasHeader) return;
        mTypePool.register(object.getClass(), provider);
        mItems.add(0, object);
        hasHeader = true;
        notifyDataSetChanged();
    }

    public void unRegisterHeader() {
        if (!hasHeader) return;
        mItems.remove(0);
        hasHeader = false;
        notifyDataSetChanged();
    }

    public void registerFooter(@NonNull Object object, @NonNull BaseViewProvider provider) {
        if (hasFooter) return;
        mTypePool.register(object.getClass(), provider);
        mItems.add(object);
        hasFooter = true;
        notifyDataSetChanged();
    }

    public void unRegisterFooter() {
        if (!hasFooter) return;
        mItems.remove(mItems.size() - 1);
        hasFooter = false;
        notifyDataSetChanged();
    }

    @Override
    public RecyclerViewHolder onCreateViewHolder(ViewGroup parent, int indexViewType) {
        BaseViewProvider provider = getProviderByIndex(indexViewType);
        return provider.onCreateViewHolder(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerViewHolder holder, int position) {
        assert mItems != null;
        Object item = mItems.get(position);
        BaseViewProvider provider = getProviderByClass(item.getClass());
        provider.onBindView(holder, item);
    }

    @Override
    public int indexOf(@NonNull Class<?> clazz) {
        return mTypePool.indexOf(clazz);
    }

    @Override
    public List<BaseViewProvider> getProviders() {
        return mTypePool.getProviders();
    }

    @Override
    public BaseViewProvider getProviderByIndex(int index) {
        return mTypePool.getProviderByIndex(index);
    }

    @Override
    public <T extends BaseViewProvider> T getProviderByClass(@NonNull Class<?> clazz) {
        return mTypePool.getProviderByClass(clazz);
    }

    public void addDatas(List<?> items) {
        if (hasFooter) {
            mItems.addAll(mItems.size() - 1, items);
        } else {
            mItems.addAll(items);
        }
        notifyDataSetChanged();
    }

    /**
     * 获取纯数据 (不包含 Header 和 Footer)
     */
    public List<Object> getDatas() {
        int startIndex = 0;
        int endIndex = mItems.size();
        if (hasHeader) {
            startIndex++;
        }
        if (hasFooter) {
            endIndex--;
        }
        return mItems.subList(startIndex, endIndex);
    }

    /**
     * 获取全部数据 (包含 Header 和 Footer)
     */
    public List<Object> getFullDatas() {
        return mItems;
    }

    public void clearDatas() {
        int startIndex = 0;
        int endIndex = mItems.size();
        if (hasHeader) {
            startIndex++;
        }
        if (hasFooter) {
            endIndex--;
        }
        for (int i = endIndex - 1; i >= startIndex; i--) {
            mItems.remove(i);
        }
        notifyDataSetChanged();
    }
}

                     这是一个带有头部和底部的适配器。这个类其实是在另外一个模块中的。现在先不了解了。

                这里之后直接用adapter.addDatas(event.getBean)添加了数据。注意:这里不仅仅只是添加了一个bean,而是一个List<T>,所以可能会有很多数据都放在这里面了。而adapter只要调用一个addDatas方法就行了。

           6.1.5.然后是一个onLoadMore方法。            

              6.1.6.最后是一个onError方法。

        6.2.终于回到NodeTopicListFragment中了。它继承了一个Topic的bean类。看一下Topic的定义吧。   

/*
 * Copyright 2017 GcsSloop
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * Last modified 2017-03-08 01:01:18
 *
 * GitHub:  https://github.com/GcsSloop
 * Website: http://www.gcssloop.com
 * Weibo:   http://weibo.com/GcsSloop
 */

package com.gcssloop.diycode_sdk.api.topic.bean;

import com.gcssloop.diycode_sdk.api.base.bean.Abilities;
import com.gcssloop.diycode_sdk.api.user.bean.User;

import java.io.Serializable;

/**
 * topic 简略信息
 */
public class Topic implements Serializable {
    private int id;                         // 唯一 id
    private String title;                   // 标题
    private String created_at;              // 创建时间
    private String updated_at;              // 更新时间
    private String replied_at;              // 最近一次回复时间
    private int replies_count;              // 回复总数量
    private String node_name;               // 节点名称
    private int node_id;                    // 节点 id
    private int last_reply_user_id;         // 最近一次回复的用户 id
    private String last_reply_user_login;   // 最近一次回复的用户登录名
    private User user;                      // 创建该话题的用户(信息)
    private boolean deleted;                // 是否是被删除的
    private boolean excellent;              // 是否是加精的
    private Abilities abilities;            // 当前用户对该话题拥有的权限

    public void setId(int id) {
        this.id = id;
    }

    public int getId() {
        return this.id;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getTitle() {
        return this.title;
    }

    public void setCreated_at(String created_at) {
        this.created_at = created_at;
    }

    public String getCreated_at() {
        return this.created_at;
    }

    public void setUpdated_at(String updated_at) {
        this.updated_at = updated_at;
    }

    public String getUpdated_at() {
        return this.updated_at;
    }

    public void setReplied_at(String replied_at) {
        this.replied_at = replied_at;
    }

    public String getReplied_at() {
        return this.replied_at;
    }

    public void setReplies_count(int replies_count) {
        this.replies_count = replies_count;
    }

    public int getReplies_count() {
        return this.replies_count;
    }

    public void setNode_name(String node_name) {
        this.node_name = node_name;
    }

    public String getNode_name() {
        return this.node_name;
    }

    public void setNode_id(int node_id) {
        this.node_id = node_id;
    }

    public int getNode_id() {
        return this.node_id;
    }

    public void setLast_reply_user_id(int last_reply_user_id) {
        this.last_reply_user_id = last_reply_user_id;
    }

    public int getLast_reply_user_id() {
        return this.last_reply_user_id;
    }

    public void setLast_reply_user_login(String last_reply_user_login) {
        this.last_reply_user_login = last_reply_user_login;
    }

    public String getLast_reply_user_login() {
        return this.last_reply_user_login;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public User getUser() {
        return this.user;
    }

    public void setDeleted(boolean deleted) {
        this.deleted = deleted;
    }

    public boolean getDeleted() {
        return this.deleted;
    }

    public void setExcellent(boolean excellent) {
        this.excellent = excellent;
    }

    public boolean getExcellent() {
        return this.excellent;
    }

    public void setAbilities(Abilities abilities) {
        this.abilities = abilities;
    }

    public Abilities getAbilities() {
        return this.abilities;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Topic)) {
            return false;
        }
        Topic temp = (Topic) obj;
        if (temp.getId() == getId()) {
            return true;
        }
        return false;
    }
}

          这个也是在diycode_sdk中的API中定义的。

          总共有14个信息。这里面有一个Override==>判断一个对象是否是Topic。

          6.3.==>T对应Topic,==>GetTopicsListEvent对应BaseEvent<List<Topic>>(其实GetTopicsListEvent就是继承BaseEvent<List<Topic>>的),这些在sdk-API中已经表现,就不深究了。

        6.4.首先一个静态方法==>newInstance(int nodeId)返回一个NodeTopicListFragment。

返回一个fragment,可以知道,这个Fragment已经具备了下拉刷新,上拉加载更多的功能。具备了返回到顶部的功能。具备了toast功能。

           6.5.实现了在RefreshRecycleFragment中定义的抽象方法==>initData()

            6.6.实现了在RefreshRecycleFragment中定义的抽象方法==>setAdapterRegister()

          6.7.实现在RefreshRecycleFragment中定义的抽象方法==>request(),这里是调用API了。

      7.回到TopicActivity中。进入initViews方法。  

        其实NodeTopicListFragment就是一个Fragment,可以刷新和加载更多的Fragment,同时也设置了Adapter的注册数据类型,还利用API调用了请求方法。

      8.Android Fragment动态添加 ==>就是将上面那个碎片添加到topicActivity中。

  总结一下吧:

    1.首先这个TopicActivity是继承一个BaseActivity的。就是一个标题栏中左上角,其实这个BaseActivity就是处理标题栏的吧。

    2.然后又一个关键的静态函数newInstance,就是实例化一个Topic吧。可能是外部函数想调用话题这个活动,这个函数的意义就是跳转到话题这个活动。

    3.然后是得到布局的id,这个也是BaseActivity中定义的抽象函数,就是整个界面的布局函数,有一个Toolbar,然后是一个FrameLayout占据了其他所有空间。

    4.然后是实现了BaseActivity中定义的抽象函数,初始化视图,将多次继承的fragment赋给这个FrameLayout。就是将碎片给到这个FrameLayout。采用了动态添加方法。

    5.其实,事先,要从Intent中获得NodeId,才能利用这个NodeId,获得相应的碎片,调用了NodeTopicListFragment的newInstance方法,NodeId就是里面的参数。然后从Intent从获得的NodeName作为标题栏的Title。

    6.这个NodeTopicListFragment其实是非常复杂的,它首先是继承了SimpleRefreshRecyclerFragment<Topic,GetTopicsListEvent>类的,这个SimpleRefreshRecyclerFragment是有一个静态的newInstanse方法,供别人调用。然后是实现了3个抽象方法,分别是initData(),setAdapterRegister(),request()三个函数。

    7.SimpleRefreshRecyclerFragment继承了RefreshRecyclerFragment<T,Event>这个Event是从它自己的构造函数中的Event extends BaseEvent<List<T>>获得的,然后这个BaseEvent其实是API中定义的。然后这个SimpleRefreshRecyclerFragment实现了4个抽象方法,都是和加载或者刷新有关系的。

    8.RefreshRecyclerFragment<T,Event extends BaseEvent<List<T>>> 继承了BaseFragment。它是一个具有下拉刷新和上拉加载的Fragment。这个类比较关键,定义了很多抽象函数。主要处理刷新和加载的根本逻辑。包括刷新的图标的颜色,一页最多有多少个数据行。刷新后的状态的变更,和加载成功或者失败后状态的变更。以及EventBus的注册和解注册。

    9.BaseFragment就是继承Fragment的了。这个就是最基本的碎片了。里面有一个配置状态信息的Config类。有一个在线服务器Diycode类,有一个DataCache缓存本地数据的类。然后定义了两个抽象方法和一个onCreateView函数获得ViewHolder,一个onCreate函数初始化一些数据,onActivityCreate函数初始化视图。

    10.其实这个TopicActivity和很多东西挂钩,现在了解了最外层的东西,知道了基本流程,但是很多细节还是要深究一下,比如刷新和加载,还有自定义的Adapter都是涉及到的东西比较多的。

时间: 2024-11-14 12:02:31

DiyCode开源项目 TopicActivity 分析的相关文章

Diycode开源项目 NodeListFragment分析

1.一些示例文字 第一段 2.一些示例文字 第二段 3.一些示例文字 第三段 4.一些示例文字 第四段 5.一些示例文字 第五段 6.一些示例文字 第六段

开源项目成熟度分析工具-利用github api获取代码库的信息

1.github api github api是http形式的api,功能还是比较丰富的,博主因为项目的原因主要用到的是提取project信息这项功能,返回的数据是JSON格式. api页:https://developer.github.com/v3/ Options: (H) means HTTP/HTTPS only, (F) means FTP only --anyauth Pick "any" authentication method (H) -a, --append Ap

Android开源项目pulltorefresh分析与简单使用

在Android开发中有时我们需要访问网络实时刷新数据,比如QQ好友在线状态最新信息,QQ空间需要显示更多的好友动态信息,EOE论坛客户端显示更多的文章帖子信息等.android-pulltorefresh开源项目提供一个向下滑动即刷新列表的功能,将该项目稍作修改即可应用到自己的项目中. 1.下载地址 https://github.com/johannilsson/android-pulltorefresh 该项目为 Android 应用提供一个向下滑动即刷新列表的功能. 2.工程组成 Pull

开源项目成熟度分析工具-利用github api获代替码库的信息

1.github api github api是http形式的api,功能还是比較丰富的,博主由于项目的原因主要用到的是提取project信息这项功能.返回的数据是JSON格式. api页:https://developer.github.com/v3/ Options: (H) means HTTP/HTTPS only, (F) means FTP only --anyauth Pick "any" authentication method (H) -a, --append Ap

开源项目ets_cache分析

1 背景 当在程序中,有大量的数据需要读写,数据库的响应会延迟,甚至阻塞.缓存可以缓解对数据库访问的压力,而且在内存中数据的读写要比读写硬盘上的数据快. 2 目的 ets_cache是用erlang实现的简单的缓存应用,它的结构简单,但功能强大支持百万级的数据量.其主要是用来存储一些公共访问的数据,这些数据存放在ets表中,借助ets表的高性能对数据进行操作. 3 结构 3.1 源码目录 下图是ets_cache的源码目录截图: include:主要是一些宏定义和引入的一些包.其中定义了默认缓存

Diycode开源项目

1.一些示例文字 第一段 2.一些示例文字 第二段 3.一些示例文字 第三段 4.一些示例文字 第四段 5.一些示例文字 第五段 6.一些示例文字 第六段

C#.NET开源项目、机器学习、商务智能

所以原谅我,不能把所有的都发上来,太杂了,反而不好. 1..NET时间周期处理组件 这个组件很小,主要是对时间日期,特别是处理时间间隔以及时间范围非常方便.虽然.NET自带了时间日期的部分功能,但可能还不强大.这个组件就是增强版本.详细功能可以看项目主页的介绍.在CodeProject: http://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET 2.OxyPlot绘图组件 OxyPlot是一个.NET跨平台的绘图

ospaf-开源项目成熟度分析工具

1.概述 软件成熟度评估的最终目标是帮助软件的可持续发展,并为用户应用提供必要的技术参考.开放源代码软件成熟度评估也不例外.我们通过软件的成熟度评估,形成全面的涉及技术.应用.法律等层面的评价报告,帮助那些正在或潜在的开源软件使用者准确的了解软件的技术特性和应用特性,从而为他们选择适合自身需求的开源软件提供参考.同时报告中涉及的大量评测数据,为开源软件的开发者提供帮助,促进他们有效的改善软件在技术方面和使用方面的质量,使软件不断成熟和可持续的发展. 开放源代码软件由于开发模式和运作模式的独特性,

100offer举办的「寻找实干和坚持的技术力量」开源项目投票排名分析程序

由于100offer举办的「寻找实干和坚持的技术力量」开源项目投票活动没有按照票数排序的功能,所以本文写了个小程序来实现这个功能,代码如下: import org.jsoup.Jsoup; import org.jsoup.nodes.Element; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; /**