把项目的登陆,注册和修改密码全部替换成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) {
}
});
}
public class PresenterLoader<P extends IBasePresenter> extends Loader
接口PresenterFactory用于生成对应的P
抽象方法createPresenter();由子类负责具体实现
public interface PresenterFactory<P extends IBasePresenter> {
P create();
}
fragment的切换流程:
登陆--->注册---->登陆
登陆--->修改密码---->登陆
从登陆界面跳转到注册/修改密码使用如下方法:
getSupportFragmentManager().beginTransitation().add(containerId,fragment,tag).addBackToStack(tag).commit();
通过addBackToStack将当前添加的Fragment添加到回退栈
另外这里采用了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,不然回退到最后一个可能会出现空白页面
}
大致就这样,简单总结下.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。