How to make JLabel still appear after JTable column swap in the runtime?

拈花ヽ惹草 提交于 2019-12-11 03:55:32

问题


I have a JTable:

after I swap columns unfortunately the flags are not rendered anymore:

I assume it is the fault of my getColumnClass method where I have fixed class for every column, but I don't know how to fix this issue.

package zad1;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Arrays;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;

public class CountryTable extends JTable {




    public CountryTable(String countriesFileName) {
        Vector<String> columnNames = new Vector<String>();
        Vector<Vector<Object>> data = new Vector<Vector<Object>>();
        try {
            BufferedReader br = new BufferedReader(new FileReader(countriesFileName));
            columnNames.addAll(Arrays.asList(br.readLine().split("\t")));
            System.out.println(columnNames);
            String line;
            while ((line = br.readLine()) != null) {
                String[] attributes = line.split("\t");
                Vector<Object> rowData = new Vector<Object>();
                rowData.add(attributes[0]);
                rowData.add(attributes[1]);
                rowData.add(Long.valueOf(attributes[2]));
                BufferedImage icon = ImageIO.read(new File("./data/" + attributes[3]));
                double ratio = (double) icon.getWidth() / icon.getHeight();
                rowData.add(new ImageIcon(icon.getScaledInstance(30, 20, Image.SCALE_FAST)));
                data.add(rowData);
            }
            br.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        setModel(new MyTableModel(data, columnNames));
        setRowHeight(30);
        getColumnModel().getColumn(2).setCellRenderer(new PopulationCellRenderer());
        for (int i = 0; i < getColumnCount(); i++) {
            getColumnModel().getColumn(i).setWidth(400);
        }
    }

    @Override
    public Class getColumnClass(int column) {
        switch (column) {
        case 0:
            return String.class;// Panstwo
        case 1:
            return String.class;// stolica
        case 2:
            return Long.class;// ludnosc
        case 3:
            return Icon.class; // flaga jakiej klasy jest dana komorka w danej kolumnie aby tabela ja poprawnie wyswietlila
        default:
            return String.class;
        }
    }


    class MyTableModel extends DefaultTableModel {
        public MyTableModel(Vector<Vector<Object>> data, Vector<String> columnNames) {
            super(data, columnNames);//wywolanie konstruktora z nadklasy
            System.out.println(columnNames);
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            return column == 3;
        }
    }

    class PopulationCellRenderer extends JLabel implements TableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            Long population = (Long) value;
            setHorizontalAlignment(JLabel.RIGHT);
            if (population > 20000000) {
                setForeground(Color.red);
            } else {
                setForeground(Color.BLACK);
            }
            setText(population + "");
            return this;
        }

    }
}

回答1:


There's no need to extend JTable for this. Your example overrides getColumnClass() in the view, when it should do so in the model. The default renderer for Icon.class will be applied wherever the column is moved.

private final TableModel model = new DefaultTableModel(data, columnNames) {

    @Override
    public Class<?> getColumnClass(int column) {
        switch (column) {
             …
        }
    }
};

This will also make it possible to apply the PopulationCellRenderer on a per-type basis:

table.setDefaultRenderer(Long.class, new PopulationRenderer());

Also consider extending DefaultTableCellRenderer, as shown here, using a NumberFormat, as shown below. Based on an example by @mKorbel, the self-contained code below shows a corrected TableModel and updated TableCellRenderer.

import java.awt.*;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.table.*;

public class TableExample {

    private static final long CUSP = 20000000;
    private JFrame frame = new JFrame("Table Demo");
    private Icon errorIcon = (Icon) UIManager.getIcon("OptionPane.errorIcon");
    private Icon infoIcon = (Icon) UIManager.getIcon("OptionPane.informationIcon");
    private Icon warnIcon = (Icon) UIManager.getIcon("OptionPane.warningIcon");
    private String[] columnNames = {"String", "Long", "Float", "Double", "Boolean", "Icon"};
    private Object[][] data = {
        {"aaa", CUSP - 1, 12.15F, 100.05, true, (errorIcon)},
        {"bbb", CUSP, 7.154F, 6.1555, false, (infoIcon)},
        {"ccc", CUSP + 1, 0.1135F, 3.1455, true, (warnIcon)},
        {"ddd", 42L, 31.15F, 10.05, true, (errorIcon)},
        {"eee", 12345L, 5.154F, 16.1555, false, (infoIcon)},
        {"fff", 54321L, 4.1135F, 31.1455, true, (warnIcon)}};
    private final TableModel model = new DefaultTableModel(data, columnNames) {

        @Override
        public Class<?> getColumnClass(int column) {
            switch (column) {
                case 0:
                    return String.class;
                case 1:
                    return Long.class;
                case 2:
                    return Float.class;
                case 3:
                    return Double.class;
                case 4:
                    return Boolean.class;
                case 5:
                    return Icon.class;
                default:
                    return String.class;
            }
        }
    };
    private final JTable table = new JTable(model);

    private static class PopulationRenderer extends DefaultTableCellRenderer {

        NumberFormat f = NumberFormat.getInstance();

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int col) {
            JLabel r = (JLabel) super.getTableCellRendererComponent(
                table, value, isSelected, hasFocus, row, col);
            if (col == 1) {
                r.setHorizontalAlignment(JLabel.RIGHT);
                Long population = (Long) value;
                if (population > CUSP) {
                    r.setForeground(Color.red);
                } else {
                    r.setForeground(Color.BLACK);
                }
                r.setText(f.format(population));
            }
            return r;
        }
    }

    public TableExample() {
        int h = infoIcon.getIconHeight();
        table.setRowHeight(h);
        table.setPreferredScrollableViewportSize(
            new Dimension(table.getPreferredSize().width, 4 * h));
        table.setAutoCreateRowSorter(true);
        table.setDefaultRenderer(Long.class, new PopulationRenderer());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JScrollPane(table));
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            TableExample tableExample = new TableExample();
        });
    }
}


来源:https://stackoverflow.com/questions/32919867/how-to-make-jlabel-still-appear-after-jtable-column-swap-in-the-runtime

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!