Kennem's Blog
  • 🏠主页
  • 🔍搜索
  • 📚文章
  • ⏱时间轴
  • 🔖标签
  • 🗂️分类
  • 🙋🏻‍♂️关于
主页 📚文章

💻技术

Kotlin协程(2)

Kotlin协程(2) 异常处理的必要性 @OptIn(ExperimentalStdlibApi::class) // 标记该代码使用了实验性标准库 API @Test // 表示这是一个测试函数 fun `test CoroutineContext extend2`() = runBlocking { // 使用 runBlocking 启动协程 // 创建一个协程异常处理器,用于捕获协程中的异常 val coroutineExceptionHandler = CoroutineExceptionHandler { _, exception -> println("Caught : $exception") // 打印捕获到的异常 } // 创建一个协程作用域,包含一个 Job,使用主线程调度器和异常处理器 val scope = CoroutineScope( Job() + Dispatchers.Main + coroutineExceptionHandler // Job() 用于管理协程的生命周期 ) // 在 IO 线程上启动一个新的协程 val job = scope.launch(Dispatchers.IO) { // 获取当前协程上下文中的调度器 val dispatcher = coroutineContext[CoroutineDispatcher] // 打印当前使用的调度器 println("Current dispatcher: $dispatcher") } // 等待协程完成 job.join() // 确保主协程在子协程完成后再继续 } 异常的传播 协程构建器有两种形式:1、自动传播异常(launch与actor)2、向用户暴露异常(async与produce), 当这些构建器用于创建一个根协程时(该协程不是另一个协程的子协程),前者这类构建器,异常会在它发生的第一时间被抛出,而后者则依赖用户来最终消费异常,例如通过await或receive。 // 根协程 @Test @OptIn(DelicateCoroutinesApi::class) fun `test exception propagation`() = runBlocking<Unit> { // 在 GlobalScope 中启动一个新协程,返回一个 Job 对象 val job = GlobalScope.launch { try { // 在协程中抛出一个 IndexOutOfBoundsException throw IndexOutOfBoundsException() } catch (e: Exception) { // 捕获异常并打印一条消息,指示捕获了 IndexOutOfBoundsException println("IndexOutOfBoundsException") // 如果需要可以选择打印堆栈轨迹 // e.printStackTrace() } } // 等待协程完成 job.join() // 在 GlobalScope 中启动一个新协程,并返回一个 Deferred 对象 val deffer = GlobalScope.async { // 在协程中抛出一个 ArithmeticException throw ArithmeticException() } try { // 等待异步任务完成并获取结果,如果任务抛出异常,这里会重新抛出异常 deffer.await() } catch (e: Exception) { // 捕获异常并打印一条消息,指示捕获了 ArithmeticException println("ArithmeticException") // 如果需要可以选择打印堆栈轨迹 // e.printStackTrace() } } 非根协程的异常 其他协程所创建的协程中,产生的异常总是会被传播 /* 非根协程,job是scope内的子协程,所以会直接消费掉异常 */ @Test fun `test exception propagation2`() = runBlocking { // 创建一个新的 CoroutineScope,使用 Job 作为其上下文元素 val scope = CoroutineScope(Job()) // 在 scope 中启动一个新协程,返回一个 Job 对象 val job = scope.launch { // 在协程中启动一个异步任务 async { // 在异步任务中抛出一个 IllegalArgumentException throw IllegalArgumentException() } } // 等待 launch 启动的协程完成 job.join() } 异常的传播特性 当一个协程由于一个异常而运行失败时,它会传播这个异常并传递给它的父级。接下来,父级会进行下面几步操作: 取消它自己的子级 取消它自己 将异常传播并传递给它的父级 SupervisorJob 使用SupervisorJob时,一个子协程的运行失败不会影响其他子协程,SupervisorJob不会传播异常给它的父级,它会让子协程自己处理异常。 需求:在作用域内定义作业的UI组件,如果任何一个UI的子作业执行失败了,它并不总是有必要取消整个UI组件,但是如果UI组件被销毁了,由于它的结果不再被需要了,它就有必要使所有的子作业执行失败。 @Test fun `test supervisorJob`() = runBlocking<Unit> { // 创建一个新的 CoroutineScope,使用 SupervisorJob 作为其上下文元素 val supervisor = CoroutineScope(SupervisorJob()) // 如果使用普通的 Job,注释掉上一行并启用以下行 // val supervisor = CoroutineScope(Job()) // 在 supervisor 作用域中启动第一个子协程,返回一个 Job 对象 val job1 = supervisor.launch { // 延迟 100 毫秒以模拟一些工作 delay(100) println("child 1") // 在第一个子协程中抛出一个 IllegalArgumentException throw IllegalArgumentException() } // 在 supervisor 作用域中启动第二个子协程,返回一个 Job 对象 val job2 = supervisor.launch { try { // 无限延迟以模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 当协程取消时,最终会打印 "child 2 finished" println("child 2 finished") } } // 以下是可选部分,如果需要,可以取消注释 // 延迟 200 毫秒后取消 supervisor 作用域内的所有协程 // delay(200) // supervisor.cancel() // 等待 job1 和 job2 都完成 joinAll(job1, job2) } SupervisorScope 当作业自身执行失败的时候,所有子作业将会被全部取消 @Test fun `test supervisorScope`() = runBlocking<Unit> { // 使用 supervisorScope 启动一个新的协程作用域 supervisorScope { // 在 supervisorScope 中启动一个子协程,返回一个 Job 对象 launch { // 延迟 100 毫秒以模拟一些工作 delay(100) // 输出 "child 1",表示子协程已完成其任务 println("child 1") // 在协程中抛出一个 IllegalArgumentException,以测试异常传播 throw IllegalArgumentException() } // 在 supervisorScope 中启动另一个协程 try { // 延迟一个非常长的时间,模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 当 supervisorScope 被取消时(由于异常),最终会打印 "child 2 finished." println("child 2 finished.") } } } @Test fun `test supervisorScope2`() = runBlocking<Unit> { // 使用 supervisorScope 来启动一个协程作用域 supervisorScope { // 在 supervisorScope 中启动一个子协程 val child = launch { try { // 输出 "The child is sleeping" 表示子协程进入睡眠状态 println("The child is sleeping") // 延迟无限长的时间,模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 当子协程被取消时,最终会打印 "The child is cancelled" println("The child is cancelled") } } // 让出线程以确保子协程启动并进入延迟状态 yield() // 在 supervisorScope 中抛出一个异常 println("Throwing an exception from the scope") throw AssertionError() // 抛出一个断言错误,用于测试作用域的异常处理 } } 异常的捕获 使用CoroutineExceptionHandler对协程的异常进行捕获 以下的条件被满足时,异常就会被捕获: 时机:异常是被自动抛出异常的协程所抛出的(使用launch,而不是async时); 位置:在CoroutineScope的CoroutineContext中或在一个根协程(CoroutineScope或者supervisorScope的直接子协程)中。 Android中全局异常处理 全局异常处理器可以获取到所有协程未处理的未捕获异常,不过它并不能对异常进行捕获,虽然不能阻止程序崩溃,全局异常处理器在程序调试和异常上报等场景中仍然有非常大的用处。 我们需要在classpath下面创建META-INF/services目录,并在其中创建一个名为kotlinx.coroutines.CoroutineExceptionHandler的文件,文件内容就是我们的全局异常处理器的全类名。 取消与异常 取消与异常紧密相关,协程内部使用CancellationException来进行取消,这个异常会被忽略。 当子协程被取消时,不会取消它的父协程 如果一个协程遇到了CancellationException以外的异常,它将使用该异常取消它的父协程。当父协程的所有子协程都结束后,异常才会被父协程处理。 @Test fun `test cancel and exception`() = runBlocking<Unit> { // 启动一个协程作为父协程 val job = launch { // 在父协程中启动一个子协程 val child = launch { try { // 子协程尝试延迟执行,模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 当子协程被取消时,执行此块 println("Child is cancelled") } } // 让父协程暂时让出线程,给其他协程执行的机会 yield() println("Cancelling child") // 取消子协程,并等待其执行完 finally 块 child.cancelAndJoin() // 再次让出线程 yield() println("Parent is not cancelled") } // 等待父协程完成执行 job.join() } @Test fun `test cancel and exception2`() = runBlocking<Unit> { // 定义一个 CoroutineExceptionHandler 来处理未捕获的异常 val handler = CoroutineExceptionHandler { _, exception -> println("Caught: $exception") } // 使用 GlobalScope 启动一个顶层协程,并应用异常处理器 val job = GlobalScope.launch(handler) { // 第一个子协程,模拟一个需要很长时间的任务 launch { try { // 使用一个很长的延迟来模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 确保在协程被取消时,执行以下代码 withContext(NonCancellable) { println("Children are cancelled, but exceptions are not handled") delay(100) // 确保输出语句有机会执行 println("The first child finished its non cancellable block") } } } // 第二个子协程,在短暂延迟后抛出一个异常 launch { delay(10) println("Second child throws an exception") throw ArithmeticException() // 抛出一个异常来测试异常处理 } } // 等待父协程完成 job.join() } 异常聚合 当协程的多个子协程因为异常而失败时,一般情况下取第一个异常进行处理。在第一异常之后发生的所有其他异常,都将绑定到第一个异常之上。 @Test fun `test exception aggregation`() = runBlocking<Unit> { // 定义一个 CoroutineExceptionHandler 来处理未捕获的异常 val handler = CoroutineExceptionHandler { _, exception -> // 输出被捕获的主异常和抑制的异常(如果有) println("Caught: $exception, Suppressed: ${exception.suppressed.contentToString()}") } // 使用 GlobalScope 启动一个顶层协程,并应用异常处理器 val job = GlobalScope.launch(handler) { // 第一个子协程 launch { try { // 模拟长时间运行的任务 delay(Long.MAX_VALUE) } finally { // 在取消时抛出 ArithmeticException throw ArithmeticException() } } // 第二个子协程 launch { try { // 进行短暂的延迟 delay(100) } finally { // 在取消时抛出 IOException throw IOException() } } // 第三个子协程 launch { try { // 进行更短暂的延迟 delay(10) } finally { // 在取消时抛出 IndexOutOfBoundsException throw IndexOutOfBoundsException() } } } // 等待父协程完成,确保所有子协程的异常都被处理 job.join() } Flow-异步流 // 定义一个名为 simpleFlow 的挂起函数,返回一个 Flow<Int> suspend fun simpleFlow() = flow<Int> { // 使用 for 循环遍历从 1 到 100 的整数 for (i in 1..100) { delay(1000) // 延迟 1 秒钟,模拟异步任务的延迟 emit(i) // 向下游发射当前整数 } } // 这是一个测试函数,演示了异步、非阻塞式的行为 @Test fun `test multiple values3`() = runBlocking<Unit> { // 启动一个新的协程,不阻塞主线程 launch { // 使用 for 循环打印信息,展示主线程没有被阻塞 for (k in 1..3) { println("I am not blocked $k") delay(1500) // 延迟 1.5 秒钟 } } // 收集 simpleFlow() 发射的值 simpleFlow().collect { value -> println(value) // 打印每个收集到的值 } } 冷流 Flow是一种类似于序列的冷流,flow构建器中的代码直到流被收集的时候才运行 // 测试函数,演示 Flow 是冷流的概念 @Test fun `test flow is cold`() = runBlocking { // 创建一个新的 Flow 实例 val flow = simpleFlow2() // 首次调用 collect,开始收集 Flow 发射的值 println("Calling collect...") flow.collect { value -> println(value) // 打印每个收集到的值 } // 再次调用 collect,重新收集 Flow 发射的值 println("Calling collect again...") flow.collect { value -> println(value) // 打印每个收集到的值 } } 流的连续性 流的每次单独收集都是按顺序执行的,除非使用特殊操作符 从上游到下游每个过渡操作符都会处理每个发射出的值,然后再交给末端操作符。 // 测试函数,演示流的连续操作 @Test fun `test flow continuation`() = runBlocking { // 将 1 到 5 的整数转换成流 (1..5).asFlow() // 过滤流,只保留偶数 .filter { it % 2 == 0 } // 将每个整数转换为字符串 .map { "String $it" } // 收集流中的每个元素 .collect { value -> println("Collect $value") // 打印收集到的每个元素 } } 流构建器 flowOf构建器定义了一个发射固定值集的流。 @Test fun `test flow continuation`() = runBlocking { // 将 1 到 5 的整数转换成流 (1..5).asFlow() // 过滤流,只保留偶数 .filter { it % 2 == 0 } // 将每个整数转换为字符串 .map { "String $it" } // 收集流中的每个元素 .collect { value -> println("Collect $value") // 打印收集到的每个元素 } } 使用.asFlow()扩展函数,可以将各种集合与序列转换为流。 @Test fun `test flow builder2`() = runBlocking<Unit> { // 创建一个包含三个字符串元素的 Flow:"one"、"two" 和 "three" flowOf("one", "two", "three") // 对 Flow 中的每个元素,应用 1000 毫秒(1 秒)的延迟 .onEach { delay(1000) } // 收集 Flow 中的每个元素,并将其打印到控制台 .collect { v -> println(v) } // 将整数范围从 1 到 3 转换为 Flow 并收集每个元素 (1..3).asFlow() .collect { println(it) // 将每个收集到的整数打印到控制台 } } 流的上下文 流的收集总是在调用协程的上下文中发生,流的该属性称为上下文保存 flow{...}构建器中的代码必须遵循上下文保存属性,并且不允许从其他上下文中发射(emit) flowOn操作符,该函数用于更改流发射的上下文。 // 定义一个简单的 Flow,返回 Int 类型的值 fun simpleFlow5() = flow<Int> { // 打印当前线程的名称,表明 Flow 开始执行 println("Flow started ${Thread.currentThread().name}") // 生成从 1 到 3 的整数 for (i in 1..3) { // 延迟 1000 毫秒(1 秒)模拟耗时操作 delay(1000) // 发射(emit)当前整数 emit(i) } // 设置 Flow 在默认的调度器(Dispatcher)上运行 }.flowOn(Dispatchers.Default) // 测试函数,使用 JUnit 测试框架 @Test fun `test flow context3`() = runBlocking<Unit> { // 收集 Flow 中发射的值 simpleFlow5().collect { v -> // 打印当前线程的名称和收集到的值 println("Collected $v ${Thread.currentThread().name}") } } 启动流 使用launchIn替换collect,可以在单独的协程中启动流的收集 ...

