Cell rendering with FX in JTable - java

Given a renderer implemented in JavaFX that renders something onto a Canvas. It works well.
This renderer needs to be usable in Swing applications and it works fine thanks to JFXPanel component in most of the use cases, except for JTable. If the user wants render table cell values with the given renderer, the application starts to fail in many different ways.
Here's a sample application:
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
public class TableRenderer {
private JFrame frame;
private JFrame createFrame(String title, JComponent component, int width, int height) {
final Dimension dimension = new Dimension(width, height);
JFrame frame = new JFrame(Util.getActiveScreenDevice().getDefaultConfiguration());
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(dimension);
frame.setSize(dimension);
frame.add(component);
frame.setTitle(title);
Util.center(frame);
frame.pack();
return frame;
}
private void show() {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
private void init() throws IOException {
JTable table = new JTable(new TableModel());
table.setRowHeight(200);
table.setDefaultRenderer(Object.class, new CellRenderer());
JScrollPane p = new JScrollPane(table);
table.setFillsViewportHeight(true);
frame = createFrame("Renderer Demo - JTable", p, 420, 220);
}
public static void main(String[] args) {
TableRenderer app = new TableRenderer();
SwingUtilities.invokeLater(app::show);
}
#SuppressWarnings("serial")
private static class TableModel extends AbstractTableModel {
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return "this is cell at ("+columnIndex+"; "+rowIndex+")";
}
#Override
public int getRowCount() {
return 3;
}
#Override
public int getColumnCount() {
return 3;
}
};
private static class CellRenderer implements TableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
Rectangle cellRect = table.getCellRect(row, column, true);
Renderer renderer = new Renderer(cellRect.getSize(), value);
renderer.setSize(cellRect.getSize());
renderer.setPreferredSize(cellRect.getSize());
renderer.setBackground(java.awt.Color.BLACK);
return renderer;
}
}
#SuppressWarnings("serial")
private static class Renderer extends JFXPanel {
public Renderer(Dimension cellRect, Object value) {
super();
Group root = new Group();
Scene scene = new Scene(root);
Canvas canvas = new Canvas(cellRect.getWidth(), cellRect.getHeight());
canvas.getGraphicsContext2D().setFill(Color.RED);
canvas.getGraphicsContext2D().fillText(value.toString(), 10, 10);
canvas.getGraphicsContext2D().fillOval(10, 10, 10, 10);
root.getChildren().add(canvas);
setScene(scene);
}
}
}
In this case nothing appears in the cells and the application freezes.
The same thing works just fine if displayed in JPanel
import java.awt.Dimension;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javafx.embed.swing.JFXPanel;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
public class PanelRenderer {
private JFrame frame;
private JFrame createFrame(String title, JComponent component, int width, int height) {
final Dimension dimension = new Dimension(width, height);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setPreferredSize(dimension);
frame.setSize(dimension);
frame.add(component);
frame.setTitle(title);
Util.center(frame);
frame.pack();
return frame;
}
private void show() {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
frame.setVisible(true);
}
private void init() throws IOException {
JPanel p = new JPanel();
p.add(new Renderer(new Dimension(200, 200), "Demo"));
frame = createFrame("Renderer Demo - JPanel", p, 200, 200);
}
public static void main(String[] args) {
PanelRenderer app = new PanelRenderer();
SwingUtilities.invokeLater(app::show);
}
#SuppressWarnings("serial")
private static class Renderer extends JFXPanel {
public Renderer(Dimension cellRect, Object value) {
super();
Group root = new Group();
Scene scene = new Scene(root);
Canvas canvas = new Canvas(cellRect.getWidth(), cellRect.getHeight());
canvas.getGraphicsContext2D().setFill(Color.RED);
canvas.getGraphicsContext2D().fillText(value.toString(), 10, 10);
canvas.getGraphicsContext2D().fillOval(10, 10, 10, 10);
root.getChildren().add(canvas);
setScene(scene);
}
}
}
I've tried to add the renderer in a JPanel and return that as a cell renderer component, no change.
I've tried to synchronize FXThread and EDT, it is a hell and nothing happens.
I've tried to render an image, create a Component, overwrite the paintComponent to draw image, and synchronize the threads. That seems to work on Windows, but on OSX still fails.
I'm doing something wrong, or I should accept that this usecase is from the devil and no way to make it work?

