# 插件UI(PluginUI)

PluginUI 是插件UI功能的核心接口,提供创建UI组件、显示对话框、管理样式、获取主题颜色等功能。通过 PluginUI 可以轻松构建插件界面,所有UI组件都通过链式调用的 Builder 模式创建。

# 快速开始

// 创建一个简单的界面
PluginView view = pluginUI.buildVerticalLayout()
    .addTextView().text("Hello World")
    .addButton().text("确定").onClick(v -> {
        pluginUI.showToast("按钮被点击了");
    })
    .build();

// 在对话框中显示
pluginUI.buildDialog().setView(view).show();

# 接口概览

方法 说明
getContext() 获取插件上下文对象
showToast() 显示 Toast 消息(短)
showToastL() 显示 Toast 消息(长)
cancelToast() 取消 Toast 消息
disableStrictIdMode() 禁用ID严格模式
setStrictIdModeEnabled() 设置是否启用ID严格模式
isStrictIdModeEnabled() 获取是否启用ID严格模式
defaultStyle() 设置默认样式
getStyle() 获取当前样式
buildHorizontalLayout() 创建水平布局构建器
buildVerticalLayout() 创建垂直布局构建器
buildFrameLayout() 创建帧布局构建器
buildDialog() 创建对话框构建器
createPopupMenu() 创建弹出菜单
showMessage() 显示消息对话框
showErrorMessage() 显示错误消息对话框
showPreference() 跳转设置界面
isDarkTheme() 判断是否为深色主题
colorPrimary() 获取主要颜色
colorAccent() 获取强调色
colorDivider() 获取分割线颜色
colorError() 获取错误颜色
colorWarning() 获取警告颜色
colorText() 获取主要文本颜色
colorTextSecondary() 获取次要文本颜色
colorTextStateList() 获取带状态的主要文本颜色
colorTextSecondaryStateList() 获取带状态的次要文本颜色
dialogPaddingHorizontal() 获取对话框水平内边距推荐值
dialogPaddingVertical() 获取对话框垂直内边距推荐值
selectableItemBackground() 获取可选择项目背景
selectableItemBackgroundBorderless() 获取无边框可选择项目背景
dp2px() 将dp值转换为像素值
sp2px() 将sp值转换为像素值

# 获取插件上下文

# getContext

PluginContext getContext()

获取当前 PluginUI 绑定的插件上下文对象。

返回值: PluginContext 插件运行时上下文

# Toast 消息

所有 Toast 方法都可以在非 UI 线程调用。

# showToast

void showToast(CharSequence msg)
void showToast(CharSequence msg, Object... formatArgs)

显示短时间的 Toast 提示消息,等价于 getContext().showToast(msg)

参数:

  • msg - 要显示的消息内容,支持 {key} 格式的本地化文本
  • formatArgs - 格式化参数

# showToastL

void showToastL(CharSequence msg)
void showToastL(CharSequence msg, Object... formatArgs)

显示长时间的 Toast 提示消息,等价于 getContext().showToastL(msg)

参数:

  • msg - 要显示的消息内容,支持 {key} 格式的本地化文本
  • formatArgs - 格式化参数

# cancelToast

void cancelToast()

取消当前正在显示的 Toast 提示消息。

# ID严格模式

# isStrictIdModeEnabled

boolean isStrictIdModeEnabled()

获取当前是否启用ID严格模式(默认启用)。

在ID严格模式下,同一个根布局构建器实例下的所有 PluginView 的 id 不能重复,如果出现重复将在配置 id 时抛出异常。

返回值: 是否启用ID严格模式

# setStrictIdModeEnabled

PluginUI setStrictIdModeEnabled(boolean enabled)

设置是否启用ID严格模式。

参数:

  • enabled - 是否启用ID严格模式

返回值: 当前 PluginUI 实例,支持链式调用

# disableStrictIdMode

PluginUI disableStrictIdMode()

禁用ID严格模式,等价于 setStrictIdModeEnabled(false)

返回值: 当前 PluginUI 实例,支持链式调用

# 示例

// 启用ID严格模式(默认)
pluginUI.buildVerticalLayout()
    .addTextView("text").text("Text 1")
    .addTextView("text").text("Text 2")  // 抛出异常:id重复
    .build();