2024-08-09 · 11 分钟 · 5211 字 · updated: 2024-08-13 · ShowGuan

Kotlin协程(1)

Kotlin协程(1) 看明白,讲清楚。 协程处理的问题: 处理耗时任务, 这种任务常常会阻塞主线程 保证主线程安全,即确保安全的从主线程调用任何suspend函数 异步任务 协程让异步逻辑同步化,杜绝回调地狱 协程最核心的点就是:函数或一段程序能够被挂起,稍后再在挂起的位置恢复 协程的挂起与恢复 常规函数基础操作包括 : invoke(call) 和 return, 协程新增了 suspend 和 resume ; suspend —— 也称为挂起或暂停,用于暂停执行当前协程,并保存所有局部变量 resume——用于让已暂停的协程从其暂停处继续执行 挂起函数 使用suspend关键字修饰的函数叫做挂起函数 挂起函数只能在协程体内或其他挂起函数内调用 Kotlin协程的两部分 基础设施层次,标准库的协程API, 主要对协程提供了概念和语义上最基本的支持 业务框架层,协程的上层框架支持 调度器 所有协程必须在调度器中运行,即使它们在主线程上运行也是如此。 ...

2024-08-06 · 10 分钟 · 4864 字 · updated: 2024-08-13 · ShowGuan

