Android开发(29)——Fragment和Navigation

本节内容

1.简述

2.Fragment的创建

3.添加动画

4.Fragment数据传递

5.显示HomeFragment

6.添加toolBar关联

7.设置ButtonNavigationView

8.抽屉布局drawLayout

9.页面切换和数据传递

一、简述
1.对于MVVM来说,View指可交互的视图。ViewModel一般是管理数据的,通常是MutableLiveData。Model就是数据模块,通常需要一个Repository来联系Model和ViewModel。
2.Activity:管理一个界面的生命周期。
3.Fragment :可以理解为一个小型的Activity。一个手机app,用户在使用的时候,手机屏幕上只会显示一个页面,但是会有很多不同的页面进行切换。Fragment就相当于每一个小的页面,而管理这些页面的就是ViewPager。当用户滑动页面时,ViewPager就会把当前这个Fragment移出去,然后换下一个新的Fragment进来。
二、Fragment的创建
1.Fragment 表示应用界面中可重复使用的一部分。Fragment 定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。Fragment 不能独立存在,而是必须由 Activity 或另一个 Fragment 托管。Fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。
2.FragmentActivity之间的关系:
  • Fragment依赖于Activity
  • Fragment 就是Activity中不同功能的分离,它有自己的生命周期。
3.Fragment的生命周期。
  • onAttach():当Fragment被添加到activity中时调用
  • onCreate():创建Fragment时调用
  • onCreateView():Fragment具体显示的内容 -xml
  • onActivityCreated():当Activity创建好了之后,才会调用这个方法。相对Fragment进行操作的时候,就在这个方法里面操作。
Fragment生命周期
4.静态使用Fragment:
  • (1)在Activity对应的xml中添加Fragment组件
 
  • (2)自定义一个类继承于fragment。比如我们创建一个类,名为LoginFragment,继承于Fragment,然后重写对应的生命周期方法。onCreateView方法必须实现。
class LoginFragment :Fragment() {
     override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.fragmentlogin,container,false)
    }
}
  • (3)创建xml文件 进行页面布局。创建一个fragmentlogin.xml文件,自己随便布局一下。
  • (4)使用LayoutInflater解析布局文件.
return inflater.inflate(R.layout.fragmentlogin,container,false)
  • (5)在xml中设置fragment对应的name属性。在activity_xml的里面设置。
android:name="com.example.fragment.LoginFragment"
5.在activity.xml中添加几个按钮。最上方是fragment。
xml布局
5.动态使用Fragment。
  • (1)在Activity对应的xml中添加FrameLayout组件.

  • (2)自定义一个类继承于fragment(3)重写对应的生命周期方法 onCreateView(4)创建xml文件 进行页面布局(5)用layoutInflater解析布局文件
  • (6)使用FragmentManager管理fragment的切换, MainActivity中使用supportFragmentManager 获取FragmentManager,获取FragmentTransaction对象。
val fragmentTransaction = supportFragmentManager.beginTransaction()
fragmentTransaction.add(R.id.mContainer,LoginFragment())
fragmentTransaction.commit()
  • mContainer是activtiy里面FrameLayout容器的id。
运行程序,得到以下结果。
运行结果
6.新建一个Fragment,然后布局一下对应的xml文件。随便布局一下。
7.然后在MainActivity里面实现一下mReplace的监听事件,当我们点击REPLACE按钮的时候,它就会直接替换。
 mReplace.setOnClickListener { 
supportFragmentManager
.beginTransaction()
.replace(R.id.mContainer,RegistFragment("jack"))
.commit()
        }
点击replace按钮之后
三、添加动画
1.前面点击按钮完了之后,文本切换了,但是点击下方的返回按钮时并不会回到原来的页面,即刚刚那个fragment并不在栈里面。如果想要点击返回时回到之前的界面,那么可以添加一个.addToBackStack(null),这样就可以让它在栈里面了。
我们通过实现add按钮的点击事件来展示一下是如何使用的。
 mAdd.setOnClickListener {
 supportFragmentManager
.beginTransaction()
.add(R.id.mContainer,RegistFragment("rose"))
.addToBackStack(null)
.commit()
        }
  • 这个函数的意思就是把它添加到回退栈里面。
2.给这个页面切换添加动画效果,首先添加一个anim包,然后添加动画的资源文件。
目录
  • slide_in.xml

    

  • slide_out.xml

    

  • 在MainActivity里面用上这个动画。
.setCustomAnimations(R.anim.slide_in,R.anim.pop_out)
3.重新布局一下fragmentlogin页面,如下图所示。
fragmentlogin页面
4.重新布局一下activity_main.xml,把那几个按钮都删掉,只留下一个FrameLayout,然后把它的高设为0dp,宽度设为match_parent。
5.实现上面LOGIN按钮的点击事件,要在LoginFragment类里面实现。重写一下这个类里面的onActivityCreated方法,然后在里面先获取子控件,再实现其点击事件。
   override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        val btn= view?.findViewById