// 禁用ID严格模式
pluginUI.disableStrictIdMode()
    .buildVerticalLayout()
    .addTextView("text").text("Text 1")
    .addTextView("text").text("Text 2")  // 不会抛出异常
    .build();

# 默认样式

# getStyle

Style getStyle()

获取当前使用的样式。

返回值: 非空的 Style 实例

# defaultStyle

PluginUI defaultStyle(Style style)

设置默认样式,该样式将应用到所有通过此 PluginUI 创建的组件。

参数:

  • style - 要应用的样式,不能为 null

返回值: 当前 PluginUI 实例,支持链式调用

# 示例

// 基于当前样式进行部分修改(推荐)
pluginUI.defaultStyle(new PluginUI.StyleWrapper() {
    @Override
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder) {
        // 先应用当前样式
        super.handleTextView(pluginUI, builder);
        // 然后进行部分修改
        builder.textSize(14);
    }
});

// 使用无参构造创建的 StyleWrapper 会自动继承当前样式

更多介绍请查看样式系统章节

# 创建布局构建器

# buildHorizontalLayout

PluginRootLayoutBuilder buildHorizontalLayout()

创建以水平线性布局为底的根布局构建器。

返回值: PluginRootLayoutBuilder 根布局构建器实例

# buildVerticalLayout

PluginRootLayoutBuilder buildVerticalLayout()

创建以垂直线性布局为底的根布局构建器。

返回值: PluginRootLayoutBuilder 根布局构建器实例

# buildFrameLayout

PluginRootLayoutBuilder buildFrameLayout()

创建以帧布局为底的根布局构建器。

返回值: PluginRootLayoutBuilder 根布局构建器实例

# 示例

// 垂直布局
PluginView verticalView = pluginUI.buildVerticalLayout()
    .addTextView().text("第一行")
    .addTextView().text("第二行")
    .build();

// 水平布局
PluginView horizontalView = pluginUI.buildHorizontalLayout()
    .addTextView().text("第一列")
    .addTextView().text("第二列")
    .build();

// 帧布局 - 子View会叠加显示
PluginView frameView = pluginUI.buildFrameLayout()
    .addTextView().text("底层").textSize(30)
    .addTextView().text("顶层").layoutGravity(Gravity.CENTER)
    .build();

# 对话框和菜单

# buildDialog

PluginDialog.Builder buildDialog()

创建对话框构建器。

返回值: PluginDialog.Builder 对话框构建器实例

更多介绍请查看对话框章节

# createPopupMenu

PluginPopupMenu createPopupMenu(PluginView anchor)

创建弹出菜单。

参数:

  • anchor - 锚点视图,弹出菜单将相对于此视图显示

返回值: PluginPopupMenu 弹出菜单实例

# showMessage

void showMessage(CharSequence title, CharSequence message)

弹出一个自定义标题和内容的对话框,并包含一个关闭按钮。

如果 title 或 message 类型为 String 且为 {key} 格式,将尝试转化为本地化文本。

参数:

  • title - 对话框标题内容,可选用内置语言包词条:{tip}{notice}{information}{warning}{error}
  • message - 对话框消息内容

# showErrorMessage

void showErrorMessage(Throwable e)

弹出一个对话框显示错误信息,同时支持查看错误详情功能,传入的异常还会被记录到插件日志中。

参数:

  • e - 捕获到的异常

# showPreference

void showPreference(Class<? extends PluginPreference> clazz)

跳转并显示设置界面。

参数:

  • clazz - 设置界面类,需实现 PluginPreference 接口,为 null 则表示主设置界面类

# 示例

// 显示消息对话框
pluginUI.showMessage("{tip}", "这是提示内容");

// 显示错误消息
try {
    // 可能出错的代码
} catch (Exception e) {
    pluginUI.showErrorMessage(e);
}

// 打开主设置界面
pluginUI.showPreference(null);

// 打开指定设置界面
pluginUI.showPreference(AdvancedSettings.class);

# 主题和颜色

# isDarkTheme

boolean isDarkTheme()

判断当前是否为深色主题(夜间模式)。

返回值: true 表示深色主题,false 表示浅色主题

# colorPrimary

int colorPrimary()

获取主要颜色(Primary Color)。

返回值: 主要颜色值(ARGB格式)

# colorAccent

int colorAccent()

获取强调色(Accent Color)。

返回值: 强调色值(ARGB格式)

# colorDivider

int colorDivider()

获取分割线颜色。

返回值: 分割线颜色值(ARGB格式)

# colorError

int colorError()

获取错误颜色。

返回值: 错误颜色值(ARGB格式)

# colorWarning

int colorWarning()

获取警告颜色。

返回值: 警告颜色值(ARGB格式)

# colorText

int colorText()

获取主要文本颜色。

返回值: 主要文本颜色值(ARGB格式)

# colorTextSecondary

int colorTextSecondary()

获取次要文本颜色。相比主要文本颜色,次要文本颜色较浅。

返回值: 次要文本颜色值(ARGB格式)

# colorTextStateList

ColorStateList colorTextStateList()

获取带状态的主要文本颜色。

返回值: 带状态的主要文本颜色

# colorTextSecondaryStateList

ColorStateList colorTextSecondaryStateList()

获取带状态的次要文本颜色。相比主要文本颜色,次要文本颜色较浅。

返回值: 带状态的次要文本颜色

# 示例

// 使用主题颜色
pluginUI.buildVerticalLayout()
    .addTextView().text("主要颜色").backgroundColor(pluginUI.colorPrimary())
    .addTextView().text("强调色").backgroundColor(pluginUI.colorAccent())
    .addTextView().text("错误颜色").textColor(pluginUI.colorError())
    .addTextView().text("警告颜色").textColor(pluginUI.colorWarning())
    .addView().height(1).widthMatchParent().backgroundColor(pluginUI.colorDivider())
    .build();

// 使用带状态的文本颜色(禁用时会自动变灰)
pluginUI.buildVerticalLayout()
    .addButton().text("启用状态").textColor(pluginUI.colorTextStateList())
    .addButton().text("禁用状态").textColor(pluginUI.colorTextStateList()).enable(false)
    .build();

# 布局相关

# dialogPaddingHorizontal

int dialogPaddingHorizontal()

获取对话框左右内边距推荐值,单位为 px。

返回值: 对话框水平内边距像素值

# dialogPaddingVertical

int dialogPaddingVertical()

获取对话框上下内边距推荐值,单位为 px。如果设置了对话框按钮,则不推荐设置底部内边距。

返回值: 对话框垂直内边距像素值

# selectableItemBackground

Drawable selectableItemBackground()

用于可选择项目背景,例如 PluginTextView 设置点击事件后,按下没有视觉效果,可调用此方法设置其背景。

返回值: 可选择项目背景 Drawable

# selectableItemBackgroundBorderless

Drawable selectableItemBackgroundBorderless()

获取无边框可选择项目背景 Drawable。

返回值: 无边框可选择项目背景 Drawable

# 示例

// 使用推荐的对话框内边距
PluginView view = pluginUI.buildVerticalLayout()
    .paddingHorizontal(pluginUI.dialogPaddingHorizontal())
    .paddingVertical(pluginUI.dialogPaddingVertical())
    .addTextView().text("对话框内容")
    .build();

// 设置可选择背景
pluginUI.buildVerticalLayout()
    .addTextView().text("点我试试")
        .background(pluginUI.selectableItemBackground())
        .onClick(view -> {
            pluginUI.showToast("被点击了");
        })
    .build();

# 单位转换

# dp2px

int dp2px(float dp)

将 dp 值转换为像素值。

参数:

  • dp - 要转换的 dp 值

返回值: 对应的像素值

# sp2px

int sp2px(float sp)

将 sp 值转换为像素值。

参数:

  • sp - 要转换的 sp 值

返回值: 对应的像素值

# 示例

// 使用dp单位设置尺寸
int width = pluginUI.dp2px(100);  // 100dp转为px
int height = pluginUI.dp2px(50);  // 50dp转为px

pluginUI.buildVerticalLayout()
    .addView().size(width, height).backgroundColor(Color.RED)
    .build();

# 样式系统

PluginUI 提供了强大的样式系统,可以通过 defaultStyle() 方法设置默认样式,该样式将应用到所有通过此 PluginUI 创建的组件。

# Style 类

Style 类用于定义组件的默认样式。通过继承此类并重写相应方法,可以自定义各种 UI 组件的默认样式。

# 样式处理方法

public class Style {
    // 处理所有视图的基础样式
    protected boolean handleBaseView(PluginUI pluginUI, PluginBaseViewBuilder<?> builder);

    // 处理文本视图的基础样式
    protected boolean handleBaseTextView(PluginUI pluginUI, PluginBaseTextViewBuilder<?> builder);

    // 处理按钮的基础样式
    protected boolean handleBaseButton(PluginUI pluginUI, PluginBaseButtonBuilder<?> builder);

    // 处理复合按钮的基础样式
    protected boolean handleBaseCompoundButton(PluginUI pluginUI, PluginBaseCompoundButtonBuilder<?> builder);

    // 处理视图组的基础样式
    protected boolean handleBaseViewGroup(PluginUI pluginUI, PluginBaseViewGroupBuilder<?> builder);

    // 处理具体组件的样式
    protected void handleView(PluginUI pluginUI, PluginViewBuilder builder);
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder);
    protected void handleButton(PluginUI pluginUI, PluginButtonBuilder builder);
    protected void handleCheckBox(PluginUI pluginUI, PluginCheckBoxBuilder builder);
    protected void handleSwitchButton(PluginUI pluginUI, PluginSwitchButtonBuilder builder);
    protected void handleRadioButton(PluginUI pluginUI, PluginRadioButtonBuilder builder);
    protected void handleRadioGroup(PluginUI pluginUI, PluginRadioGroupBuilder builder);
    protected void handleSpinner(PluginUI pluginUI, PluginSpinnerBuilder builder);
    protected void handleProgressBar(PluginUI pluginUI, PluginProgressBarBuilder builder);
    protected void handleEditText(PluginUI pluginUI, PluginEditTextBuilder builder);
    protected void handleImageView(PluginUI pluginUI, PluginImageViewBuilder builder);
    protected void handleLinearLayout(PluginUI pluginUI, PluginLinearLayoutBuilder builder);
    protected void handleRootLayout(PluginUI pluginUI, PluginRootLayoutBuilder builder);
}

# 调用时机

样式处理方法会在 builder 创建之后立刻调用。例如 addTextView() 的内部实现大致如下:

PluginTextViewBuilder addTextView() {
    PluginTextViewBuilder builder = generateTextViewBuilder();
    getStyle().dispatch(pluginUI, builder);  // 创建后立刻分发样式
    return builder;
}

这意味着:

  • 样式处理方法在 addTextView() 返回之前就已经执行
  • 用户在 addTextView() 后面链式调用的设置会覆盖样式中的设置
// 假设样式中设置了 textSize(14)
pluginUI.buildVerticalLayout()
    .addTextView().text("Hello").textSize(20)  // textSize(20) 会覆盖样式中的 14
    .build();

handleBaseXxx 方法的返回值决定是否继续分发:

  • 返回 false:继续向下分发到子类型(默认行为)
  • 返回 true:停止分发,不再调用后续的处理方法

组件继承关系图:

PluginBaseViewBuilder
├── PluginViewBuilder (handleView)
├── PluginEditTextBuilder (handleEditText)
├── PluginImageViewBuilder (handleImageView)
├── PluginSpinnerBuilder (handleSpinner)
├── PluginProgressBarBuilder (handleProgressBar)
├── PluginBaseTextViewBuilder
│   ├── PluginTextViewBuilder (handleTextView)
│   └── PluginBaseButtonBuilder
│       ├── PluginButtonBuilder (handleButton)
│       └── PluginBaseCompoundButtonBuilder
│           ├── PluginCheckBoxBuilder (handleCheckBox)
│           ├── PluginSwitchButtonBuilder (handleSwitchButton)
│           └── PluginRadioButtonBuilder (handleRadioButton)
└── PluginBaseViewGroupBuilder
    └── PluginBaseLinearLayoutBuilder
        ├── PluginLinearLayoutBuilder (handleLinearLayout)
        └── PluginRadioGroupBuilder (handleRadioGroup)

