关于基类的那些事

安卓基础开发库,让开发简单点。
DevRing & Demo地址https://github.com/LJYcoder/DevRing

前言

基类对于开发是很重要的一部分。在基类中一般可以进行以下操作
1.把一些频繁调用的代码封装起来。
2.提供抽象方法给子类实现,从而简化操作、得到更直接的数据。
使用好基类可以减少代码量,方便统一拓展,提高发开效率。

但由于Java单继承的特性,有时别人提供的基类不一定能应用到自己的项目里去,所以下面也会介绍通过LifecycleCallback的方法来实现Activity/Fragment基类的功能(该方式是从这里学习的)。


介绍

下面介绍demo中用到的基类,如Activity基类、Fragment基类、Adapter基类、Presenter基类。
基类的具体实现请根据自己的需求来定制,下面仅供参考

1. Activity基类

1.1 BaseActivity

public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements IBaseActivity {

    @BindColor(R.color.colorPrimary)
    int mColor;
    @Inject
    @Nullable
    protected P mPresenter;

    protected abstract int getContentLayout();//返回页面布局id
    protected abstract void initView(Bundle savedInstanceState);//做视图相关的初始化工作
    protected abstract void initData(Bundle savedInstanceState);//做数据相关的初始化工作
    protected abstract void initEvent();//做监听事件相关的初始化工作

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getContentLayout() != 0) {
            setContentView(getContentLayout());
            ButterKnife.bind(this);
        }
        initBarColor();//初始化状态栏/导航栏颜色,需在设置了布局后再调用
        initView(savedInstanceState);
        initData(savedInstanceState);
        initEvent();
    }

    private void initBarColor() {
        ColorBar.newColorBuilder()
                .applyNav(true)
                .navColor(mColor).navDepth(0)
                .statusColor(mColor)
                .statusDepth(0)
                .build(this)
                .apply();
    }

    @Override
    public boolean isUseEventBus() {
        return false;
    }

    @Override
    public boolean isUseFragment() {
        return true;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.destroy();
            mPresenter = null;
        }
    }
}

BaseActivity的作用如下:

  1. 重写onCreate。
    这样具体的Activity就不需要重写onCreate()方法,而只需重写getContentLayout()、initView(Bundle savedInstanceState);、initData(Bundle savedInstanceState)、initEvent()并在其中进行相应的操作即可
  2. 设置状态栏导航栏颜色。
    很多时候,我们需要对app顶部的状态栏以及底部的导航栏(含有虚拟按键的那一栏)进行颜色设置从而实现沉浸式效果。对于Andriod4.4、5.0版本,它们设置颜色的方式有区别,所以需要做兼容处理。demo中使用的是UltimateBar开源库,它内部已经做了兼容操作,并提供了几个方法来设置颜色,如不透明的、半透明的、全透明的、隐藏等。
  3. 销毁Presenter层对View层的引用
    这里涉及MVP开发模式,不了解的可以先点这里。由于Activity经常需要销毁Presenter层对View层的引用,所以移至基类中实现。
  4. 实现IBaseActivity接口
    实现该接口,以便通过Application.ActivityLifecycleCallbacks完成部分"基类操作",看完1.2你就懂了。

1.2 通过ActivityLifecycleCallbacks实现基类操作

顾名思义--- Activity生命周期回调。
当注册了该回调用,每个Activity进入相关生命周期时都会触发相关回调。
所以我们可以通过Application.registerActivityLifecycleCallbacks(callback)方法注册回调,然后在ActivityLifecycleCallbacks里实现一些基类操作。
不多说,直接上代码。(代码涉及了一些Dagger2,可以点这里了解)

@Singleton
public class ActivityLifeCallback implements Application.ActivityLifecycleCallbacks {

    @Inject
    SimpleArrayMap<String, IActivityLife> mMapActivityLife;
    @Inject
    Provider<IActivityLife> mActivityLifeProvider;
    @Inject
    Lazy<FragmentLifeCallback> mFragmentLifeCallbackProvider;

