Android笔记(三)
文章目录
1. Fragment
可以嵌入Activity的UI片段(fragment:片段)
1.1 Fragment使用方式
简单用法:
- 新建两个Fragment布局left_fragment.xml和right_fragment.xml
 - 新建LeftFragment类和RightFragment类继承AndroidX库的Fragment(AndroidX库中的可以使其特性在所有Android系统版本中保持一致)
 
class LeftFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInsatnceState: Bundle?): View? {
        return inflater.inflate(R.layout.left_fragment. container, false)
    }
}
// RigthFragment类与其类似
 
- 编写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标签添加Fragment -->
	<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>
 
动态添加Fragment:
例:点击左侧Fragment中的按钮,使得右侧的Fragment变换
再创建一个another_right_fragment.xml和AnotherRightFragment类
将上述activity_main.xml第二个fragment标签修改为FrameLayout标签
<!-- FrameLayout布局作为容器放置我们动态添加的Fragment -->
<FrameLayout
	android:id="@+id/rightLayout"
    android:layout_width="0dp"
	android:layout_height="match_parent"
	android:layout_weight="1">
</FrameLayout>
 
给左侧布局中的按钮添加点击事件,实现动态添加Fragment
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        replaceFragment(RightFragment())
        button.setOnClickListener {
            replaceFragment(AnotherRightFragment())
        }
    }
    
    private fun replaceFragment(fragment: Fragment) {
        val fragmentManager = supportFragmentManager
        val transaction = fragmentManager.beginTransaction()
        // transaction:事务
        transaction.replace(R.id.rightLayout, fragment)
        transaction.commit()
    }
}
 
动态添加Fragment步骤:
- 创建待添加Fragment实例
 - 获取FragmentManager,在Activity中可以直接调用getSupportFragmentManager()方法获取
 - 开启一个事务,通过调用beginTransaction()方法开启
 - 像容器添加或替换Fragment,一般用replace()方法实现,参数为容器id和带传入的Fragment实例
 - 提交事务
 
实现返回栈:
FragmentTransaction提供给了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中,参数接收一个名字用于描述返回栈状态,一般传入null
Fragment和Activity交互:
-  
Activity调用Fragment
- FragmentTransaction提供了一个类似findViewById()方法用于从布局文件中获取Fragment实例
 val fragment = supportFragmentManager.findFragmentById(R.id.leftFrag) as LeftFragment
 -  
Fragment调用Activity
- 通过调用getActivity()方法得知当前相关联的Activity实例
 val activity = activity as MainActivity- 注意:getActivity()可能会返回null,需要进行判空处理
 
 -  
不同Fragment之间进行通信
- 一个Fragment找到相关联的Activity,在通过这个Activity去获取另一个Fragment实例
 
 
1.2 Fragment的生命周期
状态: Fragment的状态与其相关联的Activity相同
- 运行状态
 - 暂停状态
 - 停止状态:或者通过事务的remove()、replace()方法将Fragment从Activity移除;在事务提交之前调用了addToBackStack()方法
 - 销毁状态
 
回调方法: 除与Activity类似的回调方法外
- onAttach():当Fragment与Activity建立关联时调用
 - onCreateView():为Fragment创建视图(加载布局)时调用
 - onActivityCreated():确保与Fragment相关联的Activity创建完毕时调用
 - onDestoryView():当与Fragment相关联的视图被移除时调用
 - onDetach():当Fragment和Activity解除关联时调用
 
动态加载布局技巧:
使用限定符,限定符是在资源目录名的后添加的,比如layout_large资源目录,会在屏幕被认为是large的设备上自动加载该文件夹下的布局,小设备则会加载layout资源目录下的布局
| 大小限定符 | 描述 | 
|---|---|
| small | 提供给小屏幕设备的资源 | 
| normal | ……中等屏幕…… | 
| large | ……大屏幕…… | 
| xlarge | ……超大屏幕…… | 
| 分辨率限定符 | 描述 | 
|---|---|
| ldpi | 低分辨率(~120dpi) | 
| mdpi | 中等分辨率(120dpi~160dpi) | 
| hdpi | 高分辨率(160dpi~240dpi) | 
| xhdpi | 超高分辨率(240dpi~320dpi) | 
| xxhdpi | 超超高分辨率(320dpi~480dpi) | 
| 方向限定符 | 描述 | 
|---|---|
| land | 横屏 | 
| port | 竖屏 | 
最小宽度限定符:更加灵活地为不同设备加载布局,屏幕的宽度指定一个最小值(dp),如layout_sw600dp
相关知识:
《Android Fragment 非常详细的一篇》 - 简书
Fragment - Android 开发者
2. 广播机制
分类:
- 标准广播: 
  