Kotlin协程(5)

Kotlin协程(5) Binding 视图绑定 View Binding: 通过View Binding, Android 会为每个XML布局文件自动生成一个绑定类。在这个类中,每个View都有一个直接对应的属性,可以避免findViewById的使用,从而减少了手动查找视图和类型转换的工作。提高代码的安全性和可读性。 val binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.textView.text = "Hello World" Data Binding: 功能比View Binding 更为强大,允许在布局文件中直接绑定UI组件到数据源(如ViewModel),这样,数据的变化会自动更新UI <data> <variable name="user" type="com.example.User"/> </data> <TextView android:text="@{user.name}"/> Activity 中: ...

2024-08-06 · 5 分钟 · 2116 字 · updated: 2024-08-13 · ShowGuan

Hugo配置与使用

Hugo配置与使用 下载PaperMod主题: git clone https://github.com/adityatelange/hugo-PaperMod themes/PaperMod --depth=1 生成public内容时清空文件夹 hugo -F --cleanDestinationDir 新建post hugo new content posts/my-first-post.md 完成Git push流程 echo "# showguan.github.io" >> README.md git init git add README.md git commit -m "first commit" git branch -M main git remote add origin https://github.com/showguan/showguan.github.io.git git push -u origin main git remote add origin https://github.com/showguan/showguan.github.io.git git branch -M main git push -u origin main 强制push到仓库 ...

