在 JList 中自定义渲染 JPanel
Custom Render JPanel in a JList
我正在尝试通过修改渲染在 JList 中显示面板。
我用 JLabel (https://www.codejava.net/java-se/swing/jlist-custom-renderer-example) 尝试了一个示例,它运行良好(见图)
所以我尝试将它改编为 JPanel(而不是 JLabel),但我遇到了一个有趣的问题,我真的不知道如何解决它。
如您所见,不是只出现一次,而是每行显示每个国家及其相关图像,我不明白为什么。 (有8行因为有8个国家)
这是我编写的代码:
CountryRenderer.java
import java.awt.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class CountryRenderer extends JPanel implements ListCellRenderer<Country> {
@Override
public Component getListCellRendererComponent(JList<? extends Country> list, Country country, int index,
boolean isSelected, boolean cellHasFocus) {
String code = country.getCode();
// to load and resize the image
Image imgSettings = null;
try {
imgSettings = ImageIO.read(getClass().getResource("./images/" + code + "1.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
imgSettings = imgSettings.getScaledInstance(25, 25, imgSettings.SCALE_SMOOTH);
// create the button and put the image on it
JButton buttontest = new JButton() ;
buttontest.setIcon(new ImageIcon(imgSettings));
add(buttontest);
// create the text (name of the country)
JTextField txtest = new JTextField();
txtest.setText(country.getName());
add(txtest);
return this;
}
}
如果您需要其他 2 个文件来制作这个 运行,它们在我放在上面或这里的 link 上:
Country.java
public class Country {
private String name;
private String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return name;
}
}
这是您可以 运行 的文件:
JListCustomRendererExample.java
import javax.swing.*;
public class JListCustomRendererExample extends JFrame {
public JListCustomRendererExample() {
Country us = new Country("USA", "1");
Country in = new Country("India", "2");
Country vn = new Country("Vietnam", "3");
Country ca = new Country("Canada", "4");
Country de = new Country("Denmark", "5");
Country fr = new Country("France", "6");
Country gb = new Country("Great Britain", "7");
Country jp = new Country("Japan", "8");
//create the model and add elements
DefaultListModel<Country> listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
//create the list
JList<Country> countryList = new JList<>(listModel);
add(new JScrollPane(countryList));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("JList Renderer Example");
this.setSize(200, 200);
this.setLocationRelativeTo(null);
this.setVisible(true);
countryList.setCellRenderer(new CountryRenderer());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JListCustomRendererExample();
}
});
}
}
编辑
我尝试了@Gilbert le blanc 的回答,但我仍然有问题,我尝试向这个标签添加新元素,比如一个按钮,我希望这些按钮上有与名称相同的文本它所在的国家/地区行。
示例:在美国行,我希望按钮上有“美国”。
所以我在 public Component getListCellRendererComponent
中添加了这 4 行:
label.setLayout(null);
JButton test = new JButton(country.getName());
test.setBounds(10,10,50,50);
label.add(test);
我明白了:
这不是预期的结果,这与我在第一个问题中遇到的问题有点相同,您知道为什么吗?有解决办法吗?
Oracle 有一个有用的教程,Creating a GUI With Swing。跳过 Netbeans 部分。
事实证明,您不能使用 JPanel
来保持 JList
选择。你必须使用 JLabel
.
因此,我设计了一个略有不同的 GUI。我从您使用的教程中复制了图标。这些图标位于源代码 zip 文件中。
我做的第一件不同的事情是将图像添加到 Country
class。我还将所有 class 字段设为最终字段,因为它们不会更改。
public class Country {
private final Image image;
private final String name;
private final String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
this.image = getImage(code);
}
private Image getImage(String code) {
Image image = null;
try {
URL url = getClass().getResource("/images/" + code + ".png");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Image getImage() {
return image;
}
@Override
public String toString() {
return name;
}
}
接下来,我创建了一个 CountryModel
class 来保存 ListModel
。
public class CountryModel {
private final DefaultListModel<Country> listModel;
public CountryModel() {
Country us = new Country("USA", "us");
Country in = new Country("India", "in");
Country vn = new Country("Vietnam", "vn");
Country ca = new Country("Canada", "ca");
Country de = new Country("Denmark", "de");
Country fr = new Country("France", "fr");
Country gb = new Country("Great Britain", "gb");
Country jp = new Country("Japan", "jp");
// create the model and add elements
listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
}
public DefaultListModel<Country> getListModel() {
return listModel;
}
}
这简化了 JListCustomRendererExample
视图 class。我创建国家模型,创建 JFrame
,创建 JList
JPanel
和 JButton
JPanel
.
为 JFrame
和 JPanels
创建单独的方法有助于将相似的代码放在一起,将不同的代码分离到方法中。这使得代码更易于阅读和修改。
JButton
JPanel
允许您创建一个 ActionListener
来处理 JList
个选择。
public class JListCustomRendererExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JListCustomRendererExample());
}
private final CountryModel model;
public JListCustomRendererExample() {
this.model = new CountryModel();
}
@Override
public void run() {
JFrame frame = new JFrame("JList Renderer Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(model), BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CountryModel model) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JList<Country> countryList = new JList<>(model.getListModel());
countryList.setCellRenderer(new CountryRenderer());
panel.add(new JScrollPane(countryList), BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Select Countries");
panel.add(button, BorderLayout.CENTER);
return panel;
}
}
最后,我们有 ListCellRenderer
class。
public class CountryRenderer implements ListCellRenderer<Country> {
private final JLabel label;
public CountryRenderer() {
this.label = new JLabel();
label.setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<? extends Country> list,
Country country, int index, boolean isSelected, boolean cellHasFocus) {
label.setIcon(new ImageIcon(country.getImage()));
label.setText(country.getName());
if (isSelected) {
label.setBackground(list.getSelectionBackground());
label.setForeground(list.getSelectionForeground());
} else {
label.setBackground(list.getBackground());
label.setForeground(list.getForeground());
}
return label;
}
}
这是完整的可运行代码。我在 classes 中添加了额外的 classes,这样我就可以 post 将代码作为一个块。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class JListCustomRendererExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JListCustomRendererExample());
}
private final CountryModel model;
public JListCustomRendererExample() {
this.model = new CountryModel();
}
@Override
public void run() {
JFrame frame = new JFrame("JList Renderer Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(model), BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CountryModel model) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JList<Country> countryList = new JList<>(model.getListModel());
countryList.setCellRenderer(new CountryRenderer());
panel.add(new JScrollPane(countryList), BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Select Countries");
panel.add(button, BorderLayout.CENTER);
return panel;
}
public class CountryRenderer implements ListCellRenderer<Country> {
private final JLabel label;
public CountryRenderer() {
this.label = new JLabel();
label.setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<? extends Country> list,
Country country, int index, boolean isSelected, boolean cellHasFocus) {
label.setIcon(new ImageIcon(country.getImage()));
label.setText(country.getName());
if (isSelected) {
label.setBackground(list.getSelectionBackground());
label.setForeground(list.getSelectionForeground());
} else {
label.setBackground(list.getBackground());
label.setForeground(list.getForeground());
}
return label;
}
}
public class CountryModel {
private final DefaultListModel<Country> listModel;
public CountryModel() {
Country us = new Country("USA", "us");
Country in = new Country("India", "in");
Country vn = new Country("Vietnam", "vn");
Country ca = new Country("Canada", "ca");
Country de = new Country("Denmark", "de");
Country fr = new Country("France", "fr");
Country gb = new Country("Great Britain", "gb");
Country jp = new Country("Japan", "jp");
// create the model and add elements
listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
}
public DefaultListModel<Country> getListModel() {
return listModel;
}
}
public class Country {
private final Image image;
private final String name;
private final String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
this.image = getImage(code);
}
private Image getImage(String code) {
Image image = null;
try {
URL url = getClass().getResource("/images/" + code + ".png");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Image getImage() {
return image;
}
@Override
public String toString() {
return name;
}
}
}
好吧,我设法通过创建一个带有侦听器的 JPanel 来解决自己的问题,因此它就像一个 JList,但我可以将我想要的对象放入其中,这样它甚至可以更好地工作。
我正在尝试通过修改渲染在 JList 中显示面板。
我用 JLabel (https://www.codejava.net/java-se/swing/jlist-custom-renderer-example) 尝试了一个示例,它运行良好(见图)
所以我尝试将它改编为 JPanel(而不是 JLabel),但我遇到了一个有趣的问题,我真的不知道如何解决它。
如您所见,不是只出现一次,而是每行显示每个国家及其相关图像,我不明白为什么。 (有8行因为有8个国家)
这是我编写的代码:
CountryRenderer.java
import java.awt.*;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class CountryRenderer extends JPanel implements ListCellRenderer<Country> {
@Override
public Component getListCellRendererComponent(JList<? extends Country> list, Country country, int index,
boolean isSelected, boolean cellHasFocus) {
String code = country.getCode();
// to load and resize the image
Image imgSettings = null;
try {
imgSettings = ImageIO.read(getClass().getResource("./images/" + code + "1.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
imgSettings = imgSettings.getScaledInstance(25, 25, imgSettings.SCALE_SMOOTH);
// create the button and put the image on it
JButton buttontest = new JButton() ;
buttontest.setIcon(new ImageIcon(imgSettings));
add(buttontest);
// create the text (name of the country)
JTextField txtest = new JTextField();
txtest.setText(country.getName());
add(txtest);
return this;
}
}
如果您需要其他 2 个文件来制作这个 运行,它们在我放在上面或这里的 link 上:
Country.java
public class Country {
private String name;
private String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toString() {
return name;
}
}
这是您可以 运行 的文件: JListCustomRendererExample.java
import javax.swing.*;
public class JListCustomRendererExample extends JFrame {
public JListCustomRendererExample() {
Country us = new Country("USA", "1");
Country in = new Country("India", "2");
Country vn = new Country("Vietnam", "3");
Country ca = new Country("Canada", "4");
Country de = new Country("Denmark", "5");
Country fr = new Country("France", "6");
Country gb = new Country("Great Britain", "7");
Country jp = new Country("Japan", "8");
//create the model and add elements
DefaultListModel<Country> listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
//create the list
JList<Country> countryList = new JList<>(listModel);
add(new JScrollPane(countryList));
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("JList Renderer Example");
this.setSize(200, 200);
this.setLocationRelativeTo(null);
this.setVisible(true);
countryList.setCellRenderer(new CountryRenderer());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new JListCustomRendererExample();
}
});
}
}
编辑
我尝试了@Gilbert le blanc 的回答,但我仍然有问题,我尝试向这个标签添加新元素,比如一个按钮,我希望这些按钮上有与名称相同的文本它所在的国家/地区行。 示例:在美国行,我希望按钮上有“美国”。
所以我在 public Component getListCellRendererComponent
中添加了这 4 行:
label.setLayout(null);
JButton test = new JButton(country.getName());
test.setBounds(10,10,50,50);
label.add(test);
我明白了:
这不是预期的结果,这与我在第一个问题中遇到的问题有点相同,您知道为什么吗?有解决办法吗?
Oracle 有一个有用的教程,Creating a GUI With Swing。跳过 Netbeans 部分。
事实证明,您不能使用 JPanel
来保持 JList
选择。你必须使用 JLabel
.
因此,我设计了一个略有不同的 GUI。我从您使用的教程中复制了图标。这些图标位于源代码 zip 文件中。
我做的第一件不同的事情是将图像添加到 Country
class。我还将所有 class 字段设为最终字段,因为它们不会更改。
public class Country {
private final Image image;
private final String name;
private final String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
this.image = getImage(code);
}
private Image getImage(String code) {
Image image = null;
try {
URL url = getClass().getResource("/images/" + code + ".png");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Image getImage() {
return image;
}
@Override
public String toString() {
return name;
}
}
接下来,我创建了一个 CountryModel
class 来保存 ListModel
。
public class CountryModel {
private final DefaultListModel<Country> listModel;
public CountryModel() {
Country us = new Country("USA", "us");
Country in = new Country("India", "in");
Country vn = new Country("Vietnam", "vn");
Country ca = new Country("Canada", "ca");
Country de = new Country("Denmark", "de");
Country fr = new Country("France", "fr");
Country gb = new Country("Great Britain", "gb");
Country jp = new Country("Japan", "jp");
// create the model and add elements
listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
}
public DefaultListModel<Country> getListModel() {
return listModel;
}
}
这简化了 JListCustomRendererExample
视图 class。我创建国家模型,创建 JFrame
,创建 JList
JPanel
和 JButton
JPanel
.
为 JFrame
和 JPanels
创建单独的方法有助于将相似的代码放在一起,将不同的代码分离到方法中。这使得代码更易于阅读和修改。
JButton
JPanel
允许您创建一个 ActionListener
来处理 JList
个选择。
public class JListCustomRendererExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JListCustomRendererExample());
}
private final CountryModel model;
public JListCustomRendererExample() {
this.model = new CountryModel();
}
@Override
public void run() {
JFrame frame = new JFrame("JList Renderer Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(model), BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CountryModel model) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JList<Country> countryList = new JList<>(model.getListModel());
countryList.setCellRenderer(new CountryRenderer());
panel.add(new JScrollPane(countryList), BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Select Countries");
panel.add(button, BorderLayout.CENTER);
return panel;
}
}
最后,我们有 ListCellRenderer
class。
public class CountryRenderer implements ListCellRenderer<Country> {
private final JLabel label;
public CountryRenderer() {
this.label = new JLabel();
label.setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<? extends Country> list,
Country country, int index, boolean isSelected, boolean cellHasFocus) {
label.setIcon(new ImageIcon(country.getImage()));
label.setText(country.getName());
if (isSelected) {
label.setBackground(list.getSelectionBackground());
label.setForeground(list.getSelectionForeground());
} else {
label.setBackground(list.getBackground());
label.setForeground(list.getForeground());
}
return label;
}
}
这是完整的可运行代码。我在 classes 中添加了额外的 classes,这样我就可以 post 将代码作为一个块。
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Image;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListCellRenderer;
import javax.swing.SwingUtilities;
public class JListCustomRendererExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JListCustomRendererExample());
}
private final CountryModel model;
public JListCustomRendererExample() {
this.model = new CountryModel();
}
@Override
public void run() {
JFrame frame = new JFrame("JList Renderer Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel(model), BorderLayout.CENTER);
frame.add(createButtonPanel(), BorderLayout.SOUTH);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel(CountryModel model) {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JList<Country> countryList = new JList<>(model.getListModel());
countryList.setCellRenderer(new CountryRenderer());
panel.add(new JScrollPane(countryList), BorderLayout.CENTER);
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton button = new JButton("Select Countries");
panel.add(button, BorderLayout.CENTER);
return panel;
}
public class CountryRenderer implements ListCellRenderer<Country> {
private final JLabel label;
public CountryRenderer() {
this.label = new JLabel();
label.setOpaque(true);
}
@Override
public Component getListCellRendererComponent(JList<? extends Country> list,
Country country, int index, boolean isSelected, boolean cellHasFocus) {
label.setIcon(new ImageIcon(country.getImage()));
label.setText(country.getName());
if (isSelected) {
label.setBackground(list.getSelectionBackground());
label.setForeground(list.getSelectionForeground());
} else {
label.setBackground(list.getBackground());
label.setForeground(list.getForeground());
}
return label;
}
}
public class CountryModel {
private final DefaultListModel<Country> listModel;
public CountryModel() {
Country us = new Country("USA", "us");
Country in = new Country("India", "in");
Country vn = new Country("Vietnam", "vn");
Country ca = new Country("Canada", "ca");
Country de = new Country("Denmark", "de");
Country fr = new Country("France", "fr");
Country gb = new Country("Great Britain", "gb");
Country jp = new Country("Japan", "jp");
// create the model and add elements
listModel = new DefaultListModel<>();
listModel.addElement(us);
listModel.addElement(in);
listModel.addElement(vn);
listModel.addElement(ca);
listModel.addElement(de);
listModel.addElement(fr);
listModel.addElement(gb);
listModel.addElement(jp);
}
public DefaultListModel<Country> getListModel() {
return listModel;
}
}
public class Country {
private final Image image;
private final String name;
private final String code;
public Country(String name, String code) {
this.name = name;
this.code = code;
this.image = getImage(code);
}
private Image getImage(String code) {
Image image = null;
try {
URL url = getClass().getResource("/images/" + code + ".png");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
return image;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public Image getImage() {
return image;
}
@Override
public String toString() {
return name;
}
}
}
好吧,我设法通过创建一个带有侦听器的 JPanel 来解决自己的问题,因此它就像一个 JList,但我可以将我想要的对象放入其中,这样它甚至可以更好地工作。