6.在MainActivity里面使用一下supportFragmentManager
supportFragmentManager
            .beginTransaction()
            .replace(R.id.mContainer,LoginFragment())
            .commit()
7.如果这样的话,点击返回按钮并没有动画。想要点击返回按钮也有动画的话,那么就要再在anim包里面添加两个动画,然后把它们添加到.setCustomAnimations里面
.setCustomAnimations(R.anim.slide_in,R.anim.slide_out,R.anim.pop_in,R.anim.pop_out)
四、Fragment数据传递
1.给ResgisterFragment添加一个构造方法
class RegistFragment ( val name:String):Fragment()
2.给fragmentregister.xml添加一个TextView,然后在ResgisterFragment类里面实现onActivityCreated方法
override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        view?.findViewById(R.id.mTextView)?.text  =name
    }
3.在LoginFragmentonActivityCreated方法里面先把EditText里的内容解析出来。
val nameTv = view?.findViewById(R.id.mNameTv)
  • 然后在replace方法里面把解析到的内容传进去。
.replace(R.id.mContainer,RegistFragment(nameTv?.text.toString()))
  • 这样数据就能传递过去了。但是一旦把手机旋转一下,就会报错。因为旋转屏幕之后,Activity重新走了一遍生命周期,所以Fragment也要重新创建一下。但是它重新创建的时候需要我们给它的构造函数添加一个参数,可是我们给不了。如果手机不支持屏幕旋转功能的话,那么就可以这样写。但是如果手机屏幕可以旋转,这样就不行。
4.想要让屏幕旋转过来后数据也不会变的话,可以像下面一样操作。使用View Model,但是一般情况下都不会这么用。
  • 先创建一个MyViewModel类继承自ViewModel,然后在里面添加一个liveData类型的数据。
class MyViewModel:ViewModel() {
    var fragment:MutableLiveData?=MutableLiveData()
}
  • LoginFragment类里面的onActivityCreated方法里面,用rf来记录数据。
  override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        val btn= view?.findViewById
  • 然后在MainActivity先懒加载一个变量,在onCreate方法把它解析出来。然后判断它是不是空,如果是空的就把Fragment传递过去,否则就把它作为livedata进行监听。
class MainActivity : AppCompatActivity() {
lateinit var viewModel:MyViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

         viewModel = ViewModelProvider(this,ViewModelProvider.NewInstanceFactory()).get(MyViewModel::class.java)
        if (viewModel.fragment?.value==null){
            viewModel.fragment?.value = LoginFragment()
        }
       viewModel.fragment?.observe(this, Observer {
           supportFragmentManager
               .beginTransaction()
               .replace(R.id.mContainer,it)
               .commit()
       })
   }
}
  • 最后屏幕转过来数据也没有改变。
屏幕旋转后
5.传数据用arguments来传,但这还不是最好的写法。
五、显示homefragment
1.前情提要
  • NavController:管理界面切换。
  • NavGraph:导航图。定义好了界面之间的跳转。
2.因为我们要实现在目的地之间传递数据,所以在gradel的dependencies里面添加以下代码。
def nav_version = "2.3.1"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
3.我们要生成适用于 Kotlin 独有的模块的 Kotlin 代码,那么就在第二个gradle里面添加以下代码。
apply plugin: "androidx.navigation.safeargs.kotlin"
4.先创建一个名为HomeFragment的类,继承自Fragment()。可以直接通过构造方法把页面id 传过来,这样就不需要在onCreateView里面再解析了。
class HomeFragment :Fragment(R.layout.fragment_home){
}
5.创建一个和上面这个类匹配的xml文件,取名fragment_home。随意布局一下这个页面,我的如下图所示。给按钮添加一个id。
fragment_home.xml布局
6.创建一个navGraph来管理页面之间的跳转。新建一个navigation的包,然后再创建一个navigation的xml文件。
文件目录
  • 在fragment_home.xml中关联一下HomeFragment类。
tools:context="HomeFragment"
  • 在nav_graph.xml中把fragment_home.xml文件添加进来
nav_graph界面
7.完成这个新的demo我们需要完成以下工作:
  • Navigation Graph 导航图,管理fragment之间的逻辑关系
  • NavigationFragment 容器
  • NavController 导航控制器,控制页面之间的切换。
8.我们已经完成了第一步,现在我们来完成第二步,添加容器。在activity_xml里面,拖动containers的NavHostFragment过来,然后选择nav_graph。
拖动一个containers
9.当它运行起来,显示的就是我们刚刚布局的那个页面。
运行结果
六、添加toolBar关联navVontroller
1.在activity.xml中添加一个toolBar,并把fragment的顶部约束到toolBar的下方。

2.然后我们在AndroidManifest.xml中把theme改为NoActionBar。这样就不会显示系统自带的Bar了。