2024-08-03 · 5 分钟 · 2444 字 · updated: 2024-09-01 · ShowGuan

Kotlin基础

Kotlin基础 val & var const 编译时常量只能在函数之外定义,因为编译时常量必须在编译时赋值, 而函数都是在运行时才调用, 函数内的变量也是在运行时赋值,编译时常量要在这些变量赋值前就已经存在。 函数参数 fun main(){ println(doSomething(1, false)) println(fix("Jack")) println(fix(age=10, name="Rose")) } private fun doSomething(age:Int, flag:Boolean):String{ return "result" } fun fix(name:String, age:Int = 2){ println(name + " " + age) } 函数没有返回类型时默认返回Unit ...

2024-08-03 · 12 分钟 · 5514 字 · updated: 2024-08-03 · ShowGuan

训练营第十二天笔记

第十二天笔记 SW 屏幕适配 Handler实现通信。 代码可维护,精简

2024-07-17 · 1 分钟 · 31 字 · updated: 2024-07-17 · ShowGuan

训练营第十一天笔记

第十一天笔记 Kotlin 讲义总结 1. 包的定义与导入 1) 包的声明 包的声明在代码文件的开头,格式为 package 包名。 包名是工程根目录下的子目录,如 com.jetbrains。 在根目录下创建 com.jetbrains 包,并在此包下创建 Demo1.kt 文件,文件第一行声明包名:package com.jetbrains。 2) 包的导入 ...

