重拾Android的快乐-Handler

说明 Handler 是Android提供的一个用于处理消息与可运行对象的组件,它有两个功能: 将消息与可运行对象在安排在未来某个时间点运行 在其它线程上操作队列 为什么提供Handler机制 Android在启动后会有一个主线程,它不允许子线程去改变主UI线程的内容,为了防止一些耗时操作导致主UI线程无响应而闪退,是Android提供的一种保护机制。在现实中确实有一些耗时长的需求使用异步线程,比如调用三方接口,加载完后来刷新UI,这种需要就可以用Android提供的 Handler 机制来实现,可以认为 Handler 是一个与Android主进程的通道 常用方法 sendMessage 发送消息 handleMessage 接收消息 obtainMessage 从全局消息pool获取一个Message对象 post 提交可运行对象,后缀带AtTime表示在指定时间提交,后续带Delayed表示提交延迟运行 removeMessages 移除消息队列中等待中的消息 hasMessages 判断消息队列中是否有某个指定消息 实战 实现一个指定时间切换图片的功能,最终效果如下 页面 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/RelativeLayout1" android:gravity="center"> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/imgAnimation" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" /> </RelativeLayout> 代码比较简单,就是一个图片,居中显示 代码逻辑 var imgAnimation: ImageView? = null var imgstart = 0 val imgids = arrayOf( R.drawable.pic1, R.drawable.pic2, R.drawable.pic3, R.drawable.pic4, R.drawable.pic5 ) private fun testHandler() { setContentView(R.layout.handler_sample) imgAnimation = findViewById(R.id.imgAnimation) val handler = ImgAnimationHandler() Timer().schedule( object : TimerTask() { override fun run() { handler.sendEmptyMessage(101) } }, 0, 500 ) } class ImgAnimationHandler : Handler() { override fun handleMessage(msg: Message) { if (msg.what == 101) { imgAnimation?.setImageResource(imgids[imgstart++ % imgids.size]) } } } 消息处理:主要是新建了一个 ImgAnimationHandler ,它覆写了 handleMessage 方法来处理信息,当消息what值=101 时,从 imgids 中顺序选择一个 sourceId 来达到图片切换的效果 发送消息:主线程中创建一个Timer的schedule线程每隔 500ms 通过 ImgAnimationHandler 发送消息 (what=101) 在子线程中调用Handler 在子线程中调用Handler,需要设置指定 Looper 的Handler(在创建 Handler 时,官方不再建议使用默认的构造函数创建 Handler,而是推荐 传入 Looper 实例作为构建参数的构造函数) ...

2025-08-01 · 1 min · 188 words · tomyli

重拾Android的快乐-Service

说明 REF https://developer.android.com/develop/background-work/services

2025-07-29 · 1 min · 3 words · tomyli

重拾Android的快乐-ListView组件

说明 ListView 组件用来以垂直滚动的显示View集合,配置方式如下: <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent" /> ListView 实现动态数据展示 ListView 是一个 AdapterView,它不关心要显示的数据,只负责展示 ListView 显示的数据是交由 ListAdapter 进行处理,ListAdapter 本质是一个 Adapter,它是ListView和展示数据的桥,这样就可以实现内容与数据的分离 BaseAdapter 抽象基类,自定义扩展都会继承这个类 ArrayAdapter 支持泛型操作,实现最简单的展示一行文字 SimpleAdapter 具有良好扩展性的Adapter,支持Map类型的List数据(List<Map>),支持自定义多种效果 重点关注一下 Adapter 的 getView 方法,它提供获取显示数据的View,这个View可以直接创建或者从XML layout中创建 实战 实现一个最简单的列表 最终效果 核心代码 ListView 容器layout <?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" > <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listView" /> </LinearLayout> 文件名为: list_item1 在线性布局下定义一个 ListView , 设置id为 listView MainActivity 代码 private fun testListView1() { setContentView(R.layout.list_item1) val list = listOf("111", "222", "333", "444", "555") val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, list) val listView = findViewById<ListView>(R.id.listView) listView.adapter = adapter } 主要关注创建 ArrayAdapter 的代码,创建时需要的参数: ...

2025-07-25 · 1 min · 115 words · tomyli

重拾Android的快乐-TableLayout布局

