在本章,我们首先介绍了Fragment的使用方式:简单用法、FragmentManager和Transaction动态添加、返回栈防止退出、Fragment和Activity之间的信息传递。随后介绍了Fragment生命周期、限定符和最小宽度限定符。最后我们介绍了扩展函数(ClassName.methodName)和运算符(operator fun plus())。
5.1.Fragment是什么
      手机屏幕在3英寸~6英寸之间,平板在7英寸~10英寸之间,屏幕大小差距过大会导致元素过分拉长、元素空隙过大的问题。因此引入Fragment概念。Fragment是嵌入在Activity当中的UI片段,能让程序更合理和充分地利用大屏空间,可以理解为迷你型Activity。
5.2.Fragment的使用方式
       建立Pixel C平板播放器。新建FragmentTest项目。
5.2.1.Fragment简单用法
       新建左侧Fragment布局left_fragment.xml和右侧right_framgent.xml。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><Buttonandroid:layout_gravity="center_horizontal"android:id="@+id/left_button"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="Button" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#00ff00"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center_horizontal"android:text="This is right fragment"android:textSize="24sp" />
</LinearLayout>

在建立LeftFragment和RightFragment类,基于Fragment加载刚才的布局xml。

package com.example.myapplicationimport android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment//在这里一定要使用androidX的Fragment,因为他可以让Fragment的特性在所有Android系统中保持一致,系统内置的在9.0版本被废除。
class LeftFragment : Fragment() {//仅仅重写了onCreateView方法override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {//通过LayoutInflater将方才定义的left_fragment加载进来return inflater.inflate(R.layout.left_fragment, container, false)}
}
package com.example.myapplicationimport android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
//同样方式使用Fragment加载右布局
class RightFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.right_fragment, container, false)}
}

修改Activity_main里面的代码,通过Fragment标签加载Fragment。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal">
<!--使用Fragment标签在布局中添加Fragment,通过android:name来显式声明要添加的Fragment名,注意加上包名--><fragmentandroid:id="@+id/left_frag"android:name="com.example.myapplication.LeftFragment"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"/><fragmentandroid:id="@+id/right_frag"android:name="com.example.myapplication.RightFragment"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1"/>
</LinearLayout>

5.2.2.动态加载Fragment
     Fragment强大之处在于在程序运行时动态将其添加到Activity当中,下面来研究下。新建another_right_fragment.xml。

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

新建AnotherRightFragment类作为另一侧需要动态添加的Fragment。

package com.example.myapplicationimport android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragmentclass AnotherRightFragment : Fragment() {override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(R.layout.another_right_fragment, container, false)}
}

修改Activity_main的布局,确保能够动态映射到此布局。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><!--使用Fragment标签在布局中添加Fragment,通过android:name来显式声明要添加的Fragment名,注意加上包名--><fragmentandroid:id="@+id/left_frag"android:name="com.example.myapplication.LeftFragment"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" />
<!--将右侧的Fragment替换为FrameLayout,默认摆在左上角,无需任何定位。--><FrameLayoutandroid:id="@+id/frame_layout"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" />
</LinearLayout>

最后在MainActivity中向刚才的Framelayout中添加内容,基于FragmentManager的transaction事务。

package com.example.myapplicationimport androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.left_fragment.*
//完成动态添加Fragment的功能,五步走战略
class MainActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)//第一步,创建添加Fragment的实例left_button.setOnClickListener{replaceFragment(AnotherRightFragment())}replaceFragment(RightFragment())}fun replaceFragment(fragment:Fragment){//第二步,获取FragmentManagerval fragmentManager = supportFragmentManager//第三步,开启一个事务val transaction = fragmentManager.beginTransaction()//第四步,向容器内添加或者替换Fragment,一般使用replace来代替,传递id和添加的Fragment实例transaction.replace(R.id.frame_layout,fragment)//第五步,提交事务transaction.commit()}
}

5.2.3.在Fragment中实现返回栈
      点击Back键退出了程序,那么想要点击back键回到上一个Fragment。实现的方法是:FragmentTransaction中提供了addToBackStack方法,可以用于将一个事务添加到返回栈中。

    fun replaceFragment(fragment:Fragment){...transaction.replace(R.id.frame_layout,fragment)transaction.addToBackStack(null)//第五步,提交事务transaction.commit()
}

