热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【背上Jetpack之Fragment】你真的会用Fragment吗?Fragment常见问题以及androidx下Fragment的使用新姿势

Fragment常见的问题getSupportFragmentManager,getParentFragmentManager和getChildFragmentMan

Fragment 常见的问题


  • getSupportFragmentManager , getParentFragmentManager 和 getChildFragmentManager

  • FragmentStateAdapter 和 FragmentPagerAdapter

  • add 和 replace

  • observe LiveData时传入 this 还是 viewLifecycleOwner

  • 使用 simpleName 作为 fragment 的 tag 有何风险?

  • 在 BottomBarNavigation 和 drawer 中如何使用Fragment多次添加?

  • 返回栈


getSupportFragmentManager , getParentFragmentManager和getChildFragmentManager


FragmentManagerandroidx.fragment.app(已弃用的不考虑)下的抽象类,创建用于 添加,移除,替换 fragment 的事务(transaction


首先要确认一件事,getSupportFragmentManager()FragmentActivity下的方法

getParentFragmentManagergetChildFragmentManagerandroidx.fragment.app.Fragment 下的方法,


其中 androidx.fragment 1.2.0getFragmentManagerrequireFragmentManager 已弃用


明确了这件事,接下来的就很清晰了


  • getSupportFragmentManageractivity关联,可以将其视为 activityFragmentManager
  • getChildFragmentManagerfragment关联,可以将其视为fragmentFragmentManager
  • getParentFragmentManager情况稍微复杂,正常情况返回的是该fragment 依附的activityFragmentManager。如果该fragment是另一个fragment 的子 fragment,则返回的是其父fragmentgetChildFragmentManager

如果这么说还不明白的话,我们可以做一个实践。

创建一个 activity,一个父fragment ,一个子fragment

// activity
class MyActivity : AppCompatActivity(R.layout.activity_main) {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)supportFragmentManager.commit {add

(R.id.content)}Log.i("MyActivity", "supportFragmentManager $supportFragmentManager")}
}class ParentFragment : Fragment(R.layout.fragment_parent) {override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)childFragmentManager.commit {add(R.id.content)}Log.i("ParentFragment", "parentFragmentManager $parentFragmentManager")Log.i("ParentFragment", "childFragmentManager $childFragmentManager")}
}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时,TabLayoutViewPager联动需要调用 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()}...
}

不需要重写 onCreateView 了?

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)

使用require_()方法

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 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。

相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。


(一)架构师必备Java基础

1、深入理解Java泛型

2、注解深入浅出

3、并发编程

4、数据传输与序列化

5、Java虚拟机原理

6、高效IO

……

图片


(二)设计思想解读开源框架

1、热修复设计

2、插件化框架设计

3、组件化框架设计

4、图片加载框架

5、网络访问框架设计

6、RXJava响应式编程框架设计

……

图片


(三)360°全方位性能优化

1、设计思想与代码质量优化

2、程序性能优化


  • 启动速度与执行效率优化
  • 布局检测与优化
  • 内存优化
  • 耗电优化
  • 网络传输与数据储存优化
  • APK大小优化

3、开发效率优化


  • 分布式版本控制系统Git
  • 自动化构建系统Gradle

……

图片


(四)Android框架体系架构

1、高级UI晋升

2、Android内核组件

3、大型项目必备IPC

4、数据持久与序列化

5、Framework内核解析

……

图片


(五)NDK模块开发

1、NDK开发之C/C++入门

2、JNI模块开发

3、Linux编程

4、底层图片处理

5、音视频开发

6、机器学习

……

图片


(六)Flutter学习进阶

1、Flutter跨平台开发概述

2、Windows中Flutter开发环境搭建

3、编写你的第一个Flutter APP

4、Flutter Dart语言系统入门

……

图片


(七)微信小程序开发

1、小程序概述及入门

2、小程序UI开发

3、API操作

4、购物商场项目实战

……

图片


(八)kotlin从入门到精通

1、准备开始

2、基础

3、类和对象

4、函数和lambda表达式

5、其他

……

图片


好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码领取哈~


img


推荐阅读
author-avatar
阿川那小子
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有