编写脚本设置页面
1. @set
注解的作用
@set
是一个特殊的注解,用于标识脚本文件定义了一个可作为独立界面的 设置页面。在 LuaHook 运行时环境中,该注解帮助脚本引擎识别并正确加载和处理这些设置页面脚本。
2. 设置页面代码概览
以下是一个典型的 Lua 设置页面脚本结构,它展示了一个包含文本视图和按钮的基础界面:
@set
function setActivity()
-- 导入必要的 Java 类,用于 UI 组件和功能
import "com.kulipai.luahook.util.*"
import "java.lang.*"
import "android.widget.Toast"
import "android.widget.*"
-- 定义一个全局的 print 函数,方便调试输出
function print(...)
local text = table.concat({ ... }, " ")
Toast.makeText(this, tostring(text), Toast.LENGTH_SHORT).show()
end
-- 使用 Lua 表定义界面布局
local layout = {
LinearLayout, -- 根布局为线性布局
gravity = "center", -- 内容居中
id = "filelayout", -- 布局ID
orientation = 1, -- 垂直排列 (1=垂直,0=水平)
layout_width = "fill", -- 宽度填满父视图
layout_height = "fill", -- 高度填满父视图
{
TextView, -- 文本视图
id = "tv", -- 控件ID
textIsSelectable = true, -- 文本可长按复制
gravity = "center", -- 文本居中
layout_width = "fill", -- 宽度填满父视图
text = "测试文本", -- 默认显示文本
},
{
Button, -- 按钮视图
id = "btn", -- 控件ID
layout_marginTop = "16dp", -- 上边距 16dp
layout_marginBottom = "16dp", -- 下边距 16dp
gravity = "center", -- 内容居中
layout_width = "fill", -- 宽度填满父视图
text = "测试按钮", -- 按钮文本
},
}
-- 加载并设置布局到当前 Activity
activity.setContentView(loadlayout(layout))
-- 绑定按钮点击事件
btn.onClick = function()
tv.setText("我爱LuaHook") -- 修改文本内容
end
end
3. 关键点说明
loadlayout
函数: 此函数负责将 Lua 表格定义的布局结构转换为 Android 实际的视图对象,并将其渲染到屏幕上。- 事件绑定: 通过
控件ID.onClick
这种简洁的方式可以直接为 UI 控件绑定事件监听器,例如btn.onClick = function() ... end
。 - 布局属性:
layout_width
/layout_height
: 可以设置为"fill"
(填满父视图) 或"wrap"
(包裹内容)。gravity
: 控制视图内容的对齐方式。orientation
: 仅适用于线性布局 (LinearLayout
),决定子视图的排列方向(垂直或水平)。
- 单位: 在 Android 布局中,推荐使用
dp
(density-independent pixels) 作为尺寸单位,以确保在不同密度的屏幕上显示效果一致。
4. 页面跳转与参数传递
LuaHook 提供了两种主要方式来实现设置页面的跳转:通过封装 Intent
启动 ScriptSetActivity
或 直接使用 injectActivity
方法。无论是哪种方式,最终都是通过 ScriptSetActivity
作为统一的入口来加载和显示 Lua 设置页面。参数传递则通过标准的 Intent.putExtra
机制完成。
4.1 通过封装 Intent
实现跳转
为了简化页面跳转逻辑,建议封装一个辅助函数,集中处理 Intent
的构建和启动。这种方式提供了更明确的控制,尤其是当你需要传递复杂参数或进行其他 Intent
配置时。
local function startScriptActivity(context, scriptName, arg)
import "android.content.ComponentName"
import "android.content.Intent"
local intent = Intent()
-- 明确指定目标 Activity 组件为 LuaHook 的 ScriptSetActivity
intent.setComponent(ComponentName("com.kulipai.luahook",
"com.kulipai.luahook.activity.ScriptSetActivity"))
-- 通过 Intent.putExtra 传递参数。
-- 'arg' 是一个示例键名,你可以根据需要定义不同的键。
intent.putExtra("arg", arg)
-- 构建脚本文件的完整路径,这是 ScriptSetActivity 加载 Lua 脚本的关键
local packageName = context.getPackageName()
intent.putExtra("path", "/data/local/tmp/LuaHook/AppScript/" .. packageName .. "/" .. scriptName .. ".lua")
-- 启动目标 Activity
context.startActivity(intent)
end
4.2 通过 injectActivity
方法实现跳转
injectActivity
方法是 LuaHook 提供的一种更直接的跳转方式,它通常用于在运行时注入并启动新的 Activity。其底层实现原理与封装 Intent
启动 ScriptSetActivity
类似,都是将 Lua 代码或文件路径作为参数传递给 ScriptSetActivity
。
injectActivity
的基本用法如下:
-- injectActivity(当前的activity实例, lua代码字符串/lua文件路径)
-- 目前主要支持 lua代码字符串
injectActivity(currentActivity, "print('Hello from injected Activity')")
重要提示:
- 目前
injectActivity
主要支持直接传入 Lua 代码字符串。这意味着你需要将整个设置页面的 Lua 逻辑作为字符串传递。 - 如果需要传递参数,无论使用
startScriptActivity
还是injectActivity
,都仍然需要通过Intent.putExtra
这种原始方法来传递。 这是因为ScriptSetActivity
统一通过Intent
来接收所有必要的启动信息和参数。
4.3 宿主页面调用示例
以下示例展示了如何在宿主应用的 Lua 脚本中调用 封装的 startScriptActivity
函数 来跳转到设置页面,并传递一个字符串参数。如果你选择直接使用 injectActivity
,逻辑会类似,但需要将整个 @set
定义的 Lua 代码作为字符串传入。
hookcotr(
"com.tencent.mm.pluginsdk.ui.chat.ChatFooter", -- 目标类
loader,
"android.content.Context",
"android.util.AttributeSet",
"int",
function(it) end,
function(it)
local button = getField(it.thisObject, "w") -- 获取宿主应用中的一个按钮实例
local context = invoke(button, "getContext") -- 获取当前上下文
button.onLongClick = function() -- 按钮长按事件
-- 调用封装的函数,跳转到名为 "当前脚本名称" 的设置页面,并传递参数 "你好"
startScriptActivity(context, "当前脚本名称", "你好")
-- 如果使用 injectActivity,大致会是这样(需要将 @set 函数转换为字符串):
-- local luaCode = [[
-- @set
-- function setActivity()
-- -- ... 你的设置页面代码 ...
-- end
-- ]]
-- injectActivity(context, luaCode)
end
end
)
4.4 设置页面接收参数示例
在 @set
定义的设置页面脚本中,无论你是通过 startScriptActivity
还是 injectActivity
跳转过来,都可以通过 this.getIntent().getExtras()
方法来获取传递过来的参数:
@set
function setActivity()
import "com.kulipai.luahook.util.*"
import "java.lang.*"
import "android.widget.Toast"
import "android.widget.*"
function print(...)
local text = table.concat({ ... }, " ")
Toast.makeText(this, tostring(text), Toast.LENGTH_SHORT).show()
end
-- 获取从 Intent 中传递过来的字符串参数 "arg"
local argStr = this.getIntent().getExtras().getString("arg")
local layout = {
LinearLayout,
gravity = "center",
id = "filelayout",
orientation = 1,
layout_width = "fill",
layout_height = "fill",
{
TextView,
id = "tv",
textIsSelectable = true,
gravity = "center",
layout_width = "fill",
text = "测试文本",
},
{
Button,
id = "btn",
layout_marginTop = "16dp",
layout_marginBottom = "16dp",
gravity = "center",
layout_width = "fill",
text = "测试按钮",
},
}
activity.setContentView(loadlayout(layout))
btn.onClick = function()
-- 将接收到的参数设置到文本视图中
tv.setText(argStr)
end
end
5. 未来展望(待实现)
目前,页面跳转和参数传递主要依赖于 ScriptSetActivity
作为统一入口,并通过 Intent.putExtra
来实现。未来计划将支持更灵活的跳转方式和参数传递机制,包括:
- 通过
@set
传递 Lua 函数: 允许直接传递 Lua 函数引用,实现更高级的回调和交互。 - 通过 Lua 文件路径传递: 简化大型 Lua 脚本的组织和加载,避免长字符串。
这些改进将进一步提升 LuaHook 在开发自定义设置页面方面的便利性和强大功能。