目录
1)Fragment是什么
2)Fragment的应用场景
3)为什么使用Fragment?
4)Fragment如何使用
5)Fragment的生命周期
6)Android开发,建议是多个activity,还是activity结合fragment,优缺点如何?
7)Fragment和Activity的通讯、返回键如何处理
8)Navigation搭配Fragment使用后,事务是怎么样?
一、Fragment是什么
Fragment是Android中的一种组件,直译为"碎片"或"片段",可以看作是Activity的模块化部分。
它主要用于承载一部分用户界面和逻辑,并可以在多个Activity中复用。Fragment具有自己的生命周期,能接收输入事件,可以在Activity运行时被添加、移除、替换或隐藏。此外,Fragment还允许开发者通过FragmentManager来管理其生命周期和事务,如添加、删除、替换Fragment等。
二、Fragment的应用场景
导航
- UI一:
- UI二:
三、为什么使用Fragment?
- 通过将复杂的界面拆分成多个Fragment,每个Fragment负责一部分UI和逻辑,可以提高代码的复用性和可维护性。不同的Fragment可以专注于处理特定的UI或逻辑,使得代码更加清晰和易于管理。
- Fragment可以被多个Activity复用,提高了代码的复用率。在模块化开发中,一个Fragment可以代表一个独立的业务模块,从而在不同的地方重复使用。
四、Fragment如何使用
4.1 Fragment一般搭配navigation进行使用
(1)创建一个导航条+容器:也就是我们上面案例种的左边部分(导航条)和内容显示部分(容器)
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.navigation.NavigationView
android:id="@+id/bnv_main_navigationbar"
android:layout_width="300dp"
android:layout_height="match_parent"
android:background="@color/white"
android:paddingHorizontal="9dp"
app:itemBackground="@color/white"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menu="@menu/backstage_menu_setting" />
<androidx.fragment.app.FragmentContainerView
android:id="@+id/home_fragmentcontainerview"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/dingdian_pic_background_sys"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toEndOf="@+id/bnv_main_navigationbar"
app:layout_constraintTop_toBottomOf="@+id/backstage_constraintlayout"
app:navGraph="@navigation/backstage_nav" />
</androidx.constraintlayout.widget.ConstraintLayout>
(2)那么导航条view和容器view我们已经创建出来,那么导航条的这些:"首页","内容"这些item如何来呢?没错,可以看到上面我们使用了 app:menu="@menu/backstage_menu_setting" ,我们需要创建meun对象出来。
xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/backstage_cottoncandyfragment"
android:title="首页"/>
<item
android:id="@+id/backstage_paramsetfragment"
android:title="内容"/>
<item
android:id="@+id/backstage_datastatfragment"
android:title="咨讯"/>
<item
android:id="@+id/backstage_languagefragment"
android:title="产品"/>
<item
android:id="@+id/backstage_errorstatfragment"
android:title="图库"/>
<item
android:id="@+id/backstage_resetpwdfragment"
android:title="消息"/>
<item
android:id="@+id/backstage_localalarmclockfragment"
android:title="广告"/>
<item
android:id="@+id/backstage_permissionfragment"
android:title="设置"/>
</menu>
(3)下面我们就需要创建fragment了,Fragment就是右边部分的容器内容。
kt
@AndroidEntryPoint
class PriceFragment : BaseFragment<BackstageFragmentPriceBinding, ErrorStatFragmentVM>() {
override val mViewModel: ErrorStatFragmentVM by viewModels()
override fun createVB() = BackstageFragmentPriceBinding.inflate(layoutInflater)
override fun BackstageFragmentPriceBinding.initView() {
}
override fun initObserve() {
}
override fun initRequestData() {
mViewModel.getBanners()
mViewModel.getArticleData()
}
}
xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/backstage_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Datastat"
android:textSize="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
(4)那么如何设置点击这些"首页","资讯"就能打开对应的Fragment呢?使用Navigation,让我们可以使用非常简短的代码就能实现跳转。首先我们要创建一个导航文件
xml
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/backstage_nav"
app:startDestination="@id/backstage_datastatfragment">
<fragment
android:id="@+id/backstage_cottoncandyfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.CottonCandyFragment"
android:label="CottonCandyFragment" />
<fragment
android:id="@+id/backstage_paramsetfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.ParamSetFragment"
android:label="ParamSetFragment" />
<fragment
android:id="@+id/backstage_datastatfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.DataStatFragment"
android:label="DataStatFragment" />
<fragment
android:id="@+id/backstage_errorstatfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.ErrorStatFragment"
android:label="ErrorStatFragment" />
<fragment
android:id="@+id/backstage_languagefragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.LanguageFragment"
android:label="LanguageFragment" />
<fragment
android:id="@+id/backstage_resetpwdfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.ResetPwdFragment"
android:label="ResetPwdFragment" />
<fragment
android:id="@+id/backstage_localalarmclockfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.LocalAlarmClockFragment"
android:label="LocalAlarmClockFragment" />
<fragment
android:id="@+id/backstage_permissionfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.PermissionFragment"
android:label="PermissionFragment" />
<fragment
android:id="@+id/backstage_otherfragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.OtherFragment"
android:label="OtherFragment" />
</navigation>
注意,android:id="@+id/backstage_otherfragment"的id必须和menu的item id必须相同,这样才能找到对应的fragment
(5)实现使用navcontroller实现跳转
kt
bnvMainNavigationbar.setNavigationItemSelectedListener{
val findNavController = findNavController(homeFragmentcontainerview.id)
findNavController.navigate(it.itemId)
true
}
五、Fragment的生命周期
想要了解Fragment的生命周期,离不开Activity的生命周期,所以我们会一起讲到。
Fragment的生命周期由FragmentManager管理,FragmentManager负责将Fragment添加到Activity中,并在Fragment不再需要时将其移除。在Fragment的生命周期中,有几个关键点需要注意:
1)当Fragment被添加到Activity中时,会依次调用onAttach()、onCreate()、onCreateView()、onActivityCreated()、onStart()和onResume()方法。
2)当Fragment对用户不可见时(如被另一个Activity覆盖),会依次调用onPause()、onStop()方法。
3)当Fragment被移除或宿主Activity被销毁时,会依次调用onPause()、onStop()、onDestroyView()、onDestroy()和onDetach()方法。注意,如果只是Fragment被切换了,不会调用onDestroy()方法,只是调用onDestroyView,不要以为他离开了界面就是销毁了
5.1 Fragment的生命周期通过一系列回调方法来管理,这些方法在Fragment状态变化时被调用。以下是Fragment的主要生命周期回调方法:
1)onAttach(Context context):当Fragment与Activity关联时调用。这是Fragment生命周期中的第一个回调方法,此时可以获取到宿主Activity的引用。
2)onCreate(Bundle savedInstanceState):当Fragment被创建时调用。用于初始化Fragment的基本数据,如加载布局文件等。
3)onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState):创建Fragment的视图层次结构。如果Fragment没有UI,可以返回null。
4)onViewCreated(View view, Bundle savedInstanceState):当Fragment的视图被创建时调用。此时可以对视图进行进一步的初始化操作。
5)onActivityCreated(Bundle savedInstanceState):当宿主Activity的onCreate()方法完成后调用。此时Fragment的视图层次结构已完全创建,可以安全地与Activity的视图进行交互。
6)onStart():Fragment对用户可见时调用。此时Fragment开始对用户可见,但可能还未完全交互。
7)onResume():Fragment开始与用户交互时调用。此时Fragment完全可见且可交互。
8)onPause():Fragment失去焦点或停止与用户交互时调用。此时应保存一些重要数据或停止一些操作,如动画、网络请求等。
9)onStop():Fragment完全不可见时调用。此时应释放一些资源或做一些清理工作。
10)onDestroyView():Fragment的视图被销毁时调用。此时应释放与视图相关的资源。
11)onDestroy():Fragment被销毁时调用。此时应释放Fragment的成员变量和数据,以便回收内存和资源。
12)onDetach():Fragment与Activity分离时调用。此时应做一些最终的清理工作。
onDestroy和onDestroyView方法的区别
一、onDestroyView():
1)当Fragment的视图层次结构(即UI)被销毁时调用。这通常发生在Fragment不再需要显示其UI时,可能是因为用户离开了包含该Fragment的Activity,或者因为Fragment被替换或移除了。
2)在onDestroyView()被调用后,Fragment仍然存在于内存中,并且其状态(如成员变量等)仍然保持。但是,与视图相关的资源应该被释放,以避免内存泄漏。
二、onDestroy():
1)当Fragment被销毁时调用。这是Fragment生命周期中的最后一个回调方法,表示Fragment即将被系统回收,其资源将被完全释放。
2)在onDestroy()被调用后,Fragment无法再接收到任何生命周期回调,并且其资源(如内存)将被系统回收。
onDestroyView()主要负责释放与视图相关的资源,而onDestroy()则负责清理Fragment占用的所有资源。
六、Android开发,建议是多个activity,还是activity结合fragment,原因是什么
主要取决于你的应用需求、设计目标以及用户体验的考虑。下面是一些关于这两种方式的优缺点以及选择建议:
1. 使用多个Activity
优点:
kt
清晰的逻辑分离:每个Activity可以专注于一个特定的任务或用户流程,使得代码更加模块化和易于管理。
任务栈管理:Android系统通过任务栈管理Activity,可以很方便地实现页面之间的跳转和返回,尤其是在处理复杂的用户流程时。
内存管理:当Activity不再需要时,系统可以更容易地回收其占用的资源。
缺点:
kt
界面切换动画:Activity之间的切换可能会比Fragment之间的切换更重,因为涉及到整个界面的重建。
数据共享:Activity之间的数据共享可能需要通过Intent、全局变量或数据库等方式,相对复杂。
用户体验:在某些情况下,过多的Activity跳转可能会导致用户感到困惑,尤其是在需要频繁切换界面时。
2.使用Activity结合Fragment
优点:
kt
灵活的界面布局:Fragment可以在一个Activity中重用,并且可以在运行时动态地添加、移除或替换,非常适合实现复杂的用户界面布局。
更好的用户体验:Fragment之间的切换通常比Activity之间的切换更流畅,因为不需要重建整个界面。
数据共享:Fragment与宿主Activity之间的数据共享更加直接和方便,可以通过接口回调等方式实现。
缺点:
```kt
复杂性增加:Fragment的生命周期比Activity更复杂,需要更仔细地管理,以避免内存泄漏等问题。
状态管理:Fragment的状态管理可能更加复杂,尤其是在处理多个Fragment的嵌套和动态变化时。
选择建议
kt
如果你的应用界面相对简单,且每个界面之间的逻辑相对独立,可以考虑使用多个Activity。
如果你的应用需要实现复杂的用户界面布局,或者需要在同一个Activity中动态地展示不同的内容区域,那么使用Activity结合Fragment可能是一个更好的选择。
考虑用户体验和内存管理。Fragment之间的切换通常更流畅,但也需要更仔细地管理其生命周期和状态。
考虑应用的维护性和可扩展性。使用Fragment可以使代码更加模块化和可重用,但也可能增加复杂性。
七、返回键如何处理
在activity中,我们导航了很多的Fragment,比如从A Framgent到了B Fragment,再到了C Fragment,如果我想点击物理的返回按键,如何返回到到一个Fragment呢?并且,如果到了最初的A Framgnet,想提示如果再次点击将退出app这种的提示应该如何写呢?
kt
override fun onBackPressed() {
val navController =findNavController(mBinding.homeFragmentcontainerview.id)
val fragmentPopped = navController.popBackStack()
Log.d("fragmentPopped", "onBackPressed: "+fragmentPopped)
// 如果popBackStack()返回false,意味着没有Fragment可以返回了
// 这里你可以假设已经回到了最开始的Fragment,或者根本就没有Fragment在栈中
if (!fragmentPopped) {
// 没有任何Fragment可以返回了,结束Activity
// 获取当前时间戳
val currentTime = System.currentTimeMillis()
// 判断与上一次点击返回键的时间间隔是否小于1秒
if (currentTime - backPressedTime < 1000) {
super.onBackPressed() // 执行默认的返回键操作(退出Activity)
} else {
Toast.makeText(this, "再次点击返回键退出", Toast.LENGTH_SHORT).show()
backPressedTime = currentTime // 更新上一次点击返回键的时间戳
}
}
}
navController.popBackStack()会移除栈顶的Fragment,如果存在fragment,就会返回true,否则返回false。
7.1 假如说,我想从A Fragment跳到B Fragment的时候,我想A Fragment直接就销毁了呢?
xml
<fragment
android:id="@+id/home_homefragment"
android:name="com.quyunshuo.wanandroid.home.ui.fragment.HomeFragment"
android:label="HomeFragment" >
<action
android:id="@+id/home_action_home_homefragment_to_home_makefragment"
app:destination="@id/home_makefragment"
app:popUpTo="@id/home_homefragment"
app:popUpToInclusive="true"/>
<fragment/>
app:popUpTo="@id/home_homefragment"
app:popUpToInclusive="true"
的意思就是跳转的时候,将之前的Fragment销毁掉。就保留自己作为栈顶
八、Navigation搭配Fragment使用后,事务是怎么样?
当使用Navigation组件来管理Fragment的创建和切换时,Fragment的事务(Transaction)是通过Navigation图(Navigation Graph)和NavController来隐式处理的。这种方式简化了Fragment的切换逻辑,让开发者可以更专注于Fragment之间的交互和业务逻辑的实现。
Fragment事务的隐式处理
1)在传统的Fragment管理中,你可能需要显式地调用FragmentManager的beginTransaction()、add()、replace()等方法来创建Fragment事务。但在使用Navigation组件时,这些操作都被封装在Navigation图内,并由NavController根据导航操作(如点击按钮、接收通知等)来自动处理。
2)例如,当你在Navigation图中定义了两个Fragment(A和B),并从一个Fragment(A)到另一个(B)的导航时,你只需要在A Fragment中调用NavController的navigate()方法,并传入目标Fragment的ID或Action ID。NavController会根据Navigation图自动处理Fragment的添加、替换和移除等事务。