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

Nattable单元格访问拓展:自定义BodyDataProvider/IDataProvider示例

Alternative Cell Access Methods in NatTable (Beyond Getter/Setter)

Your current NatTable setup uses DefaultBodyDataProvider, which relies on JavaBean getter/setter methods to access cell data:

final ColumnGroupModel columnGroupModel = new ColumnGroupModel(); ColumnHeaderLayer columnHeaderLayer; String[] propertyNames = { "name", "groupNumber", "examName", "examMark" }; Map<String, String> propertyToLabelMap = new HashMap<String, String>(); propertyToLabelMap.put("name", "Full Name"); propertyToLabelMap.put("groupNumber", "Group"); propertyToLabelMap.put("examName", "Name"); propertyToLabelMap.put("examMark", "Mark"); DefaultBodyDataProvider<Student> bodyDataProvider = new DefaultBodyDataProvider<Student>(students, propertyNames); ColumnGroupBodyLayerStack bodyLayer = new ColumnGroupBodyLayerStack(new DataLayer(bodyDataProvider), columnGroupModel); DefaultColumnHeaderDataProvider defaultColumnHeaderDataProvider = new DefaultColumnHeaderDataProvider( propertyNames, propertyToLabelMap); DefaultColumnHeaderDataLayer columnHeaderDataLayer = new DefaultColumnHeaderDataLayer( defaultColumnHeaderDataProvider); columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer()); ColumnGroupHeaderLayer columnGroupHeaderLayer = new ColumnHeaderLayer(columnHeaderLayer, bodyLayer.getSelectionLayer(), columnGroupModel); columnGroupHeaderLayer.addColumnsIndexesToGroup("Exams", 2, 3); columnGroupHeaderLayer.setGroupUnbreakable(2); final DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyDataProvider); DefaultRowHeaderDataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(rowHeaderDataProvider); ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, bodyLayer, bodyLayer.getSelectionLayer()); final DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider( defaultColumnHeaderDataProvider, rowHeaderDataProvider); DataLayer cornerDataLayer = new DataLayer(cornerDataProvider); ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer, columnGroupHeaderLayer); GridLayer gridLayer = new GridLayer(bodyLayer, columnGroupHeaderLayer, rowHeaderLayer, cornerLayer); NatTable table = new NatTable(shell, gridLayer, true);

Great question! Let's break down your options for accessing cells in NatTable beyond standard getter/setter calls. You absolutely can use custom data providers or accessors to get more flexibility—here are the most practical approaches with code examples:


1. Custom IDataProvider (Full Control)

If you want to skip JavaBean conventions entirely (e.g., access fields directly, compute dynamic values, or work with non-bean data sources), implement the IDataProvider interface directly. This gives you total control over reading and writing cell data.

Example Implementation

import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import java.util.List;

public class StudentDataProvider implements IDataProvider {

    private final List<Student> students;
    private final String[] propertyNames;

    public StudentDataProvider(List<Student> students, String[] propertyNames) {
        this.students = students;
        this.propertyNames = propertyNames;
    }

    @Override
    public Object getDataValue(int columnIndex, int rowIndex) {
        Student student = students.get(rowIndex);
        String property = propertyNames[columnIndex];
        
        // Custom logic for each property (no reflection, no getters required)
        switch (property) {
            case "name":
                return student.name; // Direct field access (adjust if fields are private)
            case "groupNumber":
                return student.getGroupNumber(); // Still use getters if needed
            case "examName":
                return student.getExamName();
            case "examMark":
                // Example: Add a pass/fail label to the mark
                int mark = student.getExamMark();
                return mark + (mark >= 50 ? " (Pass)" : " (Fail)");
            default:
                return "";
        }
    }

    @Override
    public void setDataValue(int columnIndex, int rowIndex, Object newValue) {
        Student student = students.get(rowIndex);
        String property = propertyNames[columnIndex];
        
        // Handle updates with custom logic
        switch (property) {
            case "name":
                student.name = (String) newValue;
                break;
            case "groupNumber":
                student.setGroupNumber((Integer) newValue);
                break;
            case "examMark":
                // Parse the input to remove the pass/fail suffix
                String valueStr = (String) newValue;
                int mark = Integer.parseInt(valueStr.split(" ")[0]);
                student.setExamMark(mark);
                break;
        }
    }

    @Override
    public int getColumnCount() {
        return propertyNames.length;
    }

    @Override
    public int getRowCount() {
        return students.size();
    }
}

How to Use It

Replace your DefaultBodyDataProvider with this custom provider:

StudentDataProvider bodyDataProvider = new StudentDataProvider(students, propertyNames);
ColumnGroupBodyLayerStack bodyLayer = new ColumnGroupBodyLayerStack(new DataLayer(bodyDataProvider), columnGroupModel);

2. ListDataProvider + Custom IColumnAccessor

If you want type-safe, explicit access to your Student objects without reflection (used by DefaultBodyDataProvider), pair ListDataProvider with a custom IColumnAccessor. This is a lightweight alternative that keeps your data access logic clean.

Example IColumnAccessor Implementation

import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;

public class StudentColumnAccessor implements IColumnAccessor<Student> {

    private final String[] propertyNames;

    public StudentColumnAccessor(String[] propertyNames) {
        this.propertyNames = propertyNames;
    }

    @Override
    public Object getDataValue(Student student, int columnIndex) {
        String property = propertyNames[columnIndex];
        switch (property) {
            case "name":
                return student.getName();
            case "groupNumber":
                return student.getGroupNumber();
            case "examName":
                return student.getExamName();
            case "examMark":
                return student.getExamMark();
            default:
                return null;
        }
    }

    @Override
    public void setDataValue(Student student, int columnIndex, Object newValue) {
        String property = propertyNames[columnIndex];
        switch (property) {
            case "name":
                student.setName((String) newValue);
                break;
            case "groupNumber":
                student.setGroupNumber((Integer) newValue);
                break;
            case "examMark":
                student.setExamMark((Integer) newValue);
                break;
        }
    }

    @Override
    public int getColumnCount() {
        return propertyNames.length;
    }
}

How to Use It

Swap out DefaultBodyDataProvider with ListDataProvider:

ListDataProvider<Student> bodyDataProvider = new ListDataProvider<>(students, new StudentColumnAccessor(propertyNames));
ColumnGroupBodyLayerStack bodyLayer = new ColumnGroupBodyLayerStack(new DataLayer(bodyDataProvider), columnGroupModel);

3. Custom BeanPropertyAccessor (Keep JavaBeans, Add Logic)

If you want to stick with JavaBean conventions but need special handling for specific properties (like data conversion or non-standard getter/setter names), extend BeanPropertyAccessor.

Example Implementation

import org.eclipse.nebula.widgets.nattable.data.convert.StringToIntegerConverter;
import org.eclipse.nebula.widgets.nattable.data.reflect.BeanPropertyAccessor;

public class CustomStudentPropertyAccessor extends BeanPropertyAccessor<Student> {

    private final StringToIntegerConverter markConverter = new StringToIntegerConverter();

    public CustomStudentPropertyAccessor(String[] propertyNames) {
        super(propertyNames);
    }

    @Override
    public Object getDataValue(Student student, int columnIndex) {
        String property = getPropertyNames()[columnIndex];
        if ("examMark".equals(property)) {
            // Format the mark as a percentage string
            int mark = (Integer) super.getDataValue(student, columnIndex);
            return String.format("%d%%", mark);
        }
        return super.getDataValue(student, columnIndex);
    }

    @Override
    public void setDataValue(Student student, int columnIndex, Object newValue) {
        String property = getPropertyNames()[columnIndex];
        if ("examMark".equals(property)) {
            // Convert the percentage string back to an integer
            Integer mark = (Integer) markConverter.convertDisplayToCanonicalValue(newValue.toString().replace("%", ""));
            super.setDataValue(student, columnIndex, mark);
        } else {
            super.setDataValue(student, columnIndex, newValue);
        }
    }
}

How to Use It

Attach your custom accessor to the existing DefaultBodyDataProvider:

DefaultBodyDataProvider<Student> bodyDataProvider = new DefaultBodyDataProvider<>(students, propertyNames);
bodyDataProvider.setPropertyAccessor(new CustomStudentPropertyAccessor(propertyNames));
ColumnGroupBodyLayerStack bodyLayer = new ColumnGroupBodyLayerStack(new DataLayer(bodyDataProvider), columnGroupModel);

Which Approach Should You Pick?

  • Use custom IDataProvider for full control (non-bean data, computed values, direct field access).
  • Use ListDataProvider + IColumnAccessor for type-safe, reflection-free access to JavaBeans.
  • Use custom BeanPropertyAccessor if you want to keep JavaBean conventions but add special logic for specific properties.

All these approaches work seamlessly with your existing column group, header, and grid layer setup—just swap out the data provider/accessor as shown.

内容的提问来源于stack exchange,提问作者Andrey Anischik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.27 09:52:50