2024-07-16 · 7 分钟 · 3018 字 · updated: 2024-07-16 · ShowGuan

训练营第十天笔记

第十天笔记 内存泄漏 1. Java垃圾回收机制 1.1 什么是垃圾回收 垃圾回收(GC)是由Java虚拟机(JVM)垃圾回收器提供的一种内存回收机制。 当内存空间或内存占用过高时,系统会回收那些没有引用的对象。 JVM内存模型 运行时数据区域:包括方法区、堆、栈、本地方法栈和程序计数器等。 堆内存: 新生代(Young Generation):分为Eden区和两个Survivor区(S0、S1)。 老年代(Old Generation)。 方法区(MetaSpace):存储类元数据、方法信息等。 1.2 判断对象是否是垃圾的算法 1.2.1 引用计数算法 对象被创建后,系统为其初始化引用计数器。 每当对象被引用时,计数器加1,引用失效时,计数器减1。 当计数器为0时,对象不再被引用,可以进行回收。 优点:判断简单,效率高。 缺点:无法避免循环引用。 1.2.2 可达性分析法 从GC Roots出发,逐步遍历到所有可达的对象。 这些对象称为可达对象,不可达的对象则被标记为垃圾,需要回收。 GC Roots包括的对象: 虚拟机栈中引用的对象(栈帧中的本地变量表)。 方法区中类静态属性引用的对象。 方法区中常量引用的对象。 本地方法栈中JNI(Native方法)引用的对象。 Java中的对象引用 Java中的对象引用主要分为四种类型: ...