    @Inject
    public ActivityLifeCallback() {
    }

    @Override
    public void onActivityCreated(Activity activity, Bundle bundle) {
        if (activity instanceof IBaseActivity) {
            IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
            if (iActivityLife == null) {
                iActivityLife = mActivityLifeProvider.get();
                mMapActivityLife.put(activity.toString(), iActivityLife);
            }
            iActivityLife.onCreate(activity, bundle);
        }

        boolean isUseFragment = activity instanceof IBaseActivity ? ((IBaseActivity) activity).isUseFragment() : true;
        if (activity instanceof FragmentActivity && isUseFragment) {
            ((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(mFragmentLifeCallbackProvider.get(), true);
        }
    }

    @Override
    public void onActivityStarted(Activity activity) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onStart();
        }
    }

    @Override
    public void onActivityResumed(Activity activity) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onResume();
        }
    }

    @Override
    public void onActivityPaused(Activity activity) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onPause();
        }
    }

    @Override
    public void onActivityStopped(Activity activity) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onStop();
        }
    }

    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onSaveInstanceState(bundle);
        }
    }

    @Override
    public void onActivityDestroyed(Activity activity) {
        IActivityLife iActivityLife = mMapActivityLife.get(activity.toString());
        if (iActivityLife != null) {
            iActivityLife.onDestroy();
        }
        mMapActivityLife.remove(activity.toString());
    }

}

我把各生命周期具体要做的事情放到ActivityLife里执行了,下面贴上ActivityLife的代码:

public class ActivityLife implements IActivityLife {

    private Activity mActivity;
    private final PublishSubject<ActivityEvent> mLifecycleSubject = PublishSubject.create();

    @Override
    public void onCreate(Activity activity, Bundle savedInstanceState) {
        mActivity = activity;

        mLifecycleSubject.onNext(ActivityEvent.CREATE);

        DevRing.activityStackManager().pushOneActivity(mActivity);

        if (((IBaseActivity) mActivity).isUseEventBus()) {
            DevRing.busManager().register(mActivity);
        }
    }

    @Override
    public void onStart() {
        mLifecycleSubject.onNext(ActivityEvent.START);
    }

    @Override
    public void onResume() {
        mLifecycleSubject.onNext(ActivityEvent.RESUME);
    }

    @Override
    public void onPause() {
        mLifecycleSubject.onNext(ActivityEvent.PAUSE);
    }

    @Override
    public void onStop() {
        mLifecycleSubject.onNext(ActivityEvent.STOP);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {

    }

    @Override
    public void onDestroy() {
        mLifecycleSubject.onNext(ActivityEvent.DESTROY);

        DevRing.activityStackManager().popOneActivity(mActivity);

        if (((IBaseActivity) mActivity).isUseEventBus()) {
            DevRing.busManager().unregister(mActivity);
        }
        mActivity = null;
    }
}

接下来,只要你的Activity实现了IBaseActivity接口,即可通过ActivityLifecycleCallbacks完成以下"基类操作":

  1. 帮助控制网络请求的生命周期
    通过PublishSubject的操作,在onPause()/onStop()/onDestroy()中发射终止事件,以便控制Retrofit网络请求在页面进入特定状态时终止。
  2. EventBus的订阅/解除订阅
    根据isUseEventBus()来决定是否进行EventBus的注册/注销。
  3. Activity栈管理的入栈与出栈
    以便后面可通过ActivityStackManager进行页面销毁工作。
  4. FragmentLifecycleCallbacks的注册
    根据isUseFragment()来决定是否注册FragmentLifecycleCallbacks。

IBaseActivity接口代码:

public interface IBaseActivity {
    /**
     * 该Activity是否订阅事件总线
     * @return true则自动进行注册/注销操作,false则不注册
     */
    boolean isUseEventBus();

