把项目的登陆,注册和修改密码全部替换成Fragment,顺便通过简单的MVP模式来实现,简单记录下遇到的一些问题.

谁是V

这里把Fragment做为V,而不是宿主Activity.(具体原因有待深究,然而能力有限)

Fragment的管理

登陆,注册和修改密码,一个三个Fragment,继承自基类的IBaseFragment,并各自对应自己的P和M.

public abstract class IBaseFragment<DB extends ViewDataBinding,P extends IBasePresenter,V extends IBaseView> extends Fragment implements IBaseView

这里使用到了DataBinding.

使用Loader来管理P的生命周期,并在IBaseFragment中初始化

PresenterLoader代码如下:


    public class PresenterLoader<P extends IBasePresenter> extends Loader {

    private P prensenter;
    private PresenterFactory<P> factory;

    /**
     * Stores away the application context associated with context.
     * Since Loaders can be used across multiple activities it's dangerous to
     * store the context directly; always use {@link #getContext()} to retrieve
     * the Loader's Context, don't use the constructor argument directly.
     * The Context returned by {@link #getContext} is safe to use across
     * Activity instances.
     *
     * @param context used to retrieve the application context.
     */
    public PresenterLoader(Context context, PresenterFactory<P> factory) {
        super(context);
        this.factory = factory;
    }

    @Override
    protected void onStartLoading() {
        super.onStartLoading();
        if (prensenter!=null){
            deliverResult(prensenter);
            return;
        }
        forceLoad();
    }

    @Override
    protected void onForceLoad() {
        super.onForceLoad();
        prensenter = factory.create();
        deliverResult(prensenter);
    }

    @Override
    protected void onStopLoading() {
        super.onStopLoading();

    }

    @Override
    protected void onReset() {
        super.onReset();
        if (prensenter!=null) {
            prensenter.detach();
            prensenter = null;
        }
    }
}
 @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        LoaderManager loaderManager = activity.getSupportLoaderManager();
        loaderManager.initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks<P>() {
            @Override
            public Loader<P> onCreateLoader(int id, Bundle args) {

                return new PresenterLoader<P>(activity, new PresenterFactory<P>() {
                    @Override
                    public P create() {
                        return createPresenter();
                    }
                });
            }

            @Override
            public void onLoadFinished(Loader<P> loader, P presenter) {
                IBaseFragment.this.presenter = presenter;
            }

            @Override
            public void onLoaderReset(Loader<P> loader) {

            }
        });
    }
  1. public class PresenterLoader<P extends IBasePresenter> extends Loader

  2. 接口PresenterFactory用于生成对应的P

  3. 抽象方法createPresenter();由子类负责具体实现

public interface PresenterFactory<P extends IBasePresenter> {
    P create();
}

fragment的切换流程:

  1. 登陆--->注册---->登陆

  2. 登陆--->修改密码---->登陆

从登陆界面跳转到注册/修改密码使用如下方法:

getSupportFragmentManager().beginTransitation().add(containerId,fragment,tag).addBackToStack(tag).commit();
  1. 通过addBackToStack将当前添加的Fragment添加到回退栈

  2. 另外这里采用了add(containerId,fragment,tag)而不是replace是为了点击实体返回键时,能回到登陆界面

这里有个坑

在fragment之间切换时,遇到一个坑.举例:从登陆跳转到注册页面,这个时候,注册页面的p的类型仍是登陆页面的p类型.起初我以为是IBaseFragment在OnActivityCreated()初始化LoadManager只进行了一次.然而打断点发现,每次fragment切换这个方法都会执行到.这时才明白是loaderManager.initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks)这里的LOADER_ID参数的问题.在IBaseFragment中,这个参数只赋值一次,那么生成的Loader对象也只有一个,导致从登陆页面切换到别的页面时,P不再被重新创建,仍然是登陆界面那个P.

解决办法: 在IBaseFragment中定义一个LOADER_ID如下:

 protected   int LOADER_ID = 210;

这里用protected修饰,保证可以被子类修改
然后在注册和修改页面重新赋值这个变量
如:

    RegisteFragment.java
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        LOADER_ID = 211;
    }

这样就解决了P的问题.

Fragment的返回

监听屏幕返回按钮事件

public boolean onKeyDown(int keyCode, KeyEvent event)

如何弹出回退栈中的Fragment(我们已经在前面通过add和addBackToStack方法将fragment加入到回退栈)
FragmentManager有如下API:

1. popBackStackImmediate();//立即弹出
2. popBackStack(String tag,int flag);//这里的tag是add方法中最后一个参数tag,flag可以是0和                 FragmentManager.POP_BACK_STACK_INCLUSIVE,0表示不弹出自身,后者表示连同自身弹出.

通过FragmentManager.getBackStackEntryCount()得到回退栈中添加的个数,然后不断弹出,直到剩下一个为止.

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode==KeyEvent.KEYCODE_BACK&&event.getAction()==KeyEvent.ACTION_DOWN){
            FragmentManager fragmentManager = getSupportFragmentManager();
            if (fragmentManager.getBackStackEntryCount()>1) {
                while (fragmentManager.getBackStackEntryCount() > 1) {
                    fragmentManager.popBackStackImmediate();
                }
            }else
                finish();
        }
        return true;//这里要返回true,不然回退到最后一个可能会出现空白页面
    }

大致就这样,简单总结下.


idealcn
27 声望4 粉丝