2024-07-15 · 3 分钟 · 1411 字 · updated: 2024-07-15 · ShowGuan

训练营第九天笔记

第九天笔记 权限与网络请求课程预习 1. 有哪些权限,具体权限的介绍 普通权限 网络权限:允许设备访问网络 <uses-permission android:name="android.permission.INTERNET" /> 获取网络的状态:如是否有网 <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 读取手机状态:如IMEI等信息 <uses-permission android:name="android.permission.READ_PHONE_STATE" /> 获取WiFi的状态 <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> 敏感权限 写入SD卡权限:文件下载、图片下载 ...

2024-07-14 · 7 分钟 · 3076 字 · updated: 2024-07-14 · ShowGuan

训练营第八天笔记

第八天笔记 自定义控件的实现 View 绘制过程 Activity 和 View 的关系 Window 类:负责在 Activity 中展示内容(具体实现为 PhoneWindow 类) DecorView:Window 创建出根布局 DecorView(继承自 FrameLayout) 单个 View 的绘制步骤 系统根据布局树完成界面绘制,单个 View 需要经过三个步骤: ...

2024-07-13 · 11 分钟 · 5173 字 · updated: 2024-07-13 · ShowGuan

训练营第七天笔记

第七天笔记 Android 动画 帧动画 作用对象 视图控件 (View) 例如 Android 的 TextView、Button 等等 不可作用于 View 组件的属性,如:颜色、背景、长度等等 使用 资源文件方式 代码方式 资源文件方式 基本信息介绍 ...

2024-07-12 · 3 分钟 · 1151 字 · updated: 2024-07-12 · ShowGuan

训练营第六天学习笔记

第六天笔记 组件库笔记 1. 组件库依赖、发布和使用 依赖方式对比 2.x版本 3.x版本 说明 apk runtimeOnly apk 功能同 runtimeOnly。只在生成 apk 时参与打包,编译时不会参与。Gradle 只会将依赖项添加到构建输出,以便在运行时使用。 provided compileOnly provided 功能同 compileOnly。只在编译时有效,不会参与打包至 apk。 compile api compile 功能同 api。该依赖方式会传递所依赖的库,当其他 module 依赖了该 module 时,可以使用该 module 下使用 api 依赖的库。 - implementation 该依赖方式所依赖的库不会传递,只会在当前 module 中生效。 格式介绍 格式 介绍 差异 AAR AAR 文件包含了 Android 库项目的代码、资源文件和清单文件等信息,可以被其他 Android 应用程序引用和使用。 Android 特有的归档文件格式,主要用于打包 Android 库项目。 JAR JAR 文件包含了 Java 类文件、资源文件和清单文件等信息,可以被其他 Java 应用程序引用和使用。 Java 中常用的归档文件格式,用于打包 Java 类库和应用程序。 依赖添加方式 本地依赖 ...

2024-07-11 · 6 分钟 · 2893 字 · updated: 2024-07-11 · ShowGuan

训练营第四天学习笔记

第四天笔记 Android UI 课程重点知识笔记 1. 了解Android控件常用属性 Android:id 任何 View 对象均可拥有与之关联的整型 ID,用于在结构树中对 View 对象进行唯一标识。 系统会以整型形式引用此 ID。 在布局 XML 文件中,系统通常以字符串的形式在 android:id 属性中指定该 ID。 // 使用系统资源 android:id="@android:id/androidView" // 使用自己创建的资源 android:id="@+id/myView" // 标识创建一个新资源 android:id="@id/myView" // 使用现有资源 android:layout_width 和 android:layout_height android:layout_width 和 android:layout_height 分别表示控件的宽度和高度。 取值为 wrap_content、match_parent 和具体的数值(如 40dp)。 wrap_content:表示控件宽度/高度由内容决定。 match_parent:表示控件宽度/高度为父view允许的最大值。 <View android:layout_width="wrap_content" // 设置宽 android:layout_height="wrap_content" // 设置高 /> 2. 了解文本框 TextView TextView 介绍 <?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:gravity="center"> <TextView android:layout_width="wrap_content" // 设置文字 android:layout_height="wrap_content" android:background="@color/teal_200" // 设置背景颜色 android:text="Hello World" // 设置文本内容 android:textSize="20dp" // 设置文字大小 android:textColor="#FAFA02" // 设置文字颜色 android:textStyle="bold"/> // 设置文字样式(normal, bold, italic) </LinearLayout> 3. 了解按钮 Button Button 介绍 Button:在 UI 上生成一个按钮,用户点击时触发 onclick 事件。 ...