    /**
     * 该Activity是否包含Fragment(是否注册FragmentLifecycleCallbacks)
     * @return
     * 返回false则不注册FragmentLifecycleCallbacks,也就是说{@link FragmentLifeCallback}中的操作不会进行
     */
    boolean isUseFragment();
}

2. Fragment基类

2.1 BaseFragment

public abstract class BaseFragment<P extends BasePresenter> extends Fragment implements IBaseFragment {

    protected Activity mActivity;
    //根布局视图
    private View mContentView;
    //视图是否已经初始化完毕
    private boolean isViewReady;
    //fragment是否处于可见状态
    private boolean isFragmentVisible;
    //是否已经初始化加载过
    protected boolean isLoaded;
    //用于butterknife解绑
    private Unbinder unbinder;
    @Inject
    @Nullable
    protected P mPresenter;

    protected abstract boolean isLazyLoad();//是否使用懒加载 (Fragment可见时才进行初始化操作(以下四个方法))
    protected abstract int getContentLayout();//返回页面布局id
    protected abstract void initView();//做视图相关的初始化工作
    protected abstract void initData();//做数据相关的初始化工作
    protected abstract void initEvent();//做监听事件相关的初始化工作

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        mActivity = (Activity) context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        if (mContentView == null) {
            try {
                mContentView = inflater.inflate(getContentLayout(), container, false);
            } catch (Resources.NotFoundException e) {
                e.printStackTrace();
            }

            Preconditions.checkNotNull(mContentView, "根布局的id非法导致根布局为空,请检查后重试!");

            unbinder = ButterKnife.bind(this, mContentView);
        }
        return mContentView;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        //视图准备完毕
        isViewReady = true;
        if (!isLazyLoad() || isFragmentVisible) {
            init();
        }
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        isFragmentVisible = isVisibleToUser;
        //如果视图准备完毕且Fragment处于可见状态,则开始初始化操作
        if (isLazyLoad() && isViewReady && isFragmentVisible) {
            init();
        }
    }

    public void init() {
        if (!isLoaded) {
            isLoaded = true;
            initView();
            initData();
            initEvent();
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //ButterKnife解绑
        if (unbinder != null) unbinder.unbind();
        isViewReady = false;
        isLoaded = false;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.destroy();
            mPresenter = null;
        }
    }

    @Override
    public void onSaveState(Bundle bundleToSave) {

    }

    @Override
    public void onRestoreState(Bundle bundleToRestore) {

    }

    @Override
    public boolean isUseEventBus() {
        return false;
    }
}

BaseFragment的作用如下:

  1. 延迟加载(懒加载)。 如果Fragment与ViewPager结合使用的话,当加载当前Fragment时,上一页和下一页的Fragment都会预先进行加载,这样如果加载的内容很多,容易造成卡顿、速度慢。
    延迟主要就是通过在setUserVisibleHint和onActivityCreated中做判断来实现的,当视图准备完毕且Fragment处于可见状态时,才开始进行初始化操作。重写isLazyLoad()方法即可决定是否开启懒加载功能。
    然后具体的fragment只需重写getContentLayout()、initView()、initData()、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);
}

  1. 销毁Presenter层对View层的引用
    这里涉及MVP开发模式,不了解的可以先点这里。由于Fragment经常需要销毁Presenter层对View层的引用,所以移至基类中实现。
  2. 实现IBaseFragment接口
    实现该接口,以便通过FragmentManager.FragmentLifecycleCallbacks完成部分"基类操作",看完2.2你就懂了。

2.2 通过FragmentManager.FragmentLifecycleCallbacks实现

同样顾名思义 --- Fragment生命周期回调。
当注册了该回调用,每个Fragment进入相关生命周期时都会触发相关回调。
所以我们可以通过((FragmentActivity) activity).getSupportFragmentManager().registerFragmentLifecycleCallbacks(callback, true)方法注册回调,然后在
FragmentLifecycleCallbacks里实现一些基类操作。
不多说,直接上代码。(代码涉及了一些Dagger2,可以点这里了解)

