Android笔记(三)

Android笔记(三)

文章目录

1. Fragment

可以嵌入Activity的UI片段(fragment:片段)

1.1 Fragment使用方式

简单用法:

  1. 新建两个Fragment布局left_fragment.xml和right_fragment.xml
  2. 新建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类与其类似
  1. 编写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步骤:

  1. 创建待添加Fragment实例
  2. 获取FragmentManager,在Activity中可以直接调用getSupportFragmentManager()方法获取
  3. 开启一个事务,通过调用beginTransaction()方法开启
  4. 像容器添加或替换Fragment,一般用replace()方法实现,参数为容器id和带传入的Fragment实例
  5. 提交事务

实现返回栈:

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博客

上一篇:knowledge_pig


下一篇:2021-2022-1 20211427 《信息安全专业导论》第九周学习总结