提示:如果需要为所有文本类组件(TextView、Button、CheckBox 等)设置统一样式,可以重写 handleBaseTextView() 方法。

# StyleWrapper 类

StyleWrapper 类是基于装饰器模式的样式扩展工具,用于包装现有 Style 实例,可在不修改原有样式的基础上添加或覆盖样式处理逻辑。

提供两种构造方式:

  • StyleWrapper() - 自动继承当前样式(推荐)
  • StyleWrapper(Style baseStyle) - 显式指定基础样式

# DEFAULT_STYLE

PluginUI 提供了默认样式 DEFAULT_STYLE,包含以下设置:

  • 根布局:设置水平内边距为 dialogPaddingHorizontal()
  • 文本视图:设置文本颜色为 colorTextStateList(),文本大小为 16sp
  • 编辑框:设置宽度为 MATCH_PARENT,box 风格的编辑框默认多行模式
  • 线性布局:水平布局默认宽度为 MATCH_PARENT
  • 单选按钮组:水平布局默认宽度为 MATCH_PARENT

# 示例

// 基于当前样式进行部分修改(推荐)
pluginUI.defaultStyle(new PluginUI.StyleWrapper() {
    @Override
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder) {
        // 先应用当前样式
        super.handleTextView(pluginUI, builder);
        // 然后进行部分修改
        builder.textSize(14);
    }
});

// 上面的代码等价于
PluginUI.Style baseStyle = pluginUI.getStyle();
pluginUI.defaultStyle(new PluginUI.StyleWrapper(baseStyle) {
    @Override
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder) {
        super.handleTextView(pluginUI, builder);
        builder.textSize(14);
    }
});

// 设置所有文本视图的默认颜色和大小
pluginUI.defaultStyle(new PluginUI.StyleWrapper() {
    @Override
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder) {
        super.handleTextView(pluginUI, builder);
        builder.textColor(0xFF000000).backgroundColor(0xFFAAAAAA);
    }
});

// 创建视图时会自动应用样式
PluginView view = pluginUI.buildVerticalLayout()
    .addTextView().text("这个文本会使用上面设置的样式")
    .build();

# 完整示例

# 创建简单界面

// 创建一个垂直布局
PluginView view = pluginUI.buildVerticalLayout()
    .addTextView().text("这是一个文本")
    .addButton().text("点击我").onClick(v -> {
        pluginUI.showToast("按钮被点击了");
    })
    .build();

// 在对话框中显示
pluginUI.buildDialog()
    .setTitle("标题")
    .setView(view)
    .show();

# 组合布局

// 垂直布局嵌套水平布局
PluginView view = pluginUI.buildVerticalLayout()
    .addHorizontalLayout().children(subBuilder -> subBuilder
        .addTextView().text("第一列").width(0).layoutWeight(1)
        .addTextView().text("第二列").width(0).layoutWeight(1)
    )
    .addHorizontalLayout().children(subBuilder -> subBuilder
        .addTextView().text("第三列").width(0).layoutWeight(1)
        .addTextView().text("第四列").width(0).layoutWeight(1)
    )
    .showDialog();

# 使用自定义样式

// 设置自定义样式
pluginUI.defaultStyle(new PluginUI.StyleWrapper() {
    @Override
    protected void handleTextView(PluginUI pluginUI, PluginTextViewBuilder builder) {
        super.handleTextView(pluginUI, builder);
        builder.textColor(0xFF000000).backgroundColor(0xFFAAAAAA);
    }
});

// 创建界面
PluginView view = pluginUI.buildVerticalLayout()
    .addTextView().text("第一行")
    .addTextView().text("第二行")
    .addTextView().text("第三行")
    .build();

# 使用主题颜色

// 创建带颜色的界面
PluginView view = pluginUI.buildVerticalLayout()
    .addTextView().text("主要颜色").backgroundColor(pluginUI.colorPrimary())
    .addTextView().text("强调色").backgroundColor(pluginUI.colorAccent())
    .addView().height(1).widthMatchParent().backgroundColor(pluginUI.colorDivider())
    .addTextView().text("错误颜色").textColor(pluginUI.colorError())
    .addTextView().text("警告颜色").textColor(pluginUI.colorWarning())
    .showDialog();

# 相关接口