@Singleton
public class FragmentLifeCallback extends FragmentManager.FragmentLifecycleCallbacks {

    @Inject
    SimpleArrayMap<String, IFragmentLife> mMapFragmentLife;
    @Inject
    Provider<IFragmentLife> mFragmentLifeProvider;

    @Inject
    public FragmentLifeCallback() {
    }

    @Override
    public void onFragmentAttached(FragmentManager fm, Fragment f, Context context) {
        if (f instanceof IBaseFragment) {
            IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
            if (iFragmentLife == null || !iFragmentLife.isAdded()) {
                iFragmentLife = mFragmentLifeProvider.get();
                mMapFragmentLife.put(f.toString(), iFragmentLife);
//                RingLog.e("onCreate activity:" + activity.toString());
//                RingLog.e("onCreate iActivityLife:" + iActivityLife.toString());
            }
            iFragmentLife.onAttach(f, context);
        }
    }

    @Override
    public void onFragmentCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onCreate(savedInstanceState);
        }
    }

    @Override
    public void onFragmentActivityCreated(FragmentManager fm, Fragment f, Bundle savedInstanceState) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onActivityCreate(savedInstanceState);
        }
    }

    @Override
    public void onFragmentViewCreated(FragmentManager fm, Fragment f, View v, Bundle savedInstanceState) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onCreateView(v,savedInstanceState);
        }
    }

    @Override
    public void onFragmentStarted(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onStart();
        }
    }

    @Override
    public void onFragmentResumed(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onResume();
        }
    }

    @Override
    public void onFragmentPaused(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onPause();
        }
    }

    @Override
    public void onFragmentStopped(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onStop();
        }
    }

    @Override
    public void onFragmentSaveInstanceState(FragmentManager fm, Fragment f, Bundle outState) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onSaveInstanceState(outState);
        }
    }

    @Override
    public void onFragmentViewDestroyed(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onDestroyView();
        }
    }

    @Override
    public void onFragmentDestroyed(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onDestroy();
        }

        mMapFragmentLife.remove(f.toString());
    }

    @Override
    public void onFragmentDetached(FragmentManager fm, Fragment f) {
        IFragmentLife iFragmentLife = mMapFragmentLife.get(f.toString());
        if (iFragmentLife != null) {
            iFragmentLife.onDetach();
        }
    }
}

我把各生命周期具体要做的事情放到FragmentLife里执行了,下面贴上FragmentLife的代码:

public class FragmentLife implements IFragmentLife {

    private static final String SAVED_STATE = "saved_state";
    private final PublishSubject<FragmentEvent> mLifecycleSubject = PublishSubject.create();

    private Fragment mFragment;
    private View mContentView;
    private Bundle mSavedState;//用于保存/恢复数据

    @Override
    public void onAttach(Fragment fragment, Context context) {
        mFragment = fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        mLifecycleSubject.onNext(FragmentEvent.CREATE);
        if (((IBaseFragment) mFragment).isUseEventBus()) {
            DevRing.busManager().register(mFragment);
        }
    }

    @Override
    public void onCreateView(View view, Bundle savedInstanceState) {
        mLifecycleSubject.onNext(FragmentEvent.CREATE_VIEW);
        mContentView = view;
    }

    @Override
    public void onActivityCreate(Bundle savedInstanceState) {
        restoreStateFromArguments();
    }

    @Override
    public void onStart() {
        mLifecycleSubject.onNext(FragmentEvent.START);
    }

    @Override
    public void onResume() {
        mLifecycleSubject.onNext(FragmentEvent.RESUME);
    }

    @Override
    public void onPause() {
        mLifecycleSubject.onNext(FragmentEvent.PAUSE);
    }

    @Override
    public void onStop() {
        mLifecycleSubject.onNext(FragmentEvent.STOP);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        saveStateToArguments();
    }

    @Override
    public void onDestroyView() {
        mLifecycleSubject.onNext(FragmentEvent.DESTROY_VIEW);

        if (mContentView != null) {
            ViewGroup parent = (ViewGroup) mContentView.getParent();
            if (parent != null) {
                parent.removeView(mContentView);
            }
        }

        saveStateToArguments();
    }

    @Override
    public void onDestroy() {
        mLifecycleSubject.onNext(FragmentEvent.DESTROY);

        if (((IBaseFragment) mFragment).isUseEventBus()) {
            DevRing.busManager().unregister(mFragment);
        }
        mContentView = null;
        mFragment = null;
    }

    @Override
    public void onDetach() {
        mLifecycleSubject.onNext(FragmentEvent.DETACH);
    }

    @Override
    public boolean isAdded() {
        return mFragment != null && mFragment.isAdded();
    }

    private void saveStateToArguments() {
        if (mFragment.getView() != null) {
            Bundle state = new Bundle();
            ((IBaseFragment) mFragment).onSaveState(state);
            mSavedState = state;
        }
        if (mSavedState != null) {
            Bundle b = mFragment.getArguments();
            if (b != null) {
                b.putBundle(SAVED_STATE, mSavedState);
            }
        }
    }

    private void restoreStateFromArguments() {
        Bundle b = mFragment.getArguments();
        if (b != null) {
            mSavedState = b.getBundle(SAVED_STATE);
            if (mSavedState != null) {
                ((IBaseFragment) mFragment).onRestoreState(mSavedState);
            }
        }
    }
}

接下来,只要你的Fragment实现了IBaseFragment接口(如果你的Activity实现了IBaseActivity,那还要确保isUseFragment()方法返回true),即可通过FragmentLifecycleCallbacks实现以下“基类操作”:

  1. 帮助控制网络请求的生命周期
    通过PublishSubject的操作,在onPause()/onStop()/onDestroy()中发射终止事件,以便控制Retrofit网络请求在页面进入特定状态时终止。
  2. EventBus的订阅/解除订阅
    根据isUseEventBus()来决定是否进行EventBus的注册/注销。
  3. 数据的保存与恢复
    fragment在保存和恢复数据方面,要比Activity复杂些,具体可以看这篇文章http://blog.csdn.net/donglynn/article/details/47065999
    具体Fragment通过实现onSaveState(Bundle bundleToSave)进行数据保存以及onRestoreState(Bundle bundleToRestore);进行数据恢复即可。

IBaseFragment接口代码:

public interface IBaseFragment {
    /**
     * 需要保存数据时,将数据写进bundleToSave
     */
    void onSaveState(Bundle bundleToSave);

    /**
     * 从bundleToRestore中获取你保存金曲的数据
     */
    void onRestoreState(Bundle bundleToRestore);

    /**
     * 该Fragment是否订阅事件总线
     * @return true则自动进行注册/注销操作,false则不注册
     */
    boolean isUseEventBus();
}

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》

public abstract class BasePresenter<V extends IBaseView,M extends IBaseModel>{

    protected V mIView;
    protected M mIModel;

    public BasePresenter(V iView,M iModel) {
        mIView = iView;
        mIModel = iModel;
    }

    public BasePresenter(V iView) {
        mIView = iView;
    }

    public BasePresenter() {
    }

    /**
     * 释放引用,防止内存泄露,一般在activity和fragment销毁时调用
     */
    public void destroy() {
        mIView = null;
    }
}

BasePresenter的作用:

  1. 提供mIView,mIModel方便子类调用。 由于IView,IModel的具体类型不确定,所以使用了泛型,子类通过泛型传入具体的IView、IModel类型。
  2. 避免内存泄漏。 提供destroy()方法给V层销毁时调用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,117评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,328评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,839评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,007评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,384评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,629评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,880评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,593评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,313评论 1 243
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,575评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,066评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,392评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,052评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,082评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,844评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,662评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,575评论 2 270