NC65客开实战:按钮配置的七大隐形陷阱与深度调试技巧

在NC65的二次开发中,按钮配置看似简单却暗藏玄机。许多开发者按照教程一步步操作后,却发现按钮要么神秘消失,要么点击无反应。本文将带你深入NC65按钮渲染的核心机制,揭示那些官方文档从未提及的配置陷阱。

1. XML配置的三大死亡陷阱

1.1 actionContainer与actionType的致命组合

<bean class="nc.ui.pubapp.plugin.action.InsertActionInfo">
    <property name="actionContainer" ref="actionsOfCard" />
    <property name="actionType" value="notedit" />
</bean>

这个看似简单的配置片段中隐藏着两个关键参数:

  • actionContainer :决定按钮出现在卡片(actionsOfCard)还是列表(actionsOfList)视图
  • actionType :控制按钮在编辑(edit)或非编辑(notedit)状态下的显示

注意:当actionType设为"edit"时,必须确保单据处于可编辑状态,否则按钮不会渲染。这是最常见的配置失误之一。

1.2 bean引用路径的幽灵错误

<bean id="NonProdShipAction" class="nc.ui.so.m30.billui.action.NonProdShipAction">
    <property name="model" ref="manageAppModel" />
    <property name="editor" ref="billFormEditor" />
</bean>

常见错误对照表:

错误类型 正确写法 错误表现
类路径错误 nc.ui.so.m30.billui.action.NonProdShipAction 按钮完全不显示
ref引用错误 ref="manageAppModel" 按钮显示但点击报空指针
属性缺失 必须包含model和editor 按钮功能异常

1.3 位置参数的隐形规则

<property name="pos" value="before" />
<property name="target" ref="printActionGroup" />

pos参数有严格限制:

  • 只能使用"before"或"after",大小写敏感
  • target引用的必须是已存在的actionGroup
  • 组名必须与BeanConfigFilePath中的定义完全一致

2. Java代码的四大暗礁

2.1 类继承的强制要求

public class NonProdShipAction extends NCAction {
    // 必须继承NCAction
}

忘记继承NCAction是导致按钮点击无效的元凶之一。 此外还需注意:

  • 必须实现doAction方法
  • 建议重写isActionEnable控制按钮状态
  • 保持无参构造函数

2.2 路径一致性的致命细节

Java类全路径必须与XML中的class属性 完全一致 ,包括:

  • 包名大小写
  • 类名拼写
  • 所在模块路径

提示:使用Eclipse的"Copy Qualified Name"功能可避免手动输入错误。

2.3 属性注入的隐藏要求

private BillForm editor;
private AbstractAppModel model;

// 必须提供setter方法
public void setEditor(BillForm editor) {
    this.editor = editor;
}

Spring注入的三大铁律:

  1. 属性必须有对应的setter方法
  2. setter方法命名必须符合JavaBean规范
  3. 基本类型属性需要默认值

2.4 事件处理的常见陷阱

@Override
public void doAction(ActionEvent event) {
    // 错误示例:直接修改UI不检查线程
    editor.setVisible(false); 
    
    // 正确做法
    SwingUtilities.invokeLater(() -> {
        MessageDialog.showHintDlg(editor, "提示", "操作成功");
    });
}

3. 调试技巧:当按钮消失时该怎么办

3.1 日志分析的黄金法则

在log4j.properties中添加:

log4j.logger.nc.ui.pubapp.plugin.action=DEBUG
log4j.logger.nc.ui.uif2.actions=DEBUG

关键日志信息解读:

  • "Registering action..." → 按钮已加载
  • "Skipping action due to..." → 条件不满足
  • "Cannot find reference..." → 引用错误

3.2 运行时诊断的三把利剑

  1. Bean检查工具
UIF2BeanFactory factory = BillUIUtil.getBeanFactory(editor);
Object bean = factory.getBean("NonProdShipAction");
  1. 属性追踪技巧
System.out.println(editor.getModel().getUiState());
  1. 事件监听诊断
Arrays.stream(editor.getActionListeners())
      .forEach(l -> System.out.println(l.getClass()));

4. 高级配置:动态按钮控制

4.1 条件显示的高级技巧

@Override
protected boolean isActionEnable() {
    // 根据业务状态动态控制
    return model.getUiState() == UIState.NOT_EDIT 
        && hasPermission("BTN_PERMISSION");
}

4.2 动态注入的实战方案

public void registerDynamicAction() {
    InsertActionInfo info = new InsertActionInfo();
    info.setActionContainer("actionsOfCard");
    // ...其他配置
    
    BillUIUtil.getBeanFactory(editor)
             .registerSingleton("dynamicAction", info);
}

4.3 按钮组扩展的黑科技

<!-- 在标准按钮组旁添加自定义组 -->
<bean id="customActionGroup" class="javax.swing.ActionGroup">
    <property name="actions">
        <list>
            <ref bean="action1" />
            <ref bean="action2" />
        </list>
    </property>
</bean>

5. 性能优化:按钮加载的隐藏成本

5.1 懒加载的最佳实践

@Lazy
@Bean
public NonProdShipAction shipAction() {
    return new NonProdShipAction();
}

5.2 资源清理的注意事项

@Override
public void dispose() {
    model.removeAppEventListener(this);
    super.dispose();
}

6. 跨版本兼容方案

6.1 NC65与NCCloud的差异处理

String version = System.getProperty("nc.version");
if (version.startsWith("6.5")) {
    // NC65特有逻辑
} else {
    // NCCloud适配代码
}

6.2 补丁影响的应对策略

创建版本兼容层:

public interface ActionCompat {
    void registerAction(InsertActionInfo info);
}

public class NC65ActionImpl implements ActionCompat {
    // NC65具体实现
}

7. 实战案例:运单按钮完整实现

7.1 完整XML配置

<beans>
    <!-- 主按钮定义 -->
    <bean id="NonProdShipAction" 
          class="nc.ui.so.m30.billui.action.NonProdShipAction">
        <property name="code" value="SHIP_ACTION" />
    </bean>
    
    <!-- 卡片视图注册 -->
    <bean class="nc.ui.pubapp.plugin.action.InsertActionInfo">
        <property name="pos" value="after" />
        <property name="target" ref="exportActionGroup" />
    </bean>
</beans>

7.2 完整Java实现

public class NonProdShipAction extends NCAction {
    @Override
    public void doAction(ActionEvent e) {
        new ShipInfoDialog(editor).show();
    }
    
    private class ShipInfoDialog extends JDialog {
        // 自定义对话框实现
    }
}

在项目压力测试阶段,我们发现当单据包含超过20个自定义按钮时,界面加载时间会显著增加。通过分析,最终定位到是多个按钮重复初始化相同的资源导致的。解决方案是为共享资源创建静态缓存:

private static final Map<String, ImageIcon> ICON_CACHE = 
    Collections.synchronizedMap(new HashMap<>());

private ImageIcon loadIcon(String path) {
    return ICON_CACHE.computeIfAbsent(path, p -> 
        new ImageIcon(getClass().getResource(p)));
}

更多推荐