5.2.4.Fragment和Activity之间的交互
       虽然说是嵌入,但其实分属不同类。如果Activity想调用Fragment里面的方法或者是Fragment想调用Activity里面的方法,该咋办?
       为了方便交互,FragmentManager提供了一个类似于findViewByID的方法,专门从布局文件中获取Fragment的实例,代码如下所示。

 val fragment = supportFragmentManager.findFragmentById(R.id.left_frag) as LeftFragment

这样可以在Activity中简单获取Fragment的实例,轻松调用Fragment里面的方法。Kotlin-android-extensions中对findFragmentById方法进行扩展,允许直接使用布局文件中的id名称直接获取相应的Fragment实例,简化代码如下:

  val fragment = left_frag as LeftFragment 

Fragment如何调用Activity里面的方法呢?每个Fragment当中都可以通过getActivity获得和当前Fragment相关联的实例,代码如下:

        //MainActivity可能为null,因此需要判空处理if(activity!=null){//获得Activity实例,当需要context对象时,也可以使用getActivity方法val mainActivity = activity as MainActivity}

两个Fragment之间如何通信?Fragment找Activity,Activity获取另一个Fragment实例,这样完成了通信功能。
5.3.Fragment生命周期
       有四种状态:运行、暂停、停止和销毁四种状态。运行状态:Fragment相关联的Activity正在运行;暂停:当一个Activity进入暂停状态(另一个为占满屏幕的Activity进入栈顶),则与此相关联的Fragment也暂停;停止:Activity停止或者FragmentTransaction的remove和replace移除被调用,但在此之间调用了addToBackStack方法;终止:Activity被销毁或者FragmentTransaction的remove和replace移除被调用。未调用addToBackStack方法。
       几个回调方法:onAttach:Fragment和Activity建立关联;onCreateView:为Fragment创建视图时调用;onActivityCreated:确保与Fragment相关联的Activity已经创建完毕时调用;onDestroyView:与Fragment关联的视图被移除时调用;onDetach:两者解除关联时调用。


5.3.2.体验Fragment生命周期

修改RightFragment里面的代码:

package com.example.myapplicationimport android.content.Context
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment//同样方式使用Fragment加载右布局
class RightFragment : Fragment() {companion object {const val TAG = "RightFragment"}override fun onAttach(context: Context) {super.onAttach(context)Log.d(TAG,"onAttach")}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)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,"onCreate")}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")}
}

当RightFragment第一次被显式在屏幕上时。


     当替换Fragment时,RightFragment停止了,此时添加了addToBackStack方法就会显示如下:


     如果没有调用,RightFragment会被销毁,onDestroy和onDetach将会被调用。此时点击Back键的显示如下:


     再点击Back键会退出:


5.4.动态加载布局的技巧
   添加、替换功能太简单,如果能根据设备分辨率或者屏幕大小选择运行时加载那个布局,那么比较有意思。
5.4.1.使用限定符
       平板一般是双页模式(左边列表、右边内容),手机一般是单页,两页分开。如何确定应该是单页双页?需要借助限定符qualifier来实现。比如large限定符。
       修改FragmentTest项目里的Activity_main.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><!--使用Fragment标签在布局中添加Fragment,通过android:name来显式声明要添加的Fragment名,注意加上包名--><fragmentandroid:id="@+id/left_frag"android:name="com.example.myapplication.LeftFragment"android:layout_width="match_parent"android:layout_height="match_parent" />
</LinearLayout>

接着新建layout-large文件夹,同样新建布局activity_man.xml,建立目录过程如下:(1)鼠标右击app/res->New->Android Resource Directory;(2)右击layout-large->New->ayout resource file。参考链接和代码示例如下:参考链接

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><fragmentandroid:id="@+id/left_frag"android:name="com.example.myapplication.LeftFragment"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" /><fragmentandroid:id="@+id/right_frag"android:name="com.example.myapplication.RightFragment"android:layout_width="0dp"android:layout_height="match_parent"android:layout_weight="1" />
</LinearLayout>