说明 TableLayout 是一个用来以行或者列方式来显示子View的视图组。TableLayout不显示行、列或者单元格的边框线。一个表可以留空多个单元格,也可以跨越多列,这个与html类似 TableRow TabRow是 TableLayout 的子View,用来表示一行,此元素下可以定义多个单元格来包含其它元素,单元格又可以嵌套 TableLayout 约束 列的宽度由该列中最宽单元格的行定义 TableLayout 子类不能指定 layout_width 属性,宽度值一直为 MATCH_PARENT layout_height 属性可以由子类进行设置,默认值为 WRAP_CONTENT ;如果子类是 TableRow ,则其高度值一直为 WRAP_CONTENT 常用属性 android:collapseColumns 基于索引0来隐藏列,多个值用英文逗号分隔,例如配置为1,则隐藏第2列 table中的列索引从0开始计数,如果不指定,则自动增加 android:shrinkColumns 基于索引0来收缩列 android:stretchColumns 基于索引0来拉升列 示例 效果图 代码 <?xml version="1.0" encoding="utf-8"?> <TableLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:stretchColumns="1"> <TableRow> <TextView android:layout_column="1" android:text="Open..." android:padding="3dip" /> <TextView android:text="Ctrl-O" android:gravity="right" android:padding="3dip" /> </TableRow> <TableRow> <TextView android:layout_column="1" android:text="Save..." android:padding="3dip" /> <TextView android:text="Ctrl-S" android:gravity="right" android:padding="3dip" /> </TableRow> <TableRow> <TextView android:layout_column="1" android:text="Save As..." android:padding="3dip" /> <TextView android:text="Ctrl-Shift-S" android:gravity="right" android:padding="3dip" /> </TableRow> <View android:layout_height="2dip" android:background="#FF909090" /> <TableRow> <TextView android:text="X" android:padding="3dip" /> <TextView android:text="Import..." android:padding="3dip" /> </TableRow> <TableRow> <TextView android:text="X" android:padding="3dip" /> <TextView android:text="Export..." android:padding="3dip" /> <TextView android:text="Ctrl-E" android:gravity="right" android:padding="3dip" /> </TableRow> <View android:layout_height="2dip" android:background="#FF909090" /> <TableRow> <TextView android:layout_column="1" android:text="Quit" android:padding="3dip" /> </TableRow> </TableLayout> 因为在 TableLayout 中设置了 android:stretchColumns="1" 属性来拉升第一列,可以看到第一列的宽度最宽

2025-07-17 · 1 min · 124 words · tomyli

重拾Android的快乐-RelativeLayout布局

说明 RelativeLayout 是一个提供子View相对位置的View组,可以指定相邻同级节点的位置(上下左右)或相对于父区域的位置。RelativeLayout主要是用来消除嵌套布局来保持布局层级扁平来提升性能。 布局方向 Android中的布局方向有两种: LTR 从左到右 RTL 从右到左 可以通过 android:layoutDirection 进行设置 常用属性配置 对齐相关 android:layout_alignParentTop 此视图顶部边缘与父视图顶部边缘对齐,忽略布局方向,对应的有 android:layout_alignParentBottom android:layout_alignParentLeft 此视图顶部边缘与父视图左侧对齐,只支持LTR,对应的有 android:layout_alignParentRight android:layout_alignParentStart 此视图顶部边缘与父视图左侧对齐,支持LTR与RTL,对应的有 android:layout_alignParentEnd android:layout_alignTop 此视图顶部边缘与给定视图顶部边缘对齐,忽略布局方向,对应的有 android:layout_alignParentBottom android:layout_alignLeft 此视图顶部边缘与给定视图左侧对齐,只支持LTR,对应的有 android:layout_alignRight android:layout_alignStart 此视图顶部边缘与给定视图左侧对齐,支持LTR与RTL,对应的有 android:layout_alignEnd android:layout_alignWithParentIfMissing 如果没有设置 layout_toLeftOf 或者 layout_toRightOf 属性则使用父view做为锚点 居中相关 android:layout_centerHorizontal 子视图在父视图中垂直居中 android:layout_centerVertical 子视图在父视图中水平居中 android:layout_centerInParent 子视图在父视图中水平和垂直居中 android:layout_below 用于控制垂直方向,此视图定位于指定资源id的下方,对应的还有 android:layout_above android:layout_toRightOf 用于左右(LTR)布局并忽略右左布局,此视图的左边缘定位于指定资源的右方,其它的还有 android:layout_toLeftOf android:layout_toEndOf 支持LTR与RTL两种布局,对于LTR表示位于指定资源id的右侧,对于RTL则相反 示例 这里引用一个官方提供的样例 效果图 代码 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="16dp" android:paddingRight="16dp" > <EditText android:id="@+id/name" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="@string/reminder" /> <Spinner android:id="@+id/dates" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_below="@id/name" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/times" /> <Spinner android:id="@id/times" android:layout_width="96dp" android:layout_height="wrap_content" android:layout_below="@id/name" android:layout_alignParentRight="true" /> <Button android:layout_width="96dp" android:layout_height="wrap_content" android:layout_below="@id/times" android:layout_alignParentRight="true" android:text="@string/done" /> </RelativeLayout> 这里主要关注中间两个Spinner和Button ...

