簡體   English   中英

在 JList 中自定義渲染 JPanel

[英]Custom Render JPanel in a JList

我試圖通過修改渲染在 JList 中顯示面板。

我用 JLabel ( https://www.codejava.net/java-se/swing/jlist-custom-renderer-example ) 嘗試了一個例子,它工作得很好(見圖片)

渲染 JLabel

所以我嘗試將它改編為 JPanel(而不是 JLabel),但我遇到了一個有趣的問題,我真的不知道如何解決它。 渲染 JPanel

如您所見,不是只出現一次,而是每個國家和他的相關圖像都顯示在每一行上,我不明白為什么。 (有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 個文件來運行此文件,它們位於我在上面或此處提供的鏈接上:

國家.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 的回答,但我仍然有問題,我嘗試向這個 label 添加新元素,就像一個按鈕,我希望這些按鈕上有與國家行名稱相同的文本在。 示例:在美國行,我希望按鈕上有“美國”。

所以我在public Component getListCellRendererComponent中添加了這 4 行:

label.setLayout(null);
JButton test = new JButton(country.getName()); 
test.setBounds(10,10,50,50);
label.add(test); 

我得到了這個:

奇怪的 JButton

這不是預期的結果,這與我在第一個問題中遇到的問題有點相同,你知道為什么嗎? 有解決辦法嗎?

Oracle 有一個有用的教程,使用 Swing 創建 GUI 跳過 Netbeans 部分。

事實證明,您不能使用JPanel來保存JList選擇。 您必須使用JLabel

因此,我設計了一個略有不同的 GUI。 我從您使用的教程中復制了圖標。 這些圖標位於源代碼 zip 文件中。

JList 例子

我做的第一件不同的事情是將圖像添加到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。我創建國家 model,創建JFrame ,創建JList JPanelJButton JPanel

JFrameJPanels創建單獨的方法有助於將相似的代碼保持在一起,將不同的代碼分離到方法中。 這使得代碼更易於閱讀和修改。

JButton JPanel允許您創建將處理JList選擇或選擇的ActionListener

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;
    }
}

這是完整的可運行代碼。 我制作了額外的類內部類,這樣我就可以將代碼作為一個塊發布。

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,但我可以將我想要的 object 放入其中,這樣它甚至可以更好地工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM