KOReader 插件开发上手:5 分钟写出第一个可用菜单项
【免费下载链接】koreaderAn ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices项目地址: https://gitcode.com/GitHub_Trending/ko/koreader
想加个自动翻页,文档却只给一行签名
想给电子阅读器加个定时自动翻页,翻遍 KOReader 的文档,只找到一行 API 签名——插件到底怎么被加载?菜单项怎么进到系统菜单里?_meta.lua和main.lua各管什么?读完这篇,你能独立写出第一个可用的 KOReader 插件,并避开目录命名、菜单注册、设置持久化这 3 个最常见的坑。
跑通最小插件:两个文件就够
先拉一份源码用来对照着看;插件本体要放进你自己设备上的 KOReader 安装目录,这份源码只读参考,不动它:
git clone https://gitcode.com/GitHub_Trending/ko/koreader- 在设备端 KOReader 目录下创建
plugins/mytool.koplugin/目录,名字必须以.koplugin结尾,加载器按这个规则匹配,写成别的不会加载 - 目录里放一个
_meta.lua,只声明fullname和description两行;再放下面这个main.lua - 重启 KOReader,打开系统菜单
local UIManager = require("ui/uimanager") local InfoMessage = require("ui/widget/infomessage") local WidgetContainer = require("ui/widget/container/widgetcontainer") local MyTool = WidgetContainer:extend{name = "mytool", is_doc_only = false} function MyTool:init() self.ui.menu:registerToMainMenu(self) end function MyTool:addToMainMenu(menu_items) menu_items.my_tool = { text = "MyTool", sorting_hint = "more_tools", callback = function() UIManager:show(InfoMessage:new{text = "Hello, KOReader"}) end, } end return MyTool预期现象:系统菜单的"更多工具"区块里多出一个 MyTool 条目,点一下,屏幕顶部短暂弹出 Hello, KOReader 文字。
菜单注册背后发生了什么
如果你只记住一件事,就是:插件本身没有任何 UI 入口,你在菜单里看到的那一项,是你"注入"给宿主菜单系统的一张表。
启动时 pluginloader 扫描plugins/目录,名字符合.koplugin后缀的目录都算插件,main.lua必须能 require 并返回一个类,这个类要继承 WidgetContainer——让插件能收到生命周期回调的 UI 容器基类。加载完成后宿主会调用插件的init(),你在里面执行self.ui.menu:registerToMainMenu(self),等于声明"我要占一个菜单位"。
真正的组装在后面:菜单系统会回调你的addToMainMenu(menu_items),你把一张表塞进menu_items,text、sorting_hint、callback三个字段分别决定文案、位置和点击后的动作。说白了,是宿主的菜单"借用"了你的回调,不是插件自己弹出一个窗口。
这里有 3 个坑。第一个最隐蔽:init里漏了registerToMainMenu,addToMainMenu就永远不会被调用,菜单凭空消失。第二个:sorting_hint不写,条目会被甩到菜单末尾,写more_tools或navi才能进对应区块。第三个:官方 hello 示例插件 默认处于禁用状态,照着抄要先去掉禁用标记,菜单不出现不是报错。
两个高频场景:记住设置、响应触摸
场景 A:让设置跨重启存活
最高频的日常需求:这次调好的值,下次打开还在。KOReader 向宿主注入了一个全局设置对象G_reader_settings,键值对写在设备端配置文件里,插件只需要读写,不用自己建文件:
-- init() 时读:没配置过就取默认值 30 self.seconds = G_reader_settings:readSetting("mytool_seconds", 30) -- 用户通过 SpinWidget 等组件修改后 G_reader_settings:saveSetting("mytool_seconds", self.seconds)两个坑要注意。G_reader_settings是宿主注入的全局变量,自己写 require 会直接报错;键名要带插件名前缀,否则可能和别的插件撞键,现象很迷惑——你的值看起来被改了,其实是另一个插件在读写。忘 save 也常见:值只留在内存里,重启后回默认,当时还以为是读错了。整段就两行调用,却是一切"记住"功能的地基。
设置存下来了,可时间到了谁去触发翻页?
场景 B:到点自动翻页
需求:每隔 N 秒自动翻一页,用户一碰屏幕就立刻停。思路别用轮询定时器,监听输入事件、随时撤销排程更干净:
function MyTool:init() -- 监听阅读界面的触摸事件 UIManager.event_hook:registerWidget("InputEvent", self) end function MyTool:onInputEvent() self:unschedule() -- 用户一碰屏幕,取消自动翻页 end触摸事件就从上图这些区域产生,这也是为什么取消排程要挂在InputEvent上。注意两点:事件处理函数按on+ 事件名命名,InputEvent对应onInputEvent,写成onInput()永远不会被调用;要翻页就用self.ui:handleEvent(Event:new("GotoViewRel", 1))通知阅读 UI,别直接调阅读器内部函数。仓库里 autoturn 自动翻页插件 就是这个写法,读一遍可以照着抄。
下一步往哪走
想折腾弹窗、输入框这类界面组件,看 ui/widget/;想摸清完整事件链路,看 dispatcher 源码 和 事件定义;想照抄一个完整实战插件,看 autoturn 源码 和 官方开发指南。
菜单项跑出来只是过了门槛,后面能玩出什么花样,就看你的了。
【免费下载链接】koreaderAn ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices项目地址: https://gitcode.com/GitHub_Trending/ko/koreader
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考