getSupportFragmentManager , getParentFragmentManager 和 getChildFragmentManager
FragmentStateAdapter 和 FragmentPagerAdapter
add 和 replace
observe LiveData时传入 this 还是 viewLifecycleOwner
使用 simpleName 作为 fragment 的 tag 有何风险?
在 BottomBarNavigation 和 drawer 中如何使用Fragment多次添加?
返回栈
FragmentManager
是androidx.fragment.app
(已弃用的不考虑)下的抽象类,创建用于 添加,移除,替换fragment
的事务(transaction
)
首先要确认一件事,getSupportFragmentManager()
是 FragmentActivity
下的方法
getParentFragmentManager
和 getChildFragmentManager
是 androidx.fragment.app.Fragment
下的方法,
其中
androidx.fragment 1.2.0
后getFragmentManager
与requireFragmentManager
已弃用
明确了这件事,接下来的就很清晰了
getSupportFragmentManager
与 activity
关联,可以将其视为 activity
的 FragmentManager
getChildFragmentManager
与 fragment
关联,可以将其视为fragment
的FragmentManager
getParentFragmentManager
情况稍微复杂,正常情况返回的是该fragment
依附的activity
的FragmentManager
。如果该fragment是另一个fragment
的子 fragment
,则返回的是其父fragment
的 getChildFragmentManager
如果这么说还不明白的话,我们可以做一个实践。
创建一个 activity
,一个父fragment
,一个子fragment
(R.id.content)}Log.i("MyActivity", "supportFragmentManager $supportFragmentManager")}// activity
class MyActivity : AppCompatActivity(R.layout.activity_main) {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)supportFragmentManager.commit {add
}class ParentFragment : Fragment(R.layout.fragment_parent) {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)childFragmentManager.commit {add
}class ChildFragment : Fragment(R.layout.fragment_child) {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)Log.i("ChildFragment", "parentFragmentManager $parentFragmentManager")Log.i("ChildFragment", "childFragmentManager $childFragmentManager")}
}
//log
I/MyActivity: supportFragmentManager FragmentManager{825dcef in HostCallbacks{14a13fc}}}
I/ParentFragment: parentFragmentManager FragmentManager{825dcef in HostCallbacks{14a13fc}}}
I/ParentFragment: childFragmentManager FragmentManager{df5de83 in ParentFragment{7cdd800}}}
I/ChildFragment: parentFragmentManager FragmentManager{df5de83 in ParentFragment{7cdd800}}}
I/ChildFragment: childFragmentManager FragmentManager{aba9afb in ChildFragment{5cea718}}}
因此* 在 `activity` 中使用 `ViewPager`,`BottomSheetFragment` 和`DialogFragment` 时,都应使用 `getSupportFragmentManager`* 在`fragment` 中使用 `ViewPager` 时应该使用`getChildFragmentManager`错误的在 `fragment` 中使用 `activity` 的 `FragmentManager` 会引发内存泄露。 为什么呢?假如您的fragment中有一些依靠 `ViewPager` 管理的子 `fragment`,并且所有这些 `fragment` 都在 `activity` 中,因为您使用的是`activity` 的`FragmentManager` 。 现在,如果关闭您的父`fragment`,它将被关闭,但不会被销毁,因为所有子`fragment`都处于活动状态,并且它们仍在内存中,从而导致泄漏。 它不仅会泄漏父`fragment`,还会泄漏所有子`fragment`,因为它们都无法从堆内存中清除。### FragmentStateAdapter 和 FragmentPagerAdapter`FragmentPagerAdapter`将整个 `fragment`存储在内存中,如果`ViewPager`中使用了大量 `fragment`,则可能导致内存开销增加。 `FragmentStatePagerAdapter`仅存储片段的`savedInstanceState`,并在失去焦点时销毁所有 `fragment`。让我们看看常见的两个问题#### 1\. 刷新ViewPager不生效`ViewPager` 中的 `fragment` 是通过 `activity`或 `fragment`的 `FragmentManager` 管理的,`FragmentManager` 包含了`viewpager`的所有`fragment`的实例因此,当`ViewPager`没有刷新时,它只是`FragmentManager`仍保留的旧 `fragment` 实例。 您需要找出为什么`FragmentManger`持有`fragment`实例的原因。#### 2\. 在Viewpager中访问当前fragment这也是我们遇到的一个非常普遍的问题。 如果遇到这种情况,我们一般在 `adapter` 内部创建 `fragment` 的数组列表,或者尝试使用某些标签访问`fragment`。 不过还有另一种选择。 `FragmentStateAdapter` 和`FragmentPagerAdapter`都提供方法`setPrimaryItem`。 可以用来设置当前`fragment`,如下所示:
var fragment: ChildFragment? = null
override fun setPrimaryItem(container: ViewGroup, position: Int, any: Any) {
if (getChildFragment() != any)
fragment = any as ChildFragment
super.setPrimaryItem(container, position, any)
}
fun getChildFragment(): ChildFragment? = fragment
//use
mAapter.getChildFragment()
### add 和 replace 如何选择?在我们的`activity`中,我们有一个容器,其中装有`fragment`。`add`只会将一个`fragment`添加到容器中。 假设您将`FragmentA`和`FragmentB`添加到容器中。 容器将具有`FragmentA`和`FragmentB`,如果容器是`FrameLayout`,则将`fragment`一个添加在另一个之上。`replace`将简单地替换容器顶部的一个`fragment`,因此,如果我创建了 `FragmentC`并 `replace` 顶部的 `FragmentB`,则`FragmentB`将被从容器中删除(执行`onDestroy`,除非您调用`addToBackStack`,仅执行`onDestroyView`),而`FragmentC`将位于顶部。那么如何选择呢? `replace`删除现有`fragment`并添加一个新`fragment`。 这意味着当您按下返回按钮时,将创建被替换的`fragment`,并调用其`onCreateView`。 另一方面,`add`保留现有`fragment`,并添加一个新`fragment`,这意味着现有`fragment`将处于活动状态,并且它们不会处于 “paused” 状态。 因此,按下返回按钮时,现有`fragment`(添加新`fragment`之前的`fragment`)不会调用`onCreateView`。 就`fragment`的生命周期事件而言,在`replace`的情况下将调用`onPause`,`onResume`,`onCreateView`和其他生命周期事件,在`add`的情况下则不会。如果不需要重新访问当前`fragment`并且不再需要当前`fragment`,请使用`replace`。 另外,如果您的应用有内存限制,请考虑使用`replace`。### observe LiveData时传入 this 还是 viewLifecycleOwner`androidx fragment 1.2.0` 起,添加了新的 Lint 检查,以确保您在从 `onCreateView()`、`onViewCreated()` 或 `onActivityCreated()` 观察 `LiveData` 时使用 `getViewLifecycleOwner()`### 使用 simpleName 作为 fragment 的 tag 有何风险?一般情况下我们会使用calss的`simpleName` 作为`fragment` 的tag
supportFragmentManager.commit {
replace(R.id.content,MyFragment.newInstance(“Fragment”),
MyFragment::class.java.simpleName)
addToBackStack(null)
}
这样做不会出现什么问题,但是...
val fragment = supportFragmentManager.findFragmentByTag(tag)
这样获取到的fragment可能不是想要的结果。为什么呢?加入有两个 fragment,经过混淆,它们变成
com.mypackage.FragmentA → com.mypackage.c.a
com.mypackage.FragmentB → com.mypackage.c.a.a
上面是混淆了 full name,如果是simpleName 呢?
com.mypackage.FragmentA → a
com.mypackage.FragmentB → a
WTF!**所以在设置tag时尽量用全名或者常量**### 在 BottomBarNavigation 和 drawer 中如何使用Fragment多次添加?当我们使用`BottomBarNavigation`和 `NavigationDrawer`时,通常会看到诸如`fragment` 重建或多次添加相同`fragment`之类的问题。在这种情况下,您可以使用`show / hide` 而不是 `add` 或 `replace`。### 返回栈如果您想在`fragment`的一系列跳转中按返回键返回上一个`fragment`,应该在`commit` `transaction`之前调用`addToBackStack`方法
//使用该扩展 androidx.fragment:fragment-ktx:1.2.0 以上
parentFragmentManager.commit {
addToBackStack(null)
add(R.id.content)
}
关于返回栈更详细的介绍,请移步 [【背上Jetpack之Fragment】从源码的角度看Fragment 返回栈 附多返回栈demo](https://shimo.im/docs/7bBISzfnVjYfqFkR/)Fragment 的使用新姿势
---------------* fragment-ktx 有哪些好用的扩展函数* fragment 之间和与 activity 通信* 使用 FragmentContainerView 作为 fragment 容器* FragmentFactory 的使用* Fragment 返回键拦截* Fragment 使用 ViewBinding* Fragment 使用 ViewPager2* 不需要重写 onCreateView 了?* 使用require\_()方法### fragment-ktx 有哪些好用的扩展函数#### 1\. FragmentManagerKt
//before
supportFragmentManager
.beginTransaction()
.add(R.id.content,Fragment1())
.commit()
//after
supportFragmentManager.commit {
add(R.id.content)
}
#### 2\. FragmentViewModelLazyKt
//before
//共享范围activity
val mViewMode1l = ViewModelProvider(requireActivity()).get(UpdateAppViewModel::class.java)
//共享范围fragment 内部
val mViewMode1l = ViewModelProvider(this).get(UpdateAppViewModel::class.java)
//after
//共享范围activity
private val mViewModel by activityViewModels()
//共享范围fragment 内部
private val mViewModel by viewModel()
> **注意:ViewModelProviders.of(this).get(MyViewModel.class); 的方式已弃用**
>
> **`lifecycle-extensions` 依赖包已弃用**### fragment 之间和与 activity 通信fragment 和 fragment之间,fragment 和 activity 之间的通信有很多方法,android jetpack 推荐我们使用 ViewModel + LiveData 处理同一个activity 内的 fragment 之间通信,可以使用作用范围为activity的ViewModel,activity与 fragment通信同理。详情可移步 [Android官方应用架构指南](https://shimo.im/docs/7bBISzfnVjYfqFkR/)### 使用 FragmentContainerView 作为 fragment 容器过去我们使用 `FrameLayout` 作为 `Fragment` 的容器,在 `AndroidX Fragment 1.2.0` 后,可以使用 `FragmentContainerView` 代替 `FrameLayout` 。它修复了一些动画 z轴索引顺序问题和窗口插入调度,这意味着两个`fragment`之间的退出和进入过渡不会互相重叠。使用`FragmentContainerView`将先开启退出动画然后才是进入动画。`FragmentContainerView` 是专门为 fragment设计的自定义View,它继承自 FrameLayout`android:name` 属性允许您添加`fragment`,`android:tag` 属性可以为`fragment`设置tag
xmlns:android=“http://schemas.android.com/apk/res/android”
xmlns:app=“http://schemas.android.com/apk/res-auto”
android:id="@+id/fragment_container_view"
android:layout_@+id/pager"
android:layout_@+id/tab_layout"android:layout_android:layout_ />
```
使用ViewPager
时,TabLayout
与ViewPager
联动需要调用 setupWithViewPager
,并重写getPageTitle
方法,而ViewPager2
改为使用TabLayoutMediator
对象
// Integrating TabLayout with ViewPager
class CollectionDemoFragment : Fragment() {...override fun onViewCreated(view: View, savedInstanceState: Bundle?) {val tabLayout = view.findViewById(R.id.tab_layout)tabLayout.setupWithViewPager(viewPager)}...
}class DemoCollectionPagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {override fun getCount(): Int = 4override fun getPageTitle(position: Int): CharSequence {return "OBJECT ${(position + 1)}"}...
}// Integrating TabLayout with ViewPager2
class CollectionDemoFragment : Fragment() {...override fun onViewCreated(view: View, savedInstanceState: Bundle?) {val tabLayout = view.findViewById(R.id.tab_layout)TabLayoutMediator(tabLayout, viewPager) { tab, position ->tab.text = "OBJECT ${(position + 1)}"}.attach()}...
}
androidx fragment 1.1.0
后,您可以使用将 layoutId
作为参数的构造函数,这样就无需重写 onCreateView
方法了
class MyActivity : AppCompatActivity(R.layout.my_activity)
class MyFragmentActivity: FragmentActivity(R.layout.my_fragment_activity)
class MyFragment : Fragment(R.layout.my_fragment)
androidx fragment 1.2.2
起,新增了一项lint检查,fragment
建议使用关联的require_()
方法获取更多描述性错误消息,而不是使用checkNotNull(get_())
,requireNonNull(get_())
或get()!
适用于所有包含 get 和 require Fragment API
例如:使用 requireActivity()
替代 getActivity()
按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。
整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。
因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。
如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
1、热修复设计
2、插件化框架设计
3、组件化框架设计
4、图片加载框架
5、网络访问框架设计
6、RXJava响应式编程框架设计
……
1、设计思想与代码质量优化
2、程序性能优化
3、开发效率优化
……
1、高级UI晋升
2、Android内核组件
3、大型项目必备IPC
4、数据持久与序列化
5、Framework内核解析
……
1、NDK开发之C/C++入门
2、JNI模块开发
3、Linux编程
4、底层图片处理
5、音视频开发
6、机器学习
……
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter Dart语言系统入门
……
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战
……
1、准备开始
2、基础
3、类和对象
4、函数和lambda表达式
5、其他
……
好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码领取哈~