Related

Need to change background on GUI using IntelliJ but background is hiding the buttons

package imgscoring;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import static java.lang.System.exit;
public class IMGSCORING
{
private JButton button1;
private JPanel main;
private JButton button2;
private JLabel lblLed;
private JButton button3;
private JLabel label;
private JLabel logo;
private static Image i;
public IMGSCORING() {
button1.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
try {
runProgam();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
button2.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent actionEvent) {
exit(1);
}
});
Image i = Toolkit.getDefaultToolkit().createImage("C:\\Users\\djuwo\\Desktop\\icon.jpg");
}
public static void paint(Graphics g)
{
g.drawImage(i, 0, 0, null);
}
public void runProgam() throws IOException, InterruptedException {
QrScanner run = new QrScanner();
run.runProgram();
}
public JPanel getMain() {
return main;
}
public void setMain(JPanel main) {
this.main = main;
}
public static void main(String[] args) {
JFrame frame = new JFrame(" IMG FORMING Label Generator");
frame.setContentPane(new IMGSCORING().main);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
ImageIcon img = new ImageIcon("C:\\Users\\djuwo\\Desktop\\icon.jpg");
frame.setIconImage(img.getImage());
}
private void createUIComponents() {
// TODO: place custom component creation code here
logo = new JLabel(new ImageIcon("icon.jpg"));
}
}
I am very new to using the gui for Java and based on looking around and seeing others code I can definitely tell mine is way off from how it should look.. Would appreciate feedback on how to properly format my code as well as how to add a background. The program works and buttons respond, icon are changed, etc. But I am unable to change the background. At first I tried the following but that obviously didn't work.
frame.setContentPane(new JLabel(new ImageIcon(ImageIO.read(new File("C:\\Users\\djuwo\\Desktop\\icon.jpg")))));
In the following solutions, you can see the following steps:
Override paintComponent of JPanel, in order to draw the image in its background.
Add any elements you want in the subclassed JPanel.
Add the subclassed JPanel to the content pane of the JFrame.
setOpaque(false) in the subclassed JPanel in order to show the background (where we paint the image).
In the first solution, I just paint the image once and not care about filling the frame when it is resized:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class RegularMain extends JPanel {
private final BufferedImage bimg;
public RegularMain(final BufferedImage bimg) {
//super(); //FlowLayout already.
this.bimg = Objects.requireNonNull(bimg);
//Add your components here:
for (int i = 1; i <= 3; ++i)
super.add(prepare(new JButton("Button " + i)));
//Allow pixels to show through (ie the backgroung image to be shown):
super.setOpaque(false);
//Preparing the preferred size:
final Dimension prefsz = super.getPreferredSize(); //Gets the preferred size, after adding the components to this panel.
super.setPreferredSize(new Dimension(Math.max(prefsz.width, bimg.getWidth()), Math.max(prefsz.height, bimg.getHeight())));
}
#Override
protected void paintComponent(final Graphics g) {
g.drawImage(bimg, 0, 0, this);
super.paintComponent(g);
}
public static JButton prepare(final JButton button) {
button.addActionListener(e -> JOptionPane.showMessageDialog(button, "You clicked \"" + button.getText() + "\"!"));
return button;
}
public static void main(final String[] args) throws IOException {
final JFrame frame = new JFrame("Main frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new RegularMain(ImageIO.read(new File("your_image.png"))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
But, another approach is to resize the image to correspond to the panel's size:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class TransformMain extends JPanel {
private final BufferedImage bimg;
public TransformMain(final BufferedImage bimg) {
//super(); //FlowLayout already.
this.bimg = Objects.requireNonNull(bimg);
//Add your components here:
for (int i = 1; i <= 3; ++i)
super.add(prepare(new JButton("Button " + i)));
//Allow pixels to show through (ie the backgroung image to be shown):
super.setOpaque(false);
//Preparing the preferred size:
final Dimension prefsz = super.getPreferredSize(); //Gets the preferred size, after adding the components to this panel.
super.setPreferredSize(new Dimension(Math.max(prefsz.width, bimg.getWidth()), Math.max(prefsz.height, bimg.getHeight())));
}
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g.create();
g2d.scale(getWidth() / (double) bimg.getWidth(), getHeight() / (double) bimg.getHeight());
g2d.drawImage(bimg, 0, 0, this);
g2d.dispose();
super.paintComponent(g);
}
public static JButton prepare(final JButton button) {
button.addActionListener(e -> JOptionPane.showMessageDialog(button, "You clicked \"" + button.getText() + "\"!"));
return button;
}
public static void main(final String[] args) throws IOException {
final JFrame frame = new JFrame("Main frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new TransformMain(ImageIO.read(new File("your_image.png"))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
And finally another approach is to make the image a TexturePaint, if you want the image to be repeated:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.TexturePaint;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class TextureMain extends JPanel {
private final TexturePaint paint;
public TextureMain(final BufferedImage bimg) {
//super(); //FlowLayout already.
paint = new TexturePaint(bimg, new Rectangle2D.Double(0, 0, bimg.getWidth(), bimg.getHeight()));
//Add your components here:
for (int i = 1; i <= 3; ++i)
super.add(prepare(new JButton("Button " + i)));
//Allow pixels to show through (ie the backgroung image to be shown):
super.setOpaque(false);
//Preparing the preferred size:
final Dimension prefsz = super.getPreferredSize(); //Gets the preferred size, after adding the components to this panel.
super.setPreferredSize(new Dimension(Math.max(prefsz.width, bimg.getWidth()), Math.max(prefsz.height, bimg.getHeight())));
}
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g.create();
g2d.setPaint(paint);
g2d.fillRect(0, 0, getWidth(), getHeight());
g2d.dispose();
super.paintComponent(g);
}
public static JButton prepare(final JButton button) {
button.addActionListener(e -> JOptionPane.showMessageDialog(button, "You clicked \"" + button.getText() + "\"!"));
return button;
}
public static void main(final String[] args) throws IOException {
final JFrame frame = new JFrame("Main frame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new TextureMain(ImageIO.read(new File("your_image.png"))));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
The difference in the solutions above is visible when the frame/panel is resized. In the first case, the frame is not supposed to be resized so we only draw the image once, without changing it in size. In the second case the image size follows the panel's size. And in the final case we repeat the image vertically and horizontally up to the panel's size (by using TexturePaint).
All the cases have in common that we paint the image in paintComponent before everything else and that we setOpaque to false.

JTextArea inside JPanel inside JScrollPane

I am creating a console and need to have two JTextAreas inside a JPanel inside a JScrollPane. Since setLineWrap(true) does not work when it is not directly inside the JScrollPane, I found a neat workaround here. This allows the LineWrap property to work, but sadly breaks the BorderLayout.CENTER property for one of the JTextAreas. So now the second JTextArea won't fill the entire Pane.
Here is the stripped down code for the console:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Rectangle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Console2Fix extends JPanel{
public final String def_text;
private final Font font = new Font("Source Code Pro", Font.PLAIN, 12);
public JTextArea outPane, inPane;
private JScrollPane conScroll;
private ScrollablePanel scrollPanel;
public Console2Fix(String title){
super();
def_text = title + "";
//UI
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException e) {
System.out.println("catdch 1");
} catch (InstantiationException e1) {
System.out.println("catdch 2");
} catch (IllegalAccessException e1) {
System.out.println("catdch 3");
} catch (UnsupportedLookAndFeelException e1) {
System.out.println("catdch 4");
}
SwingUtilities.updateComponentTreeUI(this);
setLayout(new BorderLayout(0, 0));
//Output Area
outPane = new JTextArea();
outPane.setLineWrap(true);
outPane.setWrapStyleWord(true);
outPane.setBackground(new Color(40,40,40));
outPane.setForeground(Color.LIGHT_GRAY);
outPane.setFont(font);
outPane.setEditable(false);
//Input Area
inPane = new JTextArea();
inPane.setLineWrap(true);
inPane.setWrapStyleWord(true);
inPane.setBackground(new Color(42,42,42));
inPane.setForeground(Color.WHITE);
inPane.setFont(font);
inPane.setCaretColor(Color.WHITE);
//Modified JPanel to support Linewraping
scrollPanel = new ScrollablePanel();
scrollPanel.setLayout(new BorderLayout(0, 0));
scrollPanel.add(outPane, BorderLayout.NORTH);
scrollPanel.add(inPane, BorderLayout.CENTER);
//ScrollPane
conScroll = new JScrollPane(scrollPanel);
conScroll.setBorder(BorderFactory.createEmptyBorder());
conScroll.getHorizontalScrollBar().setPreferredSize(new Dimension(0, 13));
conScroll.getVerticalScrollBar().setPreferredSize(new Dimension(13, 0));
conScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
this.add(conScroll, BorderLayout.CENTER);
outPane.setText(def_text);
createWindow();
}
//Creates JFrame for demonstration
public void createWindow() {
JFrame frame = new JFrame(def_text);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(this);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
//Bonus question: How can I get this to work?
//- When this is called it should scroll the conScroll to the bottom
public void down(){
inPane.scrollRectToVisible(new Rectangle(inPane.getSize()));
}
public static void main(String[] args){
new Console2Fix("Console2Fix");
}
}
And this is the modified JPanel:
import java.awt.Dimension;
import java.awt.Rectangle;
import javax.swing.JPanel;
import javax.swing.Scrollable;
public class ScrollablePanel extends JPanel implements Scrollable{
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize(); //tell the JScrollPane that we want to be our 'preferredSize' - but later, we'll say that vertically, it should scroll.
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 16;//set to 16 because that's what you had in your code.
}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 16;//set to 16 because that's what you had set in your code.
}
public boolean getScrollableTracksViewportWidth() {
return true;//track the width, and re-size as needed.
}
public boolean getScrollableTracksViewportHeight() {
return false; //we don't want to track the height, because we want to scroll vertically.
}
}
PS: I am aware that changing getScrollableTracksViewportHeight() to true seems to solve the problem, but it actually breaks the VerticalScrollBar...
PSPS: I just noticed that it behaves really strange when scrolling around real fast. Seems like it needs a repaint() somewhere...
Thanks in advance for your time.

Java repaint() not calling paintComponent()

In an attempt to make a very simple bullet-hell game to learn about java, I ran into a roadblock: repaint() wasn't calling paintComponent().
Here is the entire program, which for now simply draws an image I created 50 times per second onto a JPanel, which rests on a JFrame.
/*
* Bullet hell, by Nematodes
*/
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class bulletHell extends JFrame
{
private static final long serialVersionUID = 0L;
JPanel gamePanel = new JPanel();
int gameTimerDelay = 20;
int x, y = 0;
BufferedImage lightOrb;
javax.swing.Timer gameTimer;
public static void main(String[] args)
{
bulletHell createFrame = new bulletHell();
createFrame.frameConstructor();
}
public void frameConstructor()
{
// Construct frame and frame components
setTitle("Bullet hell");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
getContentPane().setLayout(new GridBagLayout());
setVisible(true);
GridBagConstraints gridConstraints;
gridConstraints = new GridBagConstraints();
gridConstraints.gridx = 0;
gridConstraints.gridy = 0;
gamePanel.setBackground(Color.BLACK);
gamePanel.setPreferredSize(new Dimension(700, 700));
getContentPane().add(gamePanel, gridConstraints);
pack();
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
setBounds((int) (0.5 * (screenSize.width - getWidth())),
(int) (0.5 * (screenSize.height - getHeight())), getWidth(), getHeight());
try
{
lightOrb = ImageIO.read(new File("C:/Users/Owner/Downloads/orb.bmp"));
}
catch(IOException e)
{
System.out.println("An issue occurred while trying to read orb.bmp");
}
// Start timer that draws game objects 50 times per second (50 FPS)
gameTimer = new javax.swing.Timer(gameTimerDelay, gameTimerAction);
gameTimer.setInitialDelay(0);
gameTimer.start();
}
ActionListener gameTimerAction = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
repaint();
}
};
class GraphicsPanel extends JPanel
{
public GraphicsPanel()
{
}
// Draw all of the components
#Override
public void paintComponent(Graphics g)
{
Graphics2D g2D = (Graphics2D) g;
super.paintComponent(g2D);
g2D.drawImage(lightOrb, x, y, this);
g2D.dispose();
}
}
}
After some debugging with breakpoints and println methods, I can confirm that the correct image is being read, the timer in gameTimerAction is being called 50 times per second, and repaint() is not invoking paintComponent() at all.
I am somewhat new to Java programming, and might just be missing something simple.
Edit: Problem has been solved by changing gamePanel to a GraphicsPanel object. Unfortunately, this also means that my much larger pong project (which this project's flawed drawing logic was essentially copied from) only worked by a miracle, and might be unstable with certain code additions.
I can immediately see several problems:
Most Important: You never instantiate a GraphicsPanel object, nor do you add it to anything. The paintComponent(...) method will never be called on a JPanel that is neither rendered nor created. Why not make your gamePanel variable a GraphicsPanel object and not a JPanel object?
You never change x and y in your Timer, and so without change, no animation will occur.
Also you're calling dispose on a Graphics object given to you by the JVM, something you should never do. This breaks the Swing painting chain making the graphics of your GUI unstable.
So keep at it, you'll get there.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class BulletExample extends JPanel {
public static final String IMG_PATH = "http://www.i2clipart.com/cliparts/f/0/5/8/clipart-blue-circle-f058.png";
private static final int PREF_W = 700;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 20;
private BufferedImage bullet;
private int bulletX;
private int bulletY;
public BulletExample() throws IOException {
URL imgUrl = new URL(IMG_PATH);
bullet = ImageIO.read(imgUrl);
new Timer(TIMER_DELAY, new BulletListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bullet != null) {
g.drawImage(bullet, bulletX, bulletY, this);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class BulletListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
bulletX++;
bulletY++;
repaint();
}
}
private static void createAndShowGui() throws IOException {
// create the drawing JPanel
BulletExample mainPanel = new BulletExample();
JFrame frame = new JFrame("BulletExample");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
// add it to the JFrame
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
createAndShowGui();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}

JComboBox how to show the right end of the item?

I have the following Java code:
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JComboBox;
import javax.swing.JFrame;
public class EnumsRightVisible {
public void show() {
JFrame frame = new JFrame("Combo box");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String[] items = {"aaaaaaaaa__________zzzzzzzzz",
"aaaaaaaaa__________zzzzzzzzz",
"aaaaaaaaa__________zzzzzzzzz"};
JComboBox combo = new JComboBox(items);
combo.setPreferredSize(new Dimension(100,20));
frame.add(combo, BorderLayout.CENTER);
frame.setLocation(600, 100);
frame.pack();
frame.setVisible(true);
}
public static void main(String args[]) {
EnumsRightVisible enumsRightVisible = new EnumsRightVisible();
enumsRightVisible.show();
}
}
Running it you can see that the visible text is left oriented.
Please note that this code does not solve my problem (it aligns the text to the right, but only when the combo box is expanded):
((JLabel)comboBox.getRenderer()).setHorizontalAlignment(JLabel.RIGHT);
How can I display the right end of the text in the same window (...__zzzzzz)?
Thanks in advance!
Use a custom renderer that uses FontMetrics to measure the String's width and does the ellipsis (...) manually. More info here: http://docs.oracle.com/javase/tutorial/uiswing/components/combobox.html#renderer
Method 1
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class ComboBoxDemo extends JFrame {
public ComboBoxDemo() {
JComboBox comboBox = new JComboBox();
((JLabel)comboBox.getRenderer()).setHorizontalAlignment(JLabel.RIGHT);
comboBox.addItem("Apple");
comboBox.addItem("Orange");
comboBox.addItem("Mango");
getContentPane().add(comboBox, "North");
setSize(200, 100);
this.setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public static void main(String[] args) {
new ComboBoxDemo().setVisible(true);
}
}
Method 2 (Oviously, First is better)
import java.awt.Component;
import java.awt.ComponentOrientation;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
public class ComboBoxDemo extends JFrame {
public ComboBoxDemo() {
JComboBox comboBox = new JComboBox();
setListCellRendererOf(comboBox);
comboBox.addItem("Apple");
comboBox.addItem("Orange");
comboBox.addItem("Mango");
getContentPane().add(comboBox, "North");
setSize(200, 100);
this.setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void setListCellRendererOf(JComboBox comboBox) {
comboBox.setRenderer(new ListCellRenderer() {
#Override
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
Component component = new DefaultListCellRenderer()
.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
component.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
return component;
}
});
}
public static void main(String[] args) {
new ComboBoxDemo().setVisible(true);
}
}

How to display the color selected in my combo box

So I'm attempting to make a pretty window displaying the color selected from the combo box. But I got the window and combo box made, for some reason my color isn't displayed. Can anyone help me?
Also from what I can tell, my combo box is at the top of my window. I would like to have it shown below the color.
import java.awt.*;
import java.awt.event.*;
import javax.swing.JFrame;
class Colors extends JFrame implements ItemListener
{
Choice chooseColor = new Choice();
Label lblQts = new Label("Choose color you like : ");
public Colors(String title)
{
super(title);
setLayout(new FlowLayout());
chooseColor.addItem("red");
chooseColor.addItem("green");
chooseColor.addItem("blue");
add(lblQts);
add(chooseColor);
chooseColor.addItemListener(this);
}
public void itemStateChanged(ItemEvent e)
{
String c;
Color color;
c = chooseColor.getSelectedItem();
color=Color.getColor(c);
setBackground(color);
}
}
public static void main(String[] args)
{
Colors objColor = new Colors("Color Chooser");
objColor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
objColor.setSize(400,400);
objColor.setVisible(true);
}
Let's assume for a moment that you really should be using the Swing API and not the AWT API (and seen as you're only just learning I think it's a decent assumption to make).
You can do the following...
Basically. I have JComboBox with a custom ListCellRenderer and ActionListener.
The ListCellRenderer renders the items in a fashion I want and the ActionListener listeners for changes to the combobox.
When a new item is selected, it will change the background of the combo box based on the selected item.
The concepts demonstrated here are crucial to understanding Swing (and Java in general)
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ComboBoxEditor;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestComboBox08 {
public static void main(String[] args) {
new TestComboBox08();
}
public TestComboBox08() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
final JComboBox cb = new JComboBox();
cb.addItem(new SelectableColor("Red", Color.RED));
cb.addItem(new SelectableColor("Green", Color.GREEN));
cb.addItem(new SelectableColor("Blue", Color.BLUE));
cb.setRenderer(new ColorCellRenderer());
add(cb);
cb.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Object value = cb.getSelectedItem();
if (value instanceof SelectableColor) {
cb.setBackground(((SelectableColor)value).getColor());
} else {
cb.setBackground(null);
}
}
});
cb.setSelectedItem(null);
}
}
public class SelectableColor {
private String name;
private Color color;
public SelectableColor(String name, Color color) {
this.name = name;
this.color = color;
}
public String getName() {
return name;
}
public Color getColor() {
return color;
}
}
public class ColorCellRenderer extends DefaultListCellRenderer {
#Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof SelectableColor) {
SelectableColor sc = (SelectableColor) value;
if (!isSelected) {
setBackground(sc.getColor());
setOpaque(true);
}
setText(sc.getName());
}
return this;
}
}
}
You really should familiarise yourself with Creating A UI with Swing. If that is going over your head, start with Trails covering the basics
You need to set contentPane's background, not JFrame background.
Then, you can't use Color.getColor to retrieve your color object for this case. See this
This is the working code:
import java.awt.Choice;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Label;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.lang.reflect.Field;
import javax.swing.JFrame;
public class Colors extends JFrame implements ItemListener
{
Choice chooseColor = new Choice();
Label lblQts = new Label("Choose color you like : ");
public Colors (String title) {
super(title);
setLayout(new FlowLayout());
chooseColor.addItem("red");
chooseColor.addItem("green");
chooseColor.addItem("blue");
add(lblQts);
add(chooseColor);
chooseColor.addItemListener(this);
}
public void itemStateChanged(ItemEvent e) {
String c;
Color color;
c = chooseColor.getSelectedItem();
try {
Field field = Class.forName("java.awt.Color").getField(c);
color = (Color)field.get(null);
} catch (Exception ex) {
color = null; // Not defined
}
getContentPane().setBackground(color);
}
public static void main(String[] args) {
Colors objColor = new Colors ("Color Chooser");
objColor.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
objColor.setSize(400, 400);
objColor.setVisible(true);
}
}

Categories

Resources