使用Fragment 我们可以把页面结构划分成几块,每块使用一个Fragment来管理。这样我们可以更加方便的在运行过程中动态地更新Activity中的用户界面,日后迭代更新、维护也是更加方便
注意事项: Fragment并不能单独使用,他需要嵌套在Activity 中使用,尽管他拥有自己的生命周期,但是还是会受到宿主Activity的生命周期的影响,比如Activity 被destory销毁了,他也会跟着销毁!一个Activity可以嵌套多个Fragment。
图片和部分文字转载自https://www.songyubao.com/book/primary/activity/fragment.html
1.Fragment的生命周期
四个场景用于加深对Fragment生命周期的理解
①Activity加载Fragment的时候,依次调用下面的方法: onAttach -> onCreate -> onCreateView -> onActivityCreated -> onStart ->onResume
②当我们启动一个新的页面, 此时Fragment所在的Activity不可见,会执行 onPause
③当新页面返回后,当前Activity和Fragment又可见了,会再次执行onStart和 onResume
④退出了Activity的话,那么Fragment将会被完全结束, Fragment会进入销毁状态 onPause -> onStop -> onDestoryView -> onDestory -> onDetach
onActivityCreated 已过时
常用 onCreateView 当前fragment所对应的视图对象
onResume 恢复动画/视频播放
2. Fragment的动态添加与数据传递
2.1 动态添加Fragment
class SecondActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val fragment = SecondFragment()
val ft:FragmentTransaction = supportFragmentManager.beginTransaction()
ft.add(R.id.container , fragment)
ft.commitAllowingStateLoss()
}
}
2.2 Fragment常见的操作
val fragment = StudyFragment()
val ft = supportFragmentManager.beginTransaction()
if(!fragment.isAdded()){
ft.add(R.id.container,fragment)
}
ft.show(fragment)
ft.hide(fragment)
ft.remove(fragment)
ft.replace(R.id.container,fragment)
ft.commitAllowingStateLoss()
2.3 给Fragment传递数据
Activity传入数值
class SecondActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_second)
val fragment = SecondFragment()
val bundle = Bundle()
bundle.putInt("int_extra" , 100)
bundle.putString("string_extra" , "string_extra")
fragment.arguments = bundle
val ft:FragmentTransaction = supportFragmentManager.beginTransaction()
ft.add(R.id.container , fragment)
ft.commitAllowingStateLoss()
}
}
Fragment接受数据
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val intValue = arguments?.getInt("int_extra")
val strValue = arguments?.getString("string_extra")
val textview = view as TextView
textview.text = "${intValue} ---${strValue}"
}
思考:运行时Fragment如何获取Activity中的数据、又如何将数据传递到Activity中呢?
https://developer.android.com/guide/fragments/communicate?hl=zh-cn
设计并实现底部导航栏页面结构
Activity页面布局
此布局和如果 创建项目选择带导航栏 的样式相同
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical">
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/toggle_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:selectionRequired="false"
android:background="#08000000"
app:singleSelection="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/tab1"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@android:color/transparent"
android:text="Tab1"
android:textColor="#000000"
android:textSize="12sp"
app:icon="@drawable/ic_home_black_24dp"
app:iconGravity="textTop"
app:iconTint="@color/black"
tools:ignore="HardcodedText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/tab2"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@android:color/transparent"
android:text="Tab2"
android:textColor="#000000"
android:textSize="12sp"
app:icon="@drawable/ic_notifications_black_24dp"
app:iconGravity="textTop"
app:iconTint="@color/black"
tools:ignore="HardcodedText" />
<com.google.android.material.button.MaterialButton
android:id="@+id/tab3"
style="@style/Widget.MaterialComponents.Button.UnelevatedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@android:color/transparent"
android:text="Tab3"
android:textColor="#000000"
android:textSize="12sp"
app:icon="@drawable/ic_dashboard_black_24dp"
app:iconGravity="textTop"
app:iconTint="@color/black"
tools:ignore="HardcodedText" />
</com.google.android.material.button.MaterialButtonToggleGroup>
</LinearLayout>
tips
1.占用父布局(除了导航栏)剩余的空间
android:layout_height="0dp"
android:layout_weight="1"
2.水平平分
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
监听选中事件
在onCreate 方法中
binding.toggleGroup.addOnButtonCheckedListener { group:MaterialButtonToggleGroup, checkedId:Int , isChecked:Boolean ->
val childCount = group.childCount
var selectIndex = 0
for (index in 0 until childCount){
val button = group.getChildAt(index) as MaterialButton
if(button.id == checkedId){
selectIndex = index
button.setTextColor(Color.CYAN)
button.iconTint = ColorStateList.valueOf(Color.CYAN)
} else {
button.setTextColor(Color.BLACK)
button.iconTint = ColorStateList.valueOf(Color.BLACK)
}
}
switchFragment(selectIndex)
}
binding.toggleGroup.check(binding.tab1.id)
导航栏切换动态切换显示的fragment
private var tab1Fragment:SecondFragment?=null
private var tab2Fragment:SecondFragment?=null
private var tab3Fragment:SecondFragment?=null
private var shownFragment:Fragment ?= null
private fun switchFragment(selectIndex: Int) {
val fragment = when(selectIndex){
0->{
if(tab1Fragment == null){
tab1Fragment = SecondFragment()
val bundle = Bundle()
bundle.putString("tab" , "tab1")
tab1Fragment!!.arguments = bundle
}
tab1Fragment
}1->{
if(tab2Fragment == null){
tab2Fragment = SecondFragment()
val bundle = Bundle()
bundle.putString("tab" , "tab2")
tab2Fragment!!.arguments = bundle
}
tab2Fragment
}2->{
if(tab3Fragment == null){
tab3Fragment = SecondFragment()
val bundle = Bundle()
bundle.putString("tab" , "tab3")
tab3Fragment!!.arguments = bundle
}
tab3Fragment
}else -> {
throw IllegalStateException("下标不符合预期")
}
}?:return
val ft = supportFragmentManager.beginTransaction()
if(!fragment.isAdded){
ft.add(R.id.container , fragment)
}
ft.show(fragment)
if(shownFragment !=null){
ft.hide(shownFragment!!)
}
shownFragment = fragment
ft.commitAllowingStateLoss()
}
本例中多个fragment的生命周期
第一次点击各个tab 每个fragment onAttach ----> onResume
2021-11-11 16:34:22.661 5666-5666/com.example.componentlearn E/SecondFragment: onAttach
2021-11-11 16:34:22.662 5666-5666/com.example.componentlearn E/SecondFragment: onCreate
2021-11-11 16:34:22.665 5666-5666/com.example.componentlearn E/SecondFragment: onCreateView
2021-11-11 16:34:22.675 5666-5666/com.example.componentlearn E/SecondFragment: onStart
2021-11-11 16:34:22.679 5666-5666/com.example.componentlearn E/SecondFragment: onResume
2021-11-11 16:34:24.570 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab1-- true
2021-11-11 16:34:24.573 5666-5666/com.example.componentlearn E/SecondFragment: onAttach
2021-11-11 16:34:24.574 5666-5666/com.example.componentlearn E/SecondFragment: onCreate
2021-11-11 16:34:24.575 5666-5666/com.example.componentlearn E/SecondFragment: onCreateView
2021-11-11 16:34:24.576 5666-5666/com.example.componentlearn E/SecondFragment: onStart
2021-11-11 16:34:24.577 5666-5666/com.example.componentlearn E/SecondFragment: onResume
2021-11-11 16:34:25.737 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab2-- true
2021-11-11 16:34:25.739 5666-5666/com.example.componentlearn E/SecondFragment: onAttach
2021-11-11 16:34:25.740 5666-5666/com.example.componentlearn E/SecondFragment: onCreate
2021-11-11 16:34:25.741 5666-5666/com.example.componentlearn E/SecondFragment: onCreateView
2021-11-11 16:34:25.741 5666-5666/com.example.componentlearn E/SecondFragment: onStart
2021-11-11 16:34:25.745 5666-5666/com.example.componentlearn E/SecondFragment: onResume
tab3切换tab2
2021-11-11 16:35:53.583 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab3-- true
2021-11-11 16:35:53.584 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab2-- false
tab2切换tab1
2021-11-11 16:35:55.533 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab2-- true
2021-11-11 16:35:55.533 5666-5666/com.example.componentlearn E/SecondFragment: onHiddenChanged : tab1-- false
当且仅当activity存在多个fragment , 并且我们调用了show - hide
fragment的生命周期会调用
override fun onHiddenChanged(hidden: Boolean)
不可见为true , 可见为false
Bug记录
使用view-binding的时候 addOnButtonCheckedListener无效果
原因setContentView位置和内容写错
正确的是
private lateinit var binding: ActivitySecondBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySecondBinding.inflate(layoutInflater)
setContentView(binding.root)
|