2024-07-10 · 4 分钟 · 1802 字 · updated: 2024-07-10 · ShowGuan

训练营第五天学习笔记

第五天笔记 Android 布局与优化 基本布局 公共属性 布局类型 共有属性 layout_gravity: 控件相对于父控件的对齐方式 gravity: 控件内部的对齐方式 padding: 内间距 paddingTop: 上间距 paddingBottom: 下间距 paddingLeft: 左间距 paddingRight: 右间距 layout_width: 控件的宽度 layout_height: 控件的高度 layout_margin: 外间距 layout_marginLeft: 控件的左间距 layout_marginRight: 控件的右间距 layout_marginTop: 控件的上间距 layout_marginBottom: 控件的下间距 示例代码 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="300dp" android:layout_height="300dp" android:layout_weight="1" android:gravity="bottom" android:text="AA" android:textSize="30sp" android:background="@color/colorPrimary" /> <TextView android:layout_width="200dp" android:layout_height="200dp" android:layout_weight="2" android:gravity="bottom" android:text="BB" android:textSize="30sp" android:background="@color/colorAccent" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_weight="3" android:gravity="bottom" android:text="CC" android:textSize="30sp" android:background="@color/colorPrimaryDark" /> </FrameLayout> 基本布局 FrameLayout 特点:FrameLayout 允许子视图堆叠在一起,只显示最后添加的子视图。 示例代码 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="300dp" android:layout_height="300dp" android:layout_weight="1" android:gravity="bottom" android:text="AA" android:textSize="30sp" android:background="@color/colorPrimary" /> <TextView android:layout_width="200dp" android:layout_height="200dp" android:layout_weight="2" android:gravity="bottom" android:text="BB" android:textSize="30sp" android:background="@color/colorAccent" /> <TextView android:layout_width="100dp" android:layout_height="100dp" android:layout_weight="3" android:gravity="bottom" android:text="CC" android:textSize="30sp" android:background="@color/colorPrimaryDark" /> </FrameLayout> 效果:如右侧预览图所示,三个TextView控件堆叠在一起,显示最上层的控件内容。 LinearLayout 特点:LinearLayout 以线性排列子视图,可以设置为水平或垂直布局。 属性介绍 orientation:排列方式 horizontal:水平布局 vertical:垂直布局 layout_weight:权重 使用比例的方式来指定控件的大小 属性关系图 属性 ├─ orientation 排列方式 │ ├─ horizontal 水平布局 │ └─ vertical 垂直布局 └─ layout_weight 权重:使用比例的方式来指定控件的大小 以上为Android布局中的FrameLayout和LinearLayout的基本概念与属性介绍。 ...

2024-07-10 · 4 分钟 · 1940 字 · updated: 2024-07-10 · ShowGuan

训练营第三天学习笔记

第三天笔记 Fragment 重点知识总结 1. fragment背景 Fragment的诞生 引入版本: Android 3.0 (API 11) 背景和目的: 初衷是为了适应大屏幕的平板电脑,由于平板电脑的屏幕比手机屏幕更大,因此可以容纳更多的UI组件,且这些UI组件之间存在交互关系。 ...

2024-07-09 · 3 分钟 · 1097 字 · updated: 2024-07-09 · ShowGuan
« 上一页  下一页  »
© 2026 Kennem's Blog · Powered by Hugo & PaperMod
👤 Visitors: 👀 Views: