Forward
The use of Fragment is indispensable in today's Android development, which has many excellent features:
- Segment complex UI
- Toolbar can be customized
- Has a life cycle callback
- Lighter page switching
- Support Backstack
- Transactional commit
- Use with ViewPager
- State can be saved when Activity is rebuilt
However, Fragment also has many disadvantages:
- It sounds very safe to update the page transactionally through FragmentManager. In actual use, we found that it was useless except for adding template code.
- Although it supports backstack, it is not smart enough to use and error-prone
- The life cycle is more complicated and difficult to understand than Activity
- The state management is too complicated. The core class has
FragmentManagerImpl
checked 2K rows, which is difficult to understand and difficult to troubleshoot. - Poor scalability, for example,
FragmentPagerAdapter
etc.
Take FragmentPagerAdapter as an example, for example, the following code
@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()
After opening a transaction in, findFragmentByTag
get the Fragment, if the get fails, get it via getItem and add it toFragmentManager
Because it mCurTransaction
is private
, it is difficult to rewrite instantiateItem to implement your own logic
private static String makeFragmentName(int viewId, int index) {
return "android:switcher:" + viewId + ":" + index;
}
makeFragmentName
It is also private and cannot be called directly.
In short, it is very difficult to extend your own implementation in FragmentPagerAdapter, and other Fragment-related classes have similar problems.
Custom View
There are many limitations to using Fragment, so is there an alternative?
Framgent is essentially a custom View with a life cycle, so using a custom View can replace the use of Fragment to a certain extent:
-
Segment complex UI; Custom Toolbar:
Custom View naturally supports UI nesting and segmentation -
With life cycle callback:
Let the custom View implement the LifecycleOwner interface, so as to realize Lifecycle forwarding is now possible. Lifecycle is no longer a Fragment patent. -
Transactional commit:
tasteless, work without support -
Use ViewPager with
ViewPager to naturally support custom View -
State
can be saved when Activity is rebuilt: With the help of ViewModel, you can also help custom View to achieve state preservation when Activity is rebuilt -
Support Backstack; Page switching:
By implementing a simple Stack management, page jump and rollback can be realized, for example:
// 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();
}
to sum up
With support libraries such as Lifecycle and ViewModel, custom View can replace the common functions of Fragment, and can be expanded and customized more freely.