You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NatTable自定义Cell Painter实现Choice Chips单元格渲染

在NatTable中自定义Cell Painter渲染Choice Chips

要在DisplayMode.NORMAL模式下渲染Choice Chips,你需要实现一个继承自AbstractCellPainter的自定义绘制器,通过SWT的GC(图形上下文)手动绘制Chip样式。以下是完整的实现步骤和代码示例:

核心实现思路

NatTable的单元格绘制器通过paintCell方法完成内容渲染,核心逻辑分为三步:

  1. 读取单元格绑定的选项数据
  2. 计算Chip的布局位置和尺寸
  3. 用GC绘制Chip的背景、文本及样式

自定义ChoiceChipCellPainter代码

import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
import org.eclipse.nebula.widgets.nattable.painter.cell.AbstractCellPainter;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.Display;

import java.util.List;

public class ChoiceChipCellPainter extends AbstractCellPainter {

    // 可自定义的Chip样式参数
    private static final int CHIP_SPACING = 4;
    private static final int CHIP_PADDING_H = 8;
    private static final int CHIP_PADDING_V = 2;
    private static final int CHIP_BORDER_RADIUS = 12;
    private final Color chipBgColor = Display.getDefault().getSystemColor(SWT.COLOR_LINK_FOREGROUND);
    private final Color chipTextColor = Display.getDefault().getSystemColor(SWT.COLOR_WHITE);

    @Override
    public void paintCell(ILayerCell cell, GC gc, Rectangle bounds, IConfigRegistry configRegistry) {
        // 1. 获取单元格数据:假设数据为List<String>类型的选中选项集合
        @SuppressWarnings("unchecked")
        List<String> selectedChoices = (List<String>) cell.getDataValue();
        if (selectedChoices == null || selectedChoices.isEmpty()) {
            gc.drawText("无选择", bounds.x + 2, bounds.y + 2, true);
            return;
        }

        // 2. 初始化绘制环境(字体、起始坐标)
        gc.setFont(configRegistry.getConfigAttribute(
                CellConfigAttributes.CELL_FONT,
                DisplayMode.NORMAL,
                cell.getConfigLabels()));
        FontMetrics metrics = gc.getFontMetrics();
        int currentX = bounds.x + CHIP_SPACING;
        int currentY = bounds.y + (bounds.height - metrics.getHeight()) / 2;

        // 3. 逐个绘制Choice Chip
        for (String choice : selectedChoices) {
            Point textSize = gc.textExtent(choice);
            int chipWidth = textSize.x + 2 * CHIP_PADDING_H;
            int chipHeight = textSize.y + 2 * CHIP_PADDING_V;

            // 超出单元格宽度时,绘制省略号并停止
            if (currentX + chipWidth > bounds.x + bounds.width - CHIP_SPACING) {
                drawEllipsisChip(gc, currentX, currentY, metrics);
                break;
            }

            // 绘制Chip圆角背景
            drawRoundedChipBg(gc, currentX, currentY - CHIP_PADDING_V, chipWidth, chipHeight);
            // 绘制Chip文本
            gc.setForeground(chipTextColor);
            gc.drawText(choice, currentX + CHIP_PADDING_H, currentY - CHIP_PADDING_V, true);

            // 更新下一个Chip的起始X坐标
            currentX += chipWidth + CHIP_SPACING;
        }
    }

    // 绘制圆角Chip背景
    private void drawRoundedChipBg(GC gc, int x, int y, int width, int height) {
        gc.setBackground(chipBgColor);
        // 先绘制圆角路径并填充
        Path path = new Path(Display.getDefault());
        path.addRoundRectangle(x, y, width, height, CHIP_BORDER_RADIUS, CHIP_BORDER_RADIUS);
        gc.fillPath(path);
        path.dispose();
        // 可选:绘制Chip边框
        gc.setForeground(Display.getDefault().getSystemColor(SWT.COLOR_GRAY));
        gc.drawRoundRectangle(x, y, width, height, CHIP_BORDER_RADIUS, CHIP_BORDER_RADIUS);
    }

    // 绘制省略号Chip
    private void drawEllipsisChip(GC gc, int x, int y, FontMetrics metrics) {
        String ellipsis = "...";
        Point textSize = gc.textExtent(ellipsis);
        int chipWidth = textSize.x + 2 * CHIP_PADDING_H;
        int chipHeight = metrics.getHeight() + 2 * CHIP_PADDING_V;

        drawRoundedChipBg(gc, x, y - CHIP_PADDING_V, chipWidth, chipHeight);
        gc.setForeground(chipTextColor);
        gc.drawText(ellipsis, x + CHIP_PADDING_H, y - CHIP_PADDING_V, true);
    }

    // 重写方法,让NatTable自动计算单元格宽度
    @Override
    public int getPreferredWidth(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {
        List<String> selectedChoices = (List<String>) cell.getDataValue();
        if (selectedChoices == null || selectedChoices.isEmpty()) {
            return gc.textExtent("无选择").x + 4;
        }

        gc.setFont(configRegistry.getConfigAttribute(CellConfigAttributes.CELL_FONT, DisplayMode.NORMAL, cell.getConfigLabels()));
        int totalWidth = CHIP_SPACING;
        for (String choice : selectedChoices) {
            Point textSize = gc.textExtent(choice);
            totalWidth += textSize.x + 2 * CHIP_PADDING_H + CHIP_SPACING;
        }
        return totalWidth;
    }

    // 重写方法,让NatTable自动计算单元格高度
    @Override
    public int getPreferredHeight(ILayerCell cell, GC gc, IConfigRegistry configRegistry) {
        gc.setFont(configRegistry.getConfigAttribute(CellConfigAttributes.CELL_FONT, DisplayMode.NORMAL, cell.getConfigLabels()));
        return gc.getFontMetrics().getHeight() + 2 * CHIP_PADDING_V;
    }
}

注册自定义Painter到NatTable

将自定义绘制器绑定到目标列的NORMAL模式配置中:

// 假设你的列有自定义标签"CHOICE_COLUMN"
configRegistry.registerConfigAttribute(
        CellConfigAttributes.CELL_PAINTER,
        new ChoiceChipCellPainter(),
        DisplayMode.NORMAL,
        "CHOICE_COLUMN");

关键注意事项

  • 数据类型适配:如果你的单元格数据不是List<String>,需要在paintCell中先提取Chip显示文本(比如自定义选项对象的getLabel()方法)。
  • 样式可配置化:可以将颜色、间距等参数通过IConfigRegistry注册,方便统一管理和修改。
  • 交互扩展:如果需要Chip支持点击等交互,需给NatTable添加鼠标监听器,判断点击坐标是否在Chip区域内,再触发相应逻辑。
  • 性能优化:如果单元格数量较多,建议缓存文本尺寸计算结果,避免重复计算。

内容的提问来源于stack exchange,提问作者Lăpădat Răzvan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 01:19:52