《第一行源代码》第三版第五章(Frgment)

5.1 Fragment是什么

Fragment是一种可以嵌入在Activity当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用得非常广泛。它能够让程序更加合理和充分地利用大屏幕的空间,出现的初衷是为了适应大屏幕的平板电脑,可以将其看成一个小型Activity,又称作Activity片段。

Fragment是Android3.0引入的新API,可以把Fragment想成Activity中的模块,这个模块有自己的布局,有自己的生命周期,单独处理自己的输入,在Activity运行的时候可以加载或者移除Fragment模块。 可以把Fragment设计成可以在多个Activity中复用的模块,当开发的应用程序同时适用于平板电脑和手机时,可以利用Fragment实现灵活的布局,改善用户体验。
在这里插入图片描述

5.2 Fragment的使用

1. Fragment的简单用法

新建一个left_fragment文件(左边frgment的布局)
新建一个right_fragment文件(右边frgment的布局)
代码如下:

//左边
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:text="Button"/>
</LinearLayout>

//右边
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#00ff00"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="24sp"
        android:text="This is right frgment"/>
</LinearLayout>

新建左边类 LeftFragment,新建右边类RightFrament
(将刚刚的布局动态加载)
代码如下:

//左边
class LeftFragment:Fragment() {
    override fun onCreateView(inflater: LayoutInflater,container:ViewGroup?,savedInstanceState: Bundle?):View {
        return inflater.inflate(R.layout.left_fragment,container,false)
    }
}

//右边
class RightFragment: Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
        return inflater.inflate(R.layout.right_fragment,container,false)
    }
}

最后在activity_main里面注册

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/LeftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/RightFrag"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</LinearLayout>

2. 动态添加Fragment

//创建一个AnotherRightFragment类和一个布局文件
class AnotherRightFragment :Fragment(){
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.another_right_fragment,container,false)
    }
}

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:background="#ffff00"		//换了颜色
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:textSize="24sp"
        android:text="This is anohter right frgment"/>
</LinearLayout>

//使用FrameLayout 放入Fragment 
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/LeftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />

    <FrameLayout	//FrameLayout替换fragment
        android:id="@+id/rightLayout"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
</LinearLayout>

//修改MainActivity 实现动态添加Fragment的功能
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener{
            replaceFragment(AnotherRightFragment())
        }
        replaceFragment(RightFragment())
    }
    private fun replaceFragment(fragment: Fragment){
        val fragmentManager = supportFragmentManager
        val transaction = fragmentManager.beginTransaction()
        transaction.replace(R.id.rightLayout,fragment)
        transaction.commit()
    }
}

总结动态添加Fragment的步骤:在这里插入图片描述

3. 在Fragment中实现返回栈

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener{
            replaceFragment(AnotherRightFragment())
        }
        replaceFragment(RightFragment())
    }
    private fun replaceFragment(fragment: Fragment){
        val fragmentManager = supportFragmentManager
        val transaction = fragmentManager.beginTransaction()
        transaction.replace(R.id.rightLayout,fragment)
        transaction.addToBackStack(null)
        //使用FragmentTransaction的addToBackStack()方法
        transaction.commit()
    }
}

4. Fragment和Activity之间的交互

Activity里调用Fragment的方法

  1. 第一种
    val fragment = supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment
    调用FragmentManager的findFragmentById()方法可以在Activity中得到相应的Fragment实例,然后就可以调用Fragment的方法了
  2. 第二种
    val fragment = leftFrag as LeftFragment
    kotlin中的插件允许我们直接使用布局文件中定义的Fragment id 名 自动获取Fragment实例

Fragment里调用Activity的方法
if(activity != null){
val mainActivity = activity as MainActivity
}
通过getActivity()方法来得到和当前Fragment相关联的Activity
由于getActivity()方法可能会返回null 所以这边进行判空处理

5.3Fragment的生命周期

1. Fragment的状态和回调

  1. 运行状态
    Fragment所关联的Activity 处于运行状态,该Fragment也处于运行状态
  2. 暂停状态
    Fragment所关联的Activity 处于暂停状态,该Fragment也处于暂停状态
  3. 停止状态
    Fragment所关联的Activity 处于停止状态,该Fragment也处于停止状态或者Fragment从Activity中移除,在事务提交之前用了addToBackStack()方法,这时的Fragment也会进入停止状态
  4. 销毁状态
    Fragment所关联的Activity 处于销毁状态,该Fragment也处于销毁状态或者Fragment从Activity中移除,在事务提交之前没用了addToBackStack()方法,这时的Fragment也会进入停止状态

回调方法:
onAttach() 当Fragment和Activity建立关联时调用。

onCreateView() 为Fragment创建视图(加载布局)时调用。

onActivityCreated() 确保与Fragment相关联的Activity已经创建完毕时调用。

onDestroyView() 当与Fragment关联的视图被移除时调用。

onDetach() 当Fragment和Activity解除关联时调用。

在这里插入图片描述

2. 体验Fragment的生命周期

class RightFragment: Fragment() {

    companion object{
        const val TAG = "RightFragment"
    }