注释相关的无用代码,此时,平板上是双屏,手机上是单屏。常用限定符如下:屏幕大小的small、normal、large和xlarge;分辨率大小的ldpi(<120)、mdpi(120~160)、hdpi(160~240)、xhdpi(240~320)以及xxhdpi(320~480);方向land(横屏)和port(竖屏)
5.4.2.使用最小宽度限定符
       使用large解决了单双页判断问题,但large多大是个问题?我们在这里使用最小宽度限定符来解决。其是对屏幕的宽度指定一个最小值(以dp为单位),然后以其为临界点。
       我们建立layout-sw600dp文件夹和activity_main.xml布局,代码和上面的一模一样。此意味着,当屏幕宽度大于等于600dp时,回家再layout-sw600dp/activity_main布局,否则加载layout/activity_main布局。
5.5.Kotlin之扩展函数和运算符重载
5.5.1.扩展函数

       扩展函数是指在不改边某个类源码的情况下,依然可以打开这个类,并向该类提供新的函数。举例来讲:字符串包含字母、数字和特殊符号,统计字符串中字母的数量,实现代码如下所示:

package com.companyobject StringUtil {fun letterCount(str: String): Int {var count = 0for (char in str) {if (char.isLetter()) {count++}}return count}
}
package com.companyfun main(){val str = "ABC!@#Xxyerw!@#"println(StringUtil.letterCount(str))
}

扩展函数的语法结构如下,相比于定义普通函数,我们只需要在前面加上一个ClassName.的语法结构,就可以将该函数添加到指定类中。

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

下面往String类中添加一个扩展函数,首先创建String.kt文件,将其定义成顶层方法一遍拥有全局访问域。

package com.companyfun String.letterCount(): Int {var count = 0for (char in this) {if (char.isLetter()) {count++}}return count
}

访问代码如下,看起来就像String类自带了lettersCount方法一样。

println(str.letterCount())

5.6.2.有趣的运算符重载
       运算符重载是Kotlin中一个有趣的语法。运算符包括+-*/%等,Kotlin运算符重载允许任意两个对象相加或者更多其他操作。譬如两个Money对象相加。
      运算符重载使用的是operator关键字,在指定函数前面使用operaor关键字,就可以实现运算符重载了。譬如加号运算符对应的是plus函数,减号对应的是minus函数。举例,完成两个Money对象的相加。

package com.company//主构造函数接受一个value参数,用于表示钱的金额
class Money(val value: Int) {//使用operator关键来修饰plus函数是必不可少,将两个对象的value值相加,得到和后封装返回operator fun plus(money: Money): Money {val sum = value + money.valuereturn Money(sum)}//仅仅单功能肯定不方便,Kotlin、允许我们对同一运算符进行多重重载,接受整型数字operator fun plus(newValue: Int): Money {val sum = value + newValuereturn Money(sum)}
}

Main函数调用代码如下:

package com.companyfun main() {val money1 = Money(5)val money2 = Money(10)val money3 = money1 + money2println(money3.value)
}

举例来讲,我们在前面使用了一个随机生成字符串长度的函数,如果使用str*n岂不是很棒?

    fun getRandomLengthString(str: String): String {val n = (1..20).random()var builder = StringBuilder()repeat(n) {builder.append(str)}return builder.toString()}

在String.kt添加如下代码:

//operator关键字必不可少,times对应*,定义扩展函数使用了String.。
operator fun String.times(n: Int): String {var builder = StringBuilder()repeat(n) {builder.append(this)}return builder.toString()
}

调用代码:

val str = "abc" * 10
println(str)

需要说明的是String类已经提供了将字符串重复n遍的repeat函数,因此可以进一步精简为:

operator fun String.times(n: Int): String = repeat(n)

最后重复任意次数的字符串代码如下:

fun getRandomLengthString(str: String): String = str * (1..20).random()

