安卓开发框架(MVP+主流框架+基类+工具类)--- 基类
来源:互联网 发布:php手游平台源码 编辑:程序博客网 时间:2024/05/18 22:41
《安卓开发框架》系列文章 >>>
前言
基类对于开发框架是必不可少的。在基类中一般可以进行以下操作
1.把一些频繁调用的代码封装起来。
2.提供抽象方法给子类实现,从而简化操作、得到更直接的数据。
使用好基类可以减少代码量,方便统一拓展,提高发开效率。
介绍
下面介绍demo中用到的基类,如Activity基类、Fragment基类、Adapter基类、Presenter基类。
基类的具体实现请根据自己的需求来定制,下面仅供参考。
1. Activity基类
结构为: 具体Activity 继承–> ToolbarBaseActivity 继承–> BaseActivity 继承–> AbstractActivity
1.1 AbstractActivity
public abstract class AbstractActivity extends AppCompatActivity { /** * 设置布局 */ protected abstract void setContentLayout(); /** * 初始化控件 */ protected abstract void initView(); /** * 加载数据 */ protected abstract void obtainData(); /** * 初始化监听 */ protected abstract void initEvent();}
AbstractActivity用于提供所有Activity都需要用到的抽象方法。
1.2 BaseActivity
ublic abstract class BaseActivity extends AbstractActivity implements IBaseActivity { //"加载中"的弹窗 private ProgressDialog mProgressDialog; //页面的堆栈管理 private ActivityStackManager mStackManager; //状态栏导航栏颜色工具类 private UltimateBar ultimateBar; //用于控制retrofit的生命周期,以便在destroy或其他状态时终止网络请求 public PublishSubject<LifeCycleEvent> lifecycleSubject = PublishSubject.create(); //该方法用于提供lifecycleSubject(相当于实现了IBaseView中的getLifeSubject抽象方法)。 //方便Presenter中直接通过IBaseView获取lifecycleSubject,而不用每次都作为参数传递过去 public PublishSubject<LifeCycleEvent> getLifeSubject() { return lifecycleSubject; } //一般的rxjava使用场景下,控制Observable的生命周期 public <T> ObservableTransformer<T, T> controlLife(final LifeCycleEvent event) { return new ObservableTransformer<T, T>() { @Override public ObservableSource<T> apply(Observable<T> upstream) { Observable<LifeCycleEvent> lifecycleObservable = lifecycleSubject.filter(new Predicate<LifeCycleEvent>() { @Override public boolean test(LifeCycleEvent lifeCycleEvent) throws Exception { //当生命周期为event状态时,发射事件 return lifeCycleEvent.equals(event); } }).take(1); return upstream.takeUntil(lifecycleObservable); } }; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏 init(); setContentLayout();//由具体的activity实现,设置内容布局ID initBarColor();//初始化状态栏/导航栏颜色,需在设置了布局后再调用 initView();//由具体的activity实现,做视图相关的初始化 obtainData();//由具体的activity实现,做数据的初始化 initEvent();//由具体的activity实现,做事件监听的初始化 } private void init() { mStackManager = ActivityStackManager.getInstance(); mStackManager.pushOneActivity(this); } private void initBarColor() { int color = getResourceColor(R.color.colorPrimary); setBarColor(color, 0, color, 0); } public UltimateBar getUltimateBar() { if (ultimateBar == null) { ultimateBar = new UltimateBar(this); } return ultimateBar; } //设置状态栏、导航栏颜色,第二个参数控制透明度,布局内容不占据状态栏空间 public void setBarColor(int statusColor, int statusAlpha, int navColor, int navAlpha) { getUltimateBar().setColorBar(statusColor, statusAlpha, navColor, navAlpha); } //单独设置状态栏的颜色,第二个参数控制透明度,布局内容不占据状态栏空间 public void setStatusBarColor(int color, int alpha) { getUltimateBar().setColorStatusBar(color, alpha); } //设置状态栏、导航栏颜色(有DrawerLayout时可使用这种),第二个参数控制透明度,布局内容不占据状态栏空间 public void setBarColorForDrawer(int statusColor, int statusAlpha, int navColor, int navAlpha) { getUltimateBar().setColorBarForDrawer(statusColor, statusAlpha, navColor, navAlpha); } //单独设置状态栏的颜色(有DrawerLayout时可使用这种),第二个参数控制透明度,布局内容不占据状态栏空间 public void setStatusBarColorForDrawer(int color, int alpha) { getUltimateBar().setColorBarForDrawer(color, alpha); } //设置半透明的状态栏、导航栏颜色,第二个参数控制透明度,布局内容占据状态栏空间 public void setBarTranslucent(int statusColor, int statusAlpha, int navColor, int navAlpha) { getUltimateBar().setTransparentBar(statusColor, statusAlpha, navColor, navAlpha); } //单独设置半透明的状态栏颜色,第二个参数控制透明度,布局内容不占据状态栏空间 public void setStatusBarTranslucent(int color, int alpha) { getUltimateBar().setColorBarForDrawer(color, alpha); } //设置全透明的状态栏、导航栏颜色,布局内容占据状态栏空间,参数为是否也应用到 public void setTransparentBar(boolean applyNav) { getUltimateBar().setImmersionBar(applyNav); } //隐藏状态栏、导航栏,布局内容占据状态栏导航栏空间,参数为是否也应用到导航栏上 public void hideBar(boolean applyNav) { getUltimateBar().setHideBar(applyNav); } // 只有魅族(Flyme4+),小米(MIUI6+),android(6.0+)可以设置状态栏中图标、字体的颜色模式(深色模式/亮色模式) public boolean setStatusBarMode(boolean isDark) { Window window = getWindow(); return SystemTypeUtil.setStatusBarLightMode(window, isDark); } @Override protected void onPause() { lifecycleSubject.onNext(LifeCycleEvent.PAUSE); super.onPause(); } @Override protected void onStop() { lifecycleSubject.onNext(LifeCycleEvent.STOP); super.onStop(); } @Override protected void onDestroy() { mStackManager.popOneActivity(this); super.onDestroy(); lifecycleSubject.onNext(LifeCycleEvent.DESTROY); } /** * 显示圆形进度对话框 */ public void showLoadingDialog() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(getContext()); } mProgressDialog.showDialog(); } /** * 显示圆形进度对话框(不可关闭) */ public void showNoCancelLoadingDialog() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(getContext()); } mProgressDialog.showDialog(); mProgressDialog.setCancelable(false); } /** * 关闭进度对话框 */ public void dismissLoadingDialog() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(getContext()); } mProgressDialog.dismissDialog(); } /** * 获取页面的堆栈管理 */ public ActivityStackManager getActivityStackManager() { return mStackManager; } @Override public Context getContext() { return this; } @Override public int getResourceColor(@ColorRes int colorId) { return ResourcesCompat.getColor(getResources(), colorId, null); } @Override public String getResourceString(@StringRes int stringId) { return getResources().getString(stringId); } @Override public String getResourceString(@StringRes int id, Object... formatArgs) { return getResources().getString(id, formatArgs); } @Override public Drawable getResourceDrawable(@DrawableRes int id) { return ResourcesCompat.getDrawable(getResources(), id, null); } @Override public void onLowMemory() { LogUtil.e("内存不足"); //清空图片内存缓存(包括Bitmap缓存和未解码图片的缓存) FrescoUtil.clearMemoryCaches(); super.onLowMemory(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_BACK: if (mOnKeyClickListener != null) {//如果没有设置返回事件的监听,则默认finish页面。 mOnKeyClickListener.clickBack(); } else { finish(); } return true; default: return super.onKeyDown(keyCode, event); } } OnKeyClickListener mOnKeyClickListener; public void setOnKeyListener(OnKeyClickListener onKeyClickListener) { this.mOnKeyClickListener = onKeyClickListener; } /** * 按键的监听,供页面设置自定义的按键行为 */ public interface OnKeyClickListener { /** * 点击了返回键 */ void clickBack(); //可加入其它按键事件 }}
BaseActivity的作用如下:
1. 重写onCreate。这样具体的Activity就不需要重写onCreate()方法,而只需重写setContentLayout()、initView()、obtainData()、initEvent()并在其中进行相应的操作即可
2. 设置状态栏导航栏颜色。很多时候,我们需要对app顶部的状态栏以及底部的导航栏(含有虚拟按键的那一栏)进行颜色设置从而实现沉浸式效果。对于Andriod4.4、5.0版本,它们设置颜色的方式有区别,所以需要做兼容处理。demo中使用的是UltimateBar开源库,它内部已经做了兼容操作,并提供了几个方法来设置颜色,如不透明的、半透明的、全透明的、隐藏等。
3. 显示/隐藏loading对话框。通过提供的showLoadingDialog()/showNoCancelLoadingDialog()来弹出loading对话框,通过dismissLoadingDialog()来隐藏loading对话框。
4. Activity栈管理。在onCreate中将activity推入ActivityStackManager栈中,在onDestroy中将activity从ActivityStackManager弹出。这样就可以通过ActivityStackManager来退出某个activity或者退出应用(全部Activity)。
5. 提供Retrofit生命周期控制。 提供lifecycleSubject 以及 getLifeSubject(),在onPause()/onStop()/onDestroy()中发射终止事件,以便控制Retrofit网络请求在页面进入特定状态时终止。
6. 提供RxJava生命周期控制。提供controlLife()方法,用于一般的RxJava使用场景下控制Observable的生命周期。通过Observable.compose(this.<具体类型>controlLife(LifeCycleEvent.DESTROY))即可。
7. 内存不足时采取的措施。重写onLowMemory()并在其中加入内存不足时要采取的措施,比如清空图片内存缓存。
8. 提供资源获取方法。通过getResourceColor、getResourceString、getResourceDrawable、getContext方法便捷地获取相关资源。
9. 简化重写按键事件的操作。 具体的Activity通过setOnKeyListener传入OnKeyClickListener的具体实现即可。
1.3 ToolbarBaseActivity
public abstract class ToolbarBaseActivity extends BaseActivity { LoadLayout mLoadLayout;//加载布局,可以显示各种状态的布局, 如加载中,加载成功, 加载失败, 无数据 @BindView(R.id.base_toolbar) Toolbar mToolbar; @BindView(R.id.tv_toolbar_right) TextView mTvToolbarRight;//toolbar右侧的文字控件 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } public void setContentView(int layoutResId) { super.setContentView(R.layout.activity_base_toolbar); addViewToContainer(layoutResId); init(); } //将布局加入到LoadLayout中 public void addViewToContainer(int layoutResId) { mLoadLayout = (LoadLayout) findViewById(R.id.base_content_layout); View view = getLayoutInflater().inflate(layoutResId, null); mLoadLayout.removeAllViews(); mLoadLayout.addSuccessView(view); } public void init() { ButterKnife.bind(this);//butterknife绑定 mToolbar.setTitle("");//必须再setSupportActionBar之前将标题置为空字符串,否则具体页面设置标题会无效 this.setSupportActionBar(mToolbar); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mOnKeyClickListener != null) {//如果没有设置返回事件的监听,则默认finish页面。 mOnKeyClickListener.clickBack(); } else { finish(); } } }); } /** * 获取toolbar */ public Toolbar getToolbar() { return mToolbar; } /** * 获取加载布局,从而设置各种加载状态 */ public LoadLayout getLoadLayout() { return mLoadLayout; } @Override protected void onStop() { super.onStop(); if (mLoadLayout != null) { mLoadLayout.closeAnim(); } }}
由于本人开发中经常用到Toolbar控件,所以就加入了ToolbarBaseActivity。它的布局文件为R.layout.activity_base_toolbar,具体Activity的布局将添加到R.layout.activity_base_toolbar当中。
ToolbarBaseActivity的作用如下:
1. 包含Toolbar控件。 提供了getToolbar()方便对toolbar进行相关设置
2. ButterKnife绑定(初始化)。 调用了ButterKnife.bind(this);,具体的Activity无需再进行初始化。
3. 切换各种加载状态。 涉及到耗时请求(如网络请求)的项目,往往需要展示“加载中”、“加载成功”、“加载失败”、“无数据”这几种状态的布局。所以自定义了一个加载布局(LoadLayout),加入以上各个状态的视图,并提供切换加载状态的方法。使用步骤如下:
- 在LoadLayout中指定各个状态对应的布局文件
public class LoadLayout extends BaseLoadLayout { //指定加载中的布局文件 private int mLoadingViewId = R.layout.layout_load_loading_view; //指定加载失败的布局文件 private int mFailedViewId = R.layout.layout_load_failed_view; //指定加载后无数据的布局文件 private int mNoDataViewId = R.layout.layout_load_null_data_view; ...}
- 具体的Activity继承基类后,通过ToolbarBaseActivity提供的setContentView()来设置布局,然后就可以调用setLayoutState来切换各种加载状态
public class CollectActivity extends ToolbarBaseActivity { @Override protected void setContentLayout() { setContentView(R.layout.activity_collect); } @Override protected void initView() { //视图相关的初始化操作 } @Override protected void obtainData() { //设置“加载中”状态时要做的事情 getLoadLayout().setOnLoadListener(new OnLoadListener() { @Override public void onLoad() { //...比如开始请求网络数据 } }); //设置页面为“加载中”状态 getLoadLayout().setLayoutState(State.LOADING); //设置页面为“加载成功”状态(即显示的正常的布局R.layout.activity_collect) getLoadLayout().setLayoutState(State.SUCCESS); //设置页面为“加载失败”状态 getLoadLayout().setLayoutState(State.FAILED); //设置页面为“加载后无数据”状态 getLoadLayout().setLayoutState(State.NO_DATA); } @Override protected void initEvent() { //监听事件相关的初始化操作 }}
效果可看demo运行图中的“加载中”和“无数据”状态:
demo中还提供了一个DrawerbaseActivity,它继承ToolbarBaseActivity, 额外添加了侧滑抽屉(由DrawerLayout和NavigationView组成),有兴趣的朋友可以将demo中MovieActivity的基类改成DrawerbaseActivity即可看到具体效果。
2. Fragment基类
public abstract class BaseFragment extends Fragment implements IBaseFragment { private static final String SAVED_STATE = "saved_state"; protected BaseActivity mActivity; //加载布局,可用于设置各种加载状态,也是根布局视图 private LoadLayout mLoadLayout; //根布局视图 private View mContentView; //保存数据 private Bundle mSavedState; //视图是否已经初始化完毕 private boolean isViewReady; //fragment是否处于可见状态 private boolean isFragmentVisible; //是否已经加载过 protected boolean isLoaded; //用于butterknife解绑 private Unbinder unbinder; //用于控制retrofit的生命周期,以便在destroy或其他状态时终止网络请求 public PublishSubject<LifeCycleEvent> lifecycleSubject = PublishSubject.create(); //该方法用于提供lifecycleSubject(相当于实现了IBaseView中的getLifeSubject抽象方法)。 //方便Presenter中直接通过IBaseView获取lifecycleSubject,而不用每次都作为参数传递过去 public PublishSubject<LifeCycleEvent> getLifeSubject() { return lifecycleSubject; } //一般的rxjava使用场景下,控制Observable的生命周期 public <T> ObservableTransformer<T, T> controlLife(final LifeCycleEvent event) { return new ObservableTransformer<T, T>() { @Override public ObservableSource<T> apply(Observable<T> upstream) { Observable<LifeCycleEvent> lifecycleObservable = lifecycleSubject.filter(new Predicate<LifeCycleEvent>() { @Override public boolean test(LifeCycleEvent lifeCycleEvent) throws Exception { //当生命周期为event状态时,发射事件 return lifeCycleEvent.equals(event); } }).take(1); return upstream.takeUntil(lifecycleObservable); } }; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (mContentView == null) { try { mContentView = inflater.inflate(R.layout.fragment_base, container, false); } catch (Resources.NotFoundException e) { } if (mContentView == null) { throw new NullPointerException("根布局的id非法导致根布局为空,请检查后重试!"); } View view = inflater.inflate(setContentLayout(), null); mLoadLayout = (LoadLayout) mContentView; mLoadLayout.addSuccessView(view); unbinder = ButterKnife.bind(this, mContentView); } return mContentView; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //视图准备完毕 isViewReady = true; //如果视图准备完毕且Fragment处于可见状态,则开始初始化操作 if (isViewReady && isFragmentVisible) onFragmentVisiable(); //如果之前有保存数据,则恢复数据 restoreStateFromArguments(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); isFragmentVisible = isVisibleToUser; //如果视图准备完毕且Fragment处于可见状态,则开始初始化操作 if (isViewReady && isFragmentVisible) onFragmentVisiable(); } //设置并返回布局ID protected abstract int setContentLayout(); //做视图相关的初始化 protected abstract void initView(); //来做数据的初始化 protected abstract void obtainData(); //做事件监听的初始化 protected abstract void initEvent(); public void onFragmentVisiable() { if (!isLoaded) { isLoaded = true; initView(); obtainData(); initEvent(); } } @Override public void onAttach(Context context) { super.onAttach(context); mActivity = (BaseActivity) context; } @Override public void onStart() { super.onStart(); } @Override public void onResume() { super.onResume(); } @Override public void onPause() { lifecycleSubject.onNext(LifeCycleEvent.PAUSE); super.onPause(); } @Override public void onStop() { lifecycleSubject.onNext(LifeCycleEvent.STOP); super.onStop(); } @Override public void onDestroy() { super.onDestroy(); lifecycleSubject.onNext(LifeCycleEvent.DESTROY);// RefWatcher refWatcher = MyApplication.getRefWatcher(getActivity());// if (refWatcher != null) refWatcher.watch(this);//内存泄露检测 } /** * 这个函数用于移除根视图 * 因为已经有过父布局的View是不能再次添加到另一个新的父布局上面的 */ @Override public void onDestroyView() { if (mContentView != null) { ViewGroup parent = (ViewGroup) mContentView.getParent(); if (parent != null) { parent.removeView(mContentView); } } isViewReady = false; //保存数据 saveStateToArguments(); super.onDestroyView(); //ButterKnife解绑 if (unbinder != null) unbinder.unbind(); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); //保存数据 saveStateToArguments(); } //获取加载布局,从而设置各种加载状态 public LoadLayout getLoadLayout() { return mLoadLayout; } /** * 该函数可以Find一个被定义在XML中的根视图上的控件 * * @param id 资源id * @return 这个id对应的控件 */ @CheckResult public View findViewById(@IdRes int id) { if (mContentView == null) { throw new NullPointerException("请检查你的根布局id合法性或view不为空后再调用此方法!"); } return mContentView.findViewById(id); } /** * 可重写这个方法来恢复获取之前保存了的数据 */ protected void onRestoreState(@Nullable Bundle savedInstanceState) { } /** * 可重写这个方法来保存数据,将要保存的数据放入bundle中 */ protected void onSaveState(@Nullable Bundle outState) { } /** * 如果之前有保存数据,则恢复数据 * @return false表示第一次加载,true表示有保存的数据 */ private boolean restoreStateFromArguments() { Bundle b = getArguments(); if (b != null) { mSavedState = b.getBundle(SAVED_STATE); if (mSavedState != null) { restoreState(); return true; } } return false; } private void restoreState() { if (mSavedState != null) { onRestoreState(mSavedState); } } //保存数据 private void saveStateToArguments() { if (getView() != null) { mSavedState = saveState(); } if (mSavedState != null) { Bundle b = getArguments(); if (b != null) { b.putBundle(SAVED_STATE, mSavedState); } } } private Bundle saveState() { Bundle state = new Bundle(); onSaveState(state); return state; } @Override public int getResourceColor(@ColorRes int colorId, @Nullable Resources.Theme theme) { return isAdded() ? ResourcesCompat.getColor(getResources(), colorId, null) : 0; } @Override public String getResourceString(@StringRes int stringId) { return isAdded() ? getResources().getString(stringId) : null; } @Override public Drawable getResourceDrawable(@DrawableRes int id) { return isAdded() ? ResourcesCompat.getDrawable(getResources(), id, null) : null; }}
BaseFragment的作用如下:
1. 延迟加载(懒加载)。 如果Fragment与ViewPager结合使用的话,当加载当前Fragment时,上一页和下一页的Fragment都会预先进行加载,这样如果加载的内容很多,容易造成卡顿、速度慢。
延迟主要就是通过在setUserVisibleHint和onActivityCreated中做判断来实现的,当视图准备完毕且Fragment处于可见状态时,才开始进行初始化操作。具体的fragment只需重写setContentLayout()、initView()、obtainData()、initEvent()中进行相应的操作即可。
另外有一点需要注意,如果fragment并不是和Viewpager结合使用,而是通过FragmentManager的Transaction进行add/hide/show的话,那么在显示Fragment的时候,请显式地调用setUserVisibleHint(),如下:
//包含多个Fragment的Activity中的代码//显示或隐藏Fragment,用于切换Fragment的展示private void addOrShowFragment(FragmentTransaction transaction, BaseFragment fragment, String tag) { if (mCurrentFragment == fragment) return; if (!fragment.isAdded()) { transaction.hide(mCurrentFragment).add(R.id.fl_movie, fragment, tag).commit(); } else { transaction.hide(mCurrentFragment).show(fragment).commit(); } //不与ViewPager嵌套的话,需要显式调用setUserVisibleHint mCurrentFragment.setUserVisibleHint(false); mCurrentFragment = fragment; mCurrentFragment.setUserVisibleHint(true);}
- 保存/恢复数据。 fragment在保存和恢复数据方面,要比Activity复杂些,具体可以看这篇文章http://blog.csdn.net/donglynn/article/details/47065999
在onSaveInstanceState和onDestroyView时保存数据,在onActivityCreated中恢复数据。
具体的Fragment,则重写onSaveState方法将要保存的数据存入bundle,重写onRestoreState方法将之前保存的数据从bundle取出。如下: - 提供Retrofit生命周期控制。 提供lifecycleSubject 以及 getLifeSubject(),在onPause()/onStop()/onDestroy()中发射终止事件,以便控制Retrofit网络请求在页面进入特定状态时终止。
- 提供RxJava生命周期控制。提供controlLife()方法,用于一般的RxJava使用场景下控制Observable的生命周期。通过Observable.compose(this.<具体类型>controlLife(LifeCycleEvent.DESTROY))即可。
- 提供资源获取方法。通过getResourceColor、getResourceString、getResourceDrawable方法便捷地获取相关资源。
3. Adapter基类
3.1 RecyclerBaseAdapter
public abstract class RecyclerBaseAdapter<T> extends RecyclerView.Adapter<ViewHolder> implements IRecyclerAdapter<T> { private static final String TAG = RecyclerBaseAdapter.class.getSimpleName(); private List<T> mDataList; private Context mContext; protected RecyclerBaseAdapter(@NonNull Context context, @NonNull List<T> mDataList) { if (context == null) { throw new NullPointerException("context is not allow null!"); } this.mDataList = mDataList; this.mContext = context; } @Override public void onBindViewHolder(final ViewHolder holder, int position) { final int p = holder.getLayoutPosition(); bindDataForView(holder, (mDataList != null && !mDataList.isEmpty()) ? (mDataList.size() > p ? mDataList.get(p) : null) : null, p); } @Override public int getItemCount() { return mDataList != null ? mDataList.size() : 0; } protected abstract void bindDataForView(ViewHolder holder, T t, int position); @Override public Context getContext() { return mContext; } @Override public T getItem(@IntRange(from = 0) int position) { if (position <= -1) { return null; } return !CollectionUtil.isEmpty(mDataList) ? mDataList.get(position) : null; } @Override public List<T> getDataList() { return mDataList; } //从某个位置开始,插入一组数据 @Override public void insertItems(@NonNull List<T> list, @IntRange(from = 0) int startIndex) { if (mDataList == null) { return; } if (list == null || list.isEmpty()) { LogUtil.e(TAG, "插入的数据集为空或长度小于等于零, 请检查你的数据集!"); return; } if (this.mDataList.containsAll(list)) { return; } notifyItemRangeInserted(startIndex, list.size()); mDataList.addAll(startIndex, list); notifyItemRangeChanged(startIndex, getItemCount() - startIndex); } //从最底下插入一组数据 @Override public void insertItems(@NonNull List<T> list) { this.insertItems(list, mDataList.size()); } //从某个位置开始,插入一个数据 @Override public void insertItem(@NonNull T t, @IntRange(from = 0) int position) { if (mDataList == null) { return; } if (t == null) { LogUtil.e(TAG, "插入的数据为空, 请检查你的数据!"); return; } notifyItemInserted(position); mDataList.add(position, t); notifyItemRangeChanged(position, getItemCount() - position); } //从最底下插入一个数据 @Override public void insertItem(@NonNull T t) { this.insertItem(t, mDataList.size()); } //替换所有数据 @Override public void replaceData(@NonNull List<T> list) { if (mDataList == null) { return; } if (list == null || list.isEmpty()) { LogUtil.e(TAG, "插入的数据集为空或长度小于等于零, 请检查你的数据集!"); return; } mDataList = list; notifyDataSetChanged(); } //从某个位置开始,更新n个数据 @Override public void updateItems(@IntRange(from = 0) int positionStart, @IntRange(from = 0) int itemCount) { notifyItemRangeChanged(positionStart, itemCount); } //更新所有数据 @Override public void updateAll() { updateItems(0, mDataList.size()); } //移除某个位置的数据 @Override public void removeItem(@IntRange(from = 0) int position) { if (CollectionUtil.isEmpty(mDataList) || position <= -1) { return; } notifyItemRemoved(position); mDataList.remove(position); notifyItemRangeChanged(position, getItemCount() - position); } //移除所有数据 @Override public void removeAll() { if (CollectionUtil.isEmpty(mDataList)) { return; } notifyItemRangeRemoved(0, getItemCount()); mDataList.clear(); notifyItemRangeChanged(0, getItemCount()); }}
RecyclerBaseAdapter的作用如下:
1. 重写onBindViewHolder并提供抽象方法。 由于列表项实体是不确定的,所以用到了泛型。
具体的Adapter继承该基类,通过泛型传入具体的实体类型,然后重写bindDataForView方法,即可更直接地得到实体数据。
2. 插入、删除、刷新列表项。 提供相关方法,方便子类快速调用。
3.2 LoadMoreBaseAdapter
如果你的RecyclerView需要有上拉加载更多的功能(添加Footer),那么可以继承LoadMoreBaseAdapter。
它继承RecyclerBaseAdapter,添加了Footer并提供了方法来设置footer的状态。
public abstract class LoadMoreBaseAdapter<T> extends RecyclerBaseAdapter<T> { // 普通布局 private final int TYPE_ITEM = 1; // 脚布局 private final int TYPE_FOOTER = 2; // 当前加载状态,默认为加载完成 private int loadState = 2; // 正在加载 public static final int LOADING = 1; // 加载完成 public static final int LOADING_COMPLETE = 2; // 加载到底了(全部数据加载完毕) public static final int LOADING_END = 3; public LoadMoreBaseAdapter(@NonNull Context context, @NonNull List<T> mDataList) { super(context, mDataList); } @Override public int getItemCount() { return super.getItemCount() + 1; } @Override public int getItemViewType(int position) { // 最后一个item设置为FooterView if (position + 1 == getItemCount()) { return TYPE_FOOTER; } else { return TYPE_ITEM; } } @Override protected void bindDataForView(ViewHolder holder, T t, int position) { if (holder.getItemViewType() == TYPE_FOOTER) { ProgressBar pbLoading = holder.getView(R.id.pb_loading); TextView tvLoading = holder.getView(R.id.tv_loading); LinearLayout llEnd = holder.getView(R.id.ll_end); switch (loadState) { case LOADING: // 正在加载 pbLoading.setVisibility(View.VISIBLE); tvLoading.setVisibility(View.VISIBLE); llEnd.setVisibility(View.GONE); break; case LOADING_COMPLETE: // 加载完成 pbLoading.setVisibility(View.INVISIBLE); tvLoading.setVisibility(View.INVISIBLE); llEnd.setVisibility(View.GONE); break; case LOADING_END: // 加载到底 pbLoading.setVisibility(View.GONE); tvLoading.setVisibility(View.GONE); llEnd.setVisibility(View.VISIBLE); break; default: break; } } else { bindDataForView_(holder, t, position); } } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //进行判断显示类型,来创建返回不同的View if (viewType == TYPE_FOOTER) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_load_more_footer, parent, false); return new ViewHolder(view); } else { return onCreateViewHolder_(parent, viewType); } } @Override public void onAttachedToRecyclerView(RecyclerView recyclerView) { super.onAttachedToRecyclerView(recyclerView); RecyclerView.LayoutManager manager = recyclerView.getLayoutManager(); if (manager instanceof GridLayoutManager) { final GridLayoutManager gridManager = ((GridLayoutManager) manager); gridManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { // 如果当前是footer的位置,那么该item占据2个单元格,正常情况下占据1个单元格 return getItemViewType(position) == TYPE_FOOTER ? gridManager.getSpanCount() : 1; } }); } } /** * 设置上拉加载状态 * * @param loadState 0.正在加载 1.加载完成 2.加载到底 */ public void setLoadState(int loadState) { this.loadState = loadState; notifyDataSetChanged(); } protected abstract void bindDataForView_(ViewHolder holder, T t, int position); protected abstract ViewHolder onCreateViewHolder_(ViewGroup parent, int viewType);}
当然,完整的上拉加载更多的流程还要配合列表的滚动监听来实现。完整的流程代码可以查看demo,效果图
4. Presenter基类
不了解MVP模式的可以先看《安卓开发框架(MVP+主流框架+基类+工具类)— MVP模式》
public abstract class BasePresenter<T extends IBaseView> { protected T mIView;//方便子类直接通过该变量调用具体IView的方法 protected WeakReference<BaseActivity> mBaseViewActivity;//对Activity采用弱引用 public BasePresenter(T t, BaseActivity activity) { mIView = t; mBaseViewActivity = new WeakReference<>(activity); } public BasePresenter(BaseActivity activity) { mBaseViewActivity = new WeakReference<>(activity); } public BasePresenter(T t) { mIView = t; } public BasePresenter() { } /** * 销毁对View层的引用,防止内存泄露,一般在activity和fragment销毁时调用 */ public void destroy() { mIView = null; }}
BasePresenter的作用:
1. 提供mIView方便子类调用。 由于IView类型不确定,所以使用了泛型,子类通过泛型传入具体的IView类型。
2. 避免内存泄漏。 对Activity采用弱引用,并提供destroy()方法给V层销毁时调用。
详细代码请看demo,地址:https://github.com/LJYcoder/DevBase
demo的内容流程,请看《安卓开发框架(MVP+主流框架+基类+工具类)— 开篇》
- 安卓开发框架(MVP+主流框架+基类+工具类)--- MVP模式
- 安卓开发框架(MVP+主流框架+基类+工具类)--- 工具类
- 安卓开发框架(MVP+主流框架+基类+工具类)--- 基类
- 安卓开发框架(MVP+主流框架+基类+工具类)--- 开篇
- 安卓开发框架(MVP+主流框架+基类+工具类)--- Retrofit+RxJava
- 安卓开发框架(MVP+主流框架+基类+工具类)--- GreenDAO
- 安卓开发框架(MVP+主流框架+基类+工具类)--- Fresco
- 安卓开发框架(MVP+主流框架+基类+工具类)--- EventBus
- 安卓开发框架(MVP+主流框架+基类+工具类)--- ButterKnife
- 安卓主流框架整理
- 浅谈安卓框架mvp
- 安卓主流框架整理(更新中)
- 简述安卓框架发展史二(mvp,mvvm)
- 安卓MVC vs MVP 框架
- 安卓开发框架
- 安卓开发框架
- Java主流开发框架
- MVP快速开发框架
- Activity Handler 使用 WeakReference
- 盘点遭遇WannaCry勒索病毒影响的几大受害者
- spring cloud sidecar
- 配置JNDI(坑爹货)
- C语言提高-27讲: 编写查找和排序函数(查成绩)
- 安卓开发框架(MVP+主流框架+基类+工具类)--- 基类
- 使用本地域名 访问本地项目,本地host文件配置
- Linux 下网络配置
- Node VS C#
- 《了不起的盖茨比》:梦想的力量与方向
- Intel/美光的3D Xpoint能够撑起未来存储的一片天吗?
- 极简工作法 第一章
- android 收不到ACTION_MEDIA_MOUNTED 广播
- webapi 开发日记