一、fragment根本运用
摘自官网的代码示例
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 留意这里有判别,当saveInstanceState为空时再创立。 表明activity不是重建的,防止fragment重复创立。
if (savedInstanceState == null) {
val bundle = bundleOf("some_int" to 0)
supportFragmentManager.commit {
setReorderingAllowed(true)
// 在add办法中经过bundle传递参数。 貌似是高版本的用法,在源码中没有看到。
add<ExampleFragment>(R.id.fragment_container_view, args = bundle)
}
}
}
}
class ExampleFragment : Fragment(R.layout.example_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// 经过requireArguments办法获取参数。
val someInt = requireArguments().getInt("some_int")
...
}
}
其间,requireArguments办法如下,主要是对传参进行了一个校验。
假如没有调用setArgument传参,调用requireArguments办法就会报错。
从办法阐明中可知,该办法要在增加之前调用。
二、回退栈
在增加fragment的同时参加回退栈,
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack("hello") // 只有加了回退栈,才能够结合popbackstack来实现回退处理。
.commit()
需求留意的是,fragment参加回退栈时不能运用commitNow及commitNowAllowingStateLoss提交。 主要是在这两个办法中有disallowAddToBackStack()办法检测是否现已参加回退栈了,假如参加了那么会抛出IllegalStateException反常。
如下代码能够监听回退栈的数据变化。
supportFragmentManager?.addOnBackStackChangedListener {
// 回退栈变化监听,体系接口供给了反注册,留意销毁时反注册
Log.i(TAG, "onCreate: 回退栈 size : ${supportFragmentManager.backStackEntryCount}")
val size = supportFragmentManager.backStackEntryCount
for (i in 0 until size) {
val backstackEntry = supportFragmentManager.getBackStackEntryAt(i)
Log.i(TAG, "第${i}个回退栈元素信息: $backstackEntry, name is ${backstackEntry.name}")
}
}
经过如下代码在activity的某个布局id中增加三个fragment
监听器中会打印如下日志
第0个回退栈元素信息: BackStackEntry{6145541 #0 hello}, name is hello
第1个回退栈元素信息: BackStackEntry{8099e32 #1 1}, name is 1
第2个回退栈元素信息: BackStackEntry{d274363 #2 2}, name is 2
第3个回退栈元素信息: BackStackEntry{326b9c0 #3 3}, name is 3
在FragmentManager中供给了popBackStack()和popBackStack(name, flag)两个办法。
popBackStack()是将栈顶的BackStackEntry弹出栈,在上面的场景中,当时的界面是fragment3,假如调用一次,界面会显示fragment2.
popBackStack(name, flag)办法会经过name找到对应的BackStackEntry目标,然后根据flag的值决议如何弹出。
flag取值有0和FragmentManager.POP_BACK_STATE_INCLUSIVE(该常量值为1)。
在上面的场景中,假如name传2,flag传0,那么会找到BackStackEntry{d274363 #2 2}目标,flag为0表明将当时这个BackStackEntry目标保存在回退栈中,那么此时,只会将fragment3出栈,界面显示fragment2.
假如name传2 ,flag传1也即是FragmentManager.POP_BACK_STATE_INCLUSIVE,表明将name对应的BackStackEntry一同出栈。此时,回退栈中只会保存fragment1.
留意,假如经过name没有找到对应的BackStackEntry对应,那么该办法不会出栈任何元素,回退栈也不会有变化。
三、回来逻辑
常见的回来处理逻辑如下:
override fun onBackPressed() {
supportFragmentManager.let {
// 回退栈有内容时才处理fragment回退
if (it.backStackEntryCount > 0) {
Log.i(TAG, "onBackPressed: supportFragmentManager.popBackStack() execute")
// 根据业务需求选择弹出办法
it.popBackStack()
// it.popBackStack("hello", 0) // 假如栈中没有对应name的backstackEntry,那么不会有任何呼应
// it.popBackStack("hello", FragmentManager.POP_BACK_STACK_INCLUSIVE)
} else {
Log.i(TAG, "onBackPressed: default")
super.onBackPressed()
}
}
}
四、业务的提交办法
在FragmentTransaction中供给了四种提交办法,分别为commit、comimitNow、commitAllowingStateLoss、commitNowAllowingStateLoss。
下面是commit办法的阐明
Schedules a commit of this transaction. The commit does not happen immediately; it will be scheduled as work on the main thread to be done the next time that thread is ready.
A transaction can only be committed with this method prior to its containing activity saving its state. If the commit is attempted after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored from its state. See {@link #commitAllowingStateLoss()} for situations where it may be okay to lose the commit.
@return Returns the identifier of this transaction’s back stack entry, if {@link #addToBackStack(String)} had been called. Otherwise, returns a negative number.
public abstract int commit();
这段阐明有两个要点, 一是提交是异步的,二是业务必要要在activity保存状况前提交。
这个规则是为了保证在 Activity 生命周期过程中的正确性。Activity 在销毁和重新创立时,会保存和复原其状况。假如在状况保存之后修正了 FragmentTransaction,体系可能无法正确复原 Fragment 的状况,因此在这个时候进行提交会导致反常。这是 Android 体系为了保证运用的稳定性而设置的一种维护机制。在需求提交业务的时候,通常应该在 Activity 生命周期的早期进行。
有了对commit的理解就很好理解commitAllowingStateLoss办法了,该办法允许丢掉Fragment状况,故也没有强制必须要在activity保存状况前提交,相当于提交的时机没有那么多的限制。 so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user. 这两个办法在源码上差异也不大,仅仅多了对activity保存状况的检测。 详细如下图
经过上述源码可知,commit及commitAllowingStateLoss是经过Handler将增加逻辑post到主线程履行的。 而commitNow及commitNowAllowingStateLoss是当即履行的。
post履行与当即履行详细有啥差异呢?
运用 commitNow
办法时,该业务会当即履行。这意味着直到该业务履行结束,你的代码会被阻塞。这样能够保证在调用 commitNow
后,你能够当即访问新的 Fragment 状况。
适用情况:
- 当你需求当即履行 FragmentTransaction 并保证当即生效时。
- 当你不希望将业务增加到主线程的音讯行列中。
其他情况则推荐运用commit办法。
提交办法 | 是否异步 | 是否允许丢掉状况 |
---|---|---|
commit | 是 | 否 |
commitAllowingStateLoss | 是 | 是 |
commitNow | 否 | 否 |
commitNowAllowingStateLoss | 否 | 是 |
五、状况保存
fragment与activity相同,供给了onSaveInstance回调办法来保存相关状况。 详细用法如下:
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putBoolean(IS_EDITING_KEY, isEditing)
outState.putString(RANDOM_GOOD_DEED_KEY, randomGoodDeed)
}
// 在onCreate办法中读取保存的数据。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
isEditing = savedInstanceState?.getBoolean(IS_EDITING_KEY, false)
randomGoodDeed = savedInstanceState?.getString(RANDOM_GOOD_DEED_KEY)
?: viewModel.generateRandomGoodDeed()
}
view state的保存与恢复 假如view没有id,那么体系是不会对该view的状况进行保存的。其实也很好理解,假如没有id,那么一般也不会动态修正该view的属性了,故也没有太大的必要保存该view的状况。
六、父子关系的FragmentManager
Fragments can host one or more child fragments. Inside a fragment, you can get a reference to the
FragmentManager
that manages the fragment’s children throughgetChildFragmentManager()
. If you need to access its hostFragmentManager
, you can usegetParentFragmentManager()
.
留意,在有父子fragment嵌套的当地,运用FragmentManager时必定要留意,否则可能会crash。
下图摘自官网介绍。
七、参阅
1、官网