DiyCode开源项目 TopicActivity 分析
Posted Jason_Jan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的的两个抽象方法,用来处理数据,其实 以上是关于DiyCode开源项目 TopicActivity 分析的主要内容,如果未能解决你的问题,请参考以下文章