- 异步执行,扩散传播
 - 效率高,无法被截断
 
 - 有序广播: 
  
- 同步执行,同一时刻只有一个会接收到(优先级高的先接收到)
 - BroadcastReceiver有先后顺序,可以被截断(broadcast:广播)
 
 
2.1 接收系统广播
动态注册监听时间变化:
class MainActivity : AppCompatActivity() {
    lateinit var timeChangeReceiver: TimeChangeReceiver
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // intent过滤器,指定接收什么广播
        val intentFilter = IntentFilter()
        intentFilter.addAction("android.intent.action.TIME_TICK")
        timeChangeReceiver = TimeChangeReceiver()
        // 注册广播
        registerReceiver(timeChangeReceiver, intentFilter)
    }
    
    override fun onDestory() {
        super.onDestory()
        // 动态注册的广播一定要取消注册
        unregisterReceiver(timeChangeReceiver)
    }
    
    inner class TimeChangeReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent, Intent) {
            // 该方法内编写接收到广播执行的逻辑
            Toast.makeTest(context, "Time has changed", Toast.LENGTH_SHORT).show()
        }
    }
}
 
查看完整的系统广播列表可以查看:
<Android SDK>/platforms/<任意android api版本>/data/broatcast_actions.text
静态注册实现开机启动:
注:动态注册具有很强的灵活性,但必须在程序启动之后才能接收广播,静态注册可以在程序未启动时接收广播
静态广播在安全性有很大的缺陷,所以Android 8.0之后所有隐式广播(大部分都为)不允许使用静态注册的方式来接收,只有少数可以
使用Android Studio快捷方式创建接收者类,会在清单文件中自动进行注册(创建时的Exported选项表示是否允许这个BroadcastReceiver接收本程序以外的广播,Enabled表示是否启动这个BroadcastReceiver)
静态注册使用<intent-filter>标签过滤接收的广播,开机启动的广播值为android.intent.action.BOOT_COMPLETED
所有敏感功能都需要在清单文件中使用<uses-permission>标签生命权限,开机权限为android.permission.RECEIVER_BOOT_COMPLETED
注意:不要在onReceive()方法中添加过多逻辑或耗时操作,因为BroadcastReceiver中不允许开启线程,当该方法运行时间过长没有结束时,程序就会出现错误
2.2 发送自定义广播
标准广播:
接收者和发送逻辑我们定义在同一个程序(项目)中,接收者通过静态注册方式接收广播(这些代码省略)
class MainActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        button.setOnClickListener {
            // 注册接收该广播的接收这的action值为下面这串值
            val intent = Intent("com.example.broadcasttest.MY_BROADCAST")
            // 传入当前应用程序包名,通过getPackageName()方法获取
            intent.setPackage(packageName)
            // 发送广播
            sendBroadcast(intent)
        }
    }
}
 
前面听到过静态注册不能接收隐式广播,而自定义的广播都是隐式广播,所以这个调用setPackage()方法,指定该广播发送给哪个应用程序,从而变成显示广播,否则接收不到该广播
有序广播:
有序广播发送广播的方法为sendOrderedBoradcast()(order:订单、顺序)
- 第一个参数:intent
 - 第二个参数:一个与权限相关的字符串
 
优先级高的接收者可以先接收到广播,通过在<intent-filter>的android:priority属性指定优先级(priority:优先级、优先的)
在BroadcastReceiver的onReceive()方法中可以通过abortBroadcast()方法截断广播
相关知识:
隐式广播例外情况 - Android 开发者
Android中action启动方法大全 - CSDN博客