《第一行代码》第三版之探究Fragment(六)相关推荐

  1. 第一行代码-android-第三版-pdf扫描-思维导图-课件-源码

    第一行代码-android-第三版-pdf扫描-思维导图-课件-源码 一帮公众号各种要你关注, 各种压缩包层层套娃要密码, 还要进群, 真他妈日了gou了,找了半天 分享给大家, 毫无套路! pdf扫 ...

  2. 第一行代码第三版笔记

    第3章 Activity 主acitivity:程序运行起来首先启动的activity manifest <?xml version="1.0" encoding=" ...

  3. 第一行代码 (第三版) 第八,九,十章

    一: 泛型和委托 1.泛型 泛型类: class MyClass<T>{ fun method(param: T) : T { return param } } 泛型方法: class M ...

  4. 第一行代码 第三版 第11章网络技术 11.6.1 Retrofit 应用 报错:android.system.ErrnoException: isConnected failed: ECONNRE

    在学习第11章 11.6.1Retrofit用法,这节的时候发生的报错:书上关于这个地方并没有说. 我搜索问题的关键语句是: java.net.ConnectException: Failed to ...

  5. Android 学习之《第一行代码》第二版 笔记(二十三)Material Design 实战 —— 卡片式布局

    实现基础: Android 学习之<第一行代码>第二版 笔记(二十二)Material Design 实战 -- 悬浮按钮和可交互提示 卡片式布局 卡片式布局是 Materials Des ...

  6. Android build.gradle文件详解(转述自《Android第一行代码》第二版)

    Android build.gradle文件详解 1. 最外层目录下的build.gradle文件 1.1 repostories 1.2 dependencies 2. app目录下的build.g ...

  7. 安卓第一行代码第3版pdf_SPECFEM2D用户手册——第3章 网格生成——3.1 如何使用SPECFEM2D...

    参考资料 manual_SPECFEM2D.pdf 数值实现 Julia 1.4.2/MATLAB 2019a 备用系统 Ubuntu 64 地球物理局 地震波动力学实验室 谱元组 译# 声明 # 欢 ...

  8. 第一行代码读书笔记(Chapter2 探究新语言,快速入门Kotlin编程)

    准确来说,Java是解释性语言,Kotlin能被编译为class文件,再在虚拟机中运行 Kotlin几乎杜绝了空指针异常 运行Kotlin代码:IDEA创建Kotlin项目:在线运行kotlin代码: ...

  9. 第一行代码学习笔记第十章——探究服务

    知识点目录 10.1 服务是什么 10.2 Android多线程编程 * 10.2.1 线程的基本用法 * 10.2.2 在子线程中更新UI * 10.2.3 解析异步消息处理机制 * 10.2.4 ...

最新文章

  1. NR 5G MAC媒体接入控制
  2. 用python玩转数据第四周答案_大学慕课用Python玩转数据答案公众号
  3. MKL学习——功能简介
  4. 串口与modem流量控制大全(1)
  5. 信息学奥赛一本通C++语言——1059:求平均年龄
  6. 模仿下列程序自己打印一个趣味图案c语言,趣味程序导学C语言(28页)-原创力文档...
  7. git fatal: index file smaller than expected
  8. Python3 类(2)
  9. MySQL的条件判断函数
  10. 【HTML简易版“冒险岛”小游戏】(游戏效果展示+全部源代码分享)
  11. 企业管理软件开发新模式:抛开旧思维,轻松做系统
  12. 程序员面试揭秘之程序员靠什么途径去美国工作?
  13. 2015给自己充电加薪——免费领取极客学院VIP会员
  14. 全文搜索引擎,索引库
  15. 基于Smart200 PLC的运动控制组态说明
  16. 如何简单又快速的清理C盘内存
  17. 记录梦幻手游PC端辅助开发及设计思路之整体架构
  18. 计算机控制g s 求d s,自动控制原理-中国大学mooc-题库零氪
  19. 《诗经》诗无邪 —— 风篇
  20. 士兵队列训练问题(队列--先进先出)

热门文章

  1. python3 scrapy框架,Python3爬虫(十八) Scrapy框架(二)
  2. 《Python语言程序设计》王恺 王志 李涛 机械工业出版社第7章 I/O编程与异常 课后习题答案【强烈推荐】
  3. 《黑马》——C++提高编程
  4. 使用pandas的drop函数删除数据
  5. 金融知识杂记(一) 市盈率 市销率 市净率 逆市操作tips(jhs 0414)
  6. Qt多功能计算器(四)——base64加密和解密
  7. QQ空间小秘书 V1.30 贺岁最新版~~ 天空原创软件
  8. htc服务器更新系统,HTC U Ultra刷机教程 HTC U Ultra卡刷ruu升级更新官方系统
  9. 计算机网络地址 菜鸟教程
  10. 鲲鹏计算展蓝图,无限风光在广东