/* * 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); }