    override fun onSaveInstanceState(outState: Bundle) {
    //该方法用于数据的存储 防止进入停止状态的Fragment系统内存不足时被回收
        super.onSaveInstanceState(outState)
        val tempData = "something you just typed"
        outState.putString("data_key",tempData)
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        Log.d(TAG,"onAttach")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if (savedInstanceState != null){
            val tempData = savedInstanceState.getString("data_key")
            //保存的数据在该方法中可以重新得到
            //onCreateView() onActivityCreated()也可以获得
            Log.d(TAG,tempData)
        }
        Log.d(TAG,"onCreate")
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        Log.d(TAG,"onCreateView")
        return inflater.inflate(R.layout.right_fragment,container,false)
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        Log.d(TAG,"onActivityCreated")
    }

    override fun onStart() {
        super.onStart()
        Log.d(TAG,"onStart")
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG,"onResume")
    }

    override fun onPause() {
        super.onPause()
        Log.d(TAG,"onPause")
    }

    override fun onStop() {
        super.onStop()
        Log.d(TAG,"onStop")
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d(TAG,"onDestroyView")
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG,"onDestroy")
    }

    override fun onDetach() {
        super.onDetach()
        Log.d(TAG,"onDetach")
    }
}

5.4 动态加载布局的技巧

1. 使用限定符

//修改activity_main  作为单页模式使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/LeftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</LinearLayout>

//新建large\activity_main(layout-large 文件夹)  作为双页模式使用
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <fragment
        android:id="@+id/LeftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
    <fragment
        android:id="@+id/RightFrag"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" />
</LinearLayout>
//最后需要将MainAcitvity里的replaceFragment()里的代码注释掉

large是一个限定符 那些被认为是large的设备会自动加载layout-large文件下的布局
在这里插入图片描述

2.使用最小宽度限定符

最小宽度限定符允许我们对屏幕指定一个最小值(以dp为单位),然后以这个最小值为临界点,屏幕宽度大于这个值的设备就加载一个布局,屏幕宽度小于这个值的设备就加载另一个布局

//新建layout—sw600dp 文件夹中新建activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <fragment
        android:id="@+id/LeftFrag"
        android:name="com.example.fragmenttest.LeftFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
    <fragment
        android:id="@+id/RightFrag"
        android:name="com.example.fragmenttest.RightFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3" />
</LinearLayout>

这就意味程序在屏幕宽度>=600dp的设备上时,加载layout-sw600dp里的布局否则就说layout/activity_main布局

5.5 Fragment的最佳实践:一个简易的新闻应用

由于代码量比较多,书中也有,这边就不写了。
有需要源代码的话可以去关注微信公众号“郭霖” 领取随书资源
这边仅说明一下,个人在实现的时候,出现的一些问题

  1. item的android:layout_height="wrap_content"写成android:layout_height=“match_parent”
    结果如图:
    在这里插入图片描述
  2. layout-sw600dp 创建错误结果一直报错
    错误原因:
    cannot find the declaration of element ‘ConstraintLayout’
    解决办法:
    在res下的layout文件夹名改为layout-xxxx
    (使用new->Android Resource Diretory创建)

3.getStringExtra用成getStringArrayExtra
结果:在这里插入图片描述

5.6 kotlin课堂:拓展函数和运算符重载

1.大有用途的扩展函数

Kotlin的扩展函数可以让你作为一个类成员进行调用的函数,但是是定义在这个类的外部。这样可以很方便的扩展一个已经存在的类,为它添加额外的方法。在Kotlin源码中,有大量的扩展函数来扩展java,这样使得Kotlin比java更方便使用,效率更高。通常在java中,我们是以各种XXXUtils的方式来对已经存在的类进行功能的扩展。但是有了扩展函数,我们就能丢弃让人讨厌的XXXUtils方法工具类。
语法如下:

fun ClassName.methodName(param1: Int, param2: Int): Int {
      return 0
}

下面我们为String类添加一个扩展函数(统计字母数)
书中建议向哪个类添加扩展函数,就定义一个同名的.kt文件,并且定义为顶层函数

fun String.lettersCount(): Int {
    var count = 0
    for (char in this) {
        if (char.isLetter()) {
            count++
        }
    }
    return count
}


fun main() {
    val stringcount = "abc123*-".lettersCount()
    println(stringcount)
}
//输出结果  3

2. 有趣的运算符重载

Kotlin的运算符重载(关键字:operator)允许我们让任意两个对象进行相加,或者是进行更多其他的运算操作。
这里以加号运算符为例,如果想要实现让两个对象(运算符重载在类种)相加的功能,那么它的语法结构如下:

class Obj {
      operator fun plus(obj: Obj): Obj {
            // 处理相加的逻辑
      }
}
//对应的调用方式
val obj1 = obj()
val obj2 = obj()
val obj3 = obj1 = obj2
//对象与对象相加
class Money(val value: Int) {

    operator fun plus(money: Money): Money {
        val sum = value + money.value
        return Money(sum)
    }
//对象与数字直接相加
    operator fun plus(newValue: Int): Money {
        val sum = value + newValue
        return Money(sum)
    }
}

fun main(){
    val m1 = Money(5)
    val m2 = Money(5)
    val m3 = m2 + m1
    val m4 = m1 + 20
    println(m3.value)
    println(m4.value)
}

运算符语法糖表达式和实际调用函数对照表:
在这里插入图片描述
需要更详细的话可以看:https://www.jianshu.com/p/d445209091f0

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值