Framgent
当今的Android开发都少不了对Fragment的使用,其具有很多优秀特性:
- 分割复杂的UI
- 可以自定义Toolbar
- 具有生命周期回调
- 更轻量的页面切换
- 支持Backstack
- 事务性提交
- 配合ViewPager使用
- 可在Activity重建时保存状态
但是,Fragment同样有很多缺点:
- 通过FragmentManager事务性的更新页面听起来非常安全,实际使用我们发现除了徒增模板代码外没什么用
- 虽然支持backstack,但是使用起来不够智能且容易出错
- 生命周期相对于Activity更加复杂和难以理解
- 状态管理过于复杂,核心类
FragmentManagerImpl
查过2K行,难以理解不易,发生问题不易排查 - 扩展性差,例如
FragmentPagerAdapter
等。
以FragmentPagerAdapter为例,例如如下代码
@Override
public Object instantiateItem(View container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
String name = makeFragmentName(container.getId(), position);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), position));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
}
return fragment;
}
instantiateItem()
中开启一个事务后通过findFragmentByTag
获取Fragment,如果获取失败,则通过getItem获取并添加到FragmentManager
由于mCurTransaction
是private
的,所以难以重写instantiateItem实现自己的逻辑
private static String makeFragmentName(int viewId, int index) {
return "android:switcher:" + viewId + ":" + index;
}
makeFragmentName
也是private的无法直接调用。
总之想要在FragmentPagerAdapter中扩展自己的实现非常困难,其他Fragment相关的类也都有类似问题。
自定义View
使用Fragment有诸多限制,那么有没有替代方案呢?
Framgent实质就是带有生命周期的自定义View,所以使用自定义View可以在一定程度上替代Fragment的使用:
-
分割复杂的UI;自定义Toolbar:
自定义View天然支持UI的嵌套与分割 -
具有生命周期回调:
让自定义View实现LifecycleOwner接口,从而实现Lifecycle转发现在已经成为可能,Lifecycle已经并非Fragment的专利。 -
事务性提交:
鸡肋,不支持也能work -
配合ViewPager使用
ViewPager天然支持自定义View -
可在Activity重建时保存状态:
借助ViewModel也可以帮助自定义View实现Activity重建时的状态保存 -
支持Backstack;页面切换:
通过实现一个简单的Stack管理,完全可以实现页面的跳转与回退,例如:
// Simple stack implementation, works as expected every time
private Stack<NavItem> navigationStack = new Stack<>();
// ...
// Navigating to a new screen/item in the stack
navigationStack.push(new NavItem(viewToBeAdded.getClass(), args));
// ...
// Navigating back using the stack
if (navigationStack.size() > 1) {
navigationStack.pop();
NavItem navItem = navigationStack.peek();
CustomView customView = (CustomView) navItem.viewClass.getConstructor(Context.class).newInstance(this);
container.removeAllViews();
customView.setArgs(navItem.args);
container.addView(customView);
} else {
super.onBackPressed();
}
总结
借助Lifecycle以及ViewModel等Support库,自定义View可以替代Fragment的常用功能,并且可以更自由的扩展和定制。