2025-07-10 · 1 min · 168 words · tomyli

重拾Android的快乐-LinearLayout布局

说明 LinearLayout 是一个视图组件,它可以将所有子视图按一个方向进行 垂直/水平 对齐 垂直方向(Vertical) 垂直方向会按每行一个元素排列,而不管设置的宽度 水平方向(Horizontal) 水平方向只有一行高,它的高度为最高的子元素+Padding值 常用属性配置 android:orientation 布局方向,可配置值: horizontal 水平 vertical 垂直 android:layout_width 元素宽度 android:layout_height 元素高度,可配置具体的数值,还支持常用的值如下: match_parent 与父元素同尺寸 wrap_content 只包含内容大小(+padding) android:layout_weight 布局权重,用于设置元素占据的空间值,值越大占的空间越大,默认值为0(匹配内容) android:gravity 指定视图内容在其自身边界内的对齐方式 实战案例 实现一个简单的邮件发送功能 <?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:paddingLeft="16dp" android:paddingRight="16dp" android:orientation="vertical" > <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:autofillHints="" android:hint="@string/to" android:inputType="text" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:autofillHints="" android:hint="@string/subject" android:inputType="text" /> <EditText android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:autofillHints="" android:gravity="top" android:hint="@string/message" android:inputType="text" /> <Button android:layout_width="100dp" android:layout_height="wrap_content" android:layout_gravity="end" android:text="@string/send" /> </LinearLayout> 效果图 Jetpack Compose组件实现同样效果 Jetpack Compose 是google新推出并推荐使用的基于kotlin声明式UI组件库,可以用更少的代码来加速开发 ...

2025-07-01 · 1 min · 204 words · tomyli

Mac上使用网易Mumu模拟器踩坑指北

前言 为了给mac找一款合适的android模拟器,先前使用的Nox player,但是老哥这Mac版本好几年都不更新了,使用的VirtualBox也不兼容。 替代者 网易MuMu模拟器 号称非常快,主要是Mac版本有更新。其它的都不行。 配置Charles抓包 以为找到了一个非常好的模拟器,没想到配置Charles抓包时遇到很多问题(我这使用的是Mumu版本是1.9.1) SSL代理配置问题 正常情况下的代码只需要配置ip和端口就可以,但是发现配置保存后,不能进行输入,修改成无代理就又可以了,后来各种找发现还得在配置中不使用代理中要配置10.0.2.2这个ip,好像是模拟器内部要用这个地址。 SSL证书安装问题 配置好代码ip后,要下载Charles的证书并安装,这时要配置模拟器的解锁密码,如果遇到输入正确密码后不能安装的问题,则可以考虑重新配置一下密码,大于6位的吧 多开问题 对于Nox player可以进行真正意义的完全多开(模拟器环境隔离),Mumu只是实现了简单的多开,不能使用不同版本的app,目测只是一个镜像。但是好在能使用了,希望后续开发团队可以支持。 软件窗口问题 如果在Mac使用窗口操作工具对Mumu进行操作,则可能一下子就找不到应用了,只能重启,正确的使用方式是使用Mumu菜单中的窗口下的拼贴功能,使用起来还不错。

2020-02-11 · 1 min · 17 words · tomyli