I have problem with multiple CheckBoxes. When I create them with my code it only show last box "bolonska zmes" and other boxes are shown only when mouseover. I think that it might be some problem with layers, but I dont know what to do. Thank you for help.
public class OknoPizzaVlastna extends JFrame
{
private String nazvy[] = { "cesnak", "feferony", "hrasok", "cibula",
"kecup", "tatarskaOmacka", "vajce",
"kapia", "fazula", "kukurica", "ananas", "brokolica",
"Niva", "Mozarella", "olivy", "inovec udeny", "articoky",
"klobasa", "sampiony", "salama", "slanina", "hranolky", "tuniak",
"sunka", "kuracie maso", "syr", "Morska zmes", "bolonska zmes"};
private JCheckBox boxes[];
public OknoPizzaVlastna()
{
boxes = new JCheckBox[nazvy.length];
for (int i = 0; i < nazvy.length; i++)
{
createrCheckBox(i);
}
setTitle("Vlastna Pizza");
setSize(480,320);
setVisible(true);
setResizable(true);
getContentPane().setLayout(null);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
public void createrCheckBox(int i)
{
boxes[i] = new JCheckBox();
//proper locations will be solved later
boxes[i].setLocation(62+i*30,54+i*20);
boxes[i].setSize(100,50);
boxes[i].setText(nazvy[i]);
boxes[i].setSelected(false);
boxes[i].setVisible(true);
getContentPane().add(boxes[i]);
}
}
If the question were, 'how to layout this GUI?' the answer might be:
To organize the components for a robust GUI, use layout managers, or combinations of them, along with layout padding & borders for white space.
In this case, we use a single column GridLayout, with an EmptyBorder on each check box to successively indent them a larger amount as we proceed down the menu.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class OknoPizzaVlastna extends JFrame {
private String nazvy[] = {
"cesnak", "feferony", "hrasok", "cibula",
"kecup", "tatarskaOmacka", "vajce",
"kapia", "fazula", "kukurica", "ananas", "brokolica",
"Niva", "Mozarella", "olivy", "inovec udeny", "articoky",
"klobasa", "sampiony", "salama", "slanina", "hranolky", "tuniak",
"sunka", "kuracie maso", "syr", "Morska zmes", "bolonska zmes"
};
JPanel ui= new JPanel(new GridLayout(0,1,4,4));
private JCheckBox boxes[];
public OknoPizzaVlastna() {
ui.setBorder(new EmptyBorder(10,10,10,10));
setContentPane(ui);
boxes = new JCheckBox[nazvy.length];
for (int i = 0; i < nazvy.length; i++) {
createrCheckBox(i);
}
setTitle("Vlastna Pizza");
pack();
setVisible(true);
setResizable(true);
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
}
public void createrCheckBox(int i) {
boxes[i] = new JCheckBox(nazvy[i]);
boxes[i].setBorder(new EmptyBorder(0,i*30,0,0));
ui.add(boxes[i]);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
public void run() {
JFrame frame = new OknoPizzaVlastna();
}
};
SwingUtilities.invokeLater(r);
}
}
Related
I am trying to make a little program that includes changing the color of a Panel time-based.
Right now I am just trying to do that part without the rest. So I just wrote a little Interface with only one panel and I want to change the color within a loop multiple times.
The problem is, even though the thread pauses for the correct amount of time, the color of the Panel doesn't change correctly. It changes just sometimes in the loop not every time.
my Interface Class:
import javax.swing.*;
import java.awt.*;
//creates the Interface
public class Interface extends JFrame {
private JPanel frame1;
public Interface (String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension (200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
my Pause Class:
import java.util.TimerTask;
//supposed to pause the thread by #pause amount of milliseconds
public class Pause extends TimerTask {
private int pause;
public Pause(int pause){
this.pause = pause;
}
#Override
public void run() {
System.out.println("Timer"+ pause+" task started at:"+System.currentTimeMillis());
pause();
System.out.println("Timer task"+ pause+" ended at:"+System.currentTimeMillis());
}
public void pause() {
try {
Thread.sleep(this.pause);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
my Blink Class
import javax.swing.*;
import java.awt.*;
public class Blink {
private JPanel frame1;
public Blink(Interface anInterface){
this.frame1 = anInterface.getFrame1();
}
// blink should change the color of the JPanel inside my Frame.
// Its supposed to change to red for 200 ms
// and then to white again for 1000 ms.
// this should be repeated 10 times.
public void blink() {
Pause pause1 = new Pause(200);
Pause pause2 = new Pause(1000);
pause2.run();
int i = 1;
while(i <= 10){
i++;
frame1.setBackground(Color.red);
frame1.repaint();
pause1.run();
frame1.setBackground(Color.white);
frame1.repaint();
pause2.run();
}
}
public static void main ( String[] args ) {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
}
}
According to Concurrency to Swing you cannot simply Thread.sleep the Thread where the GUI runs because it will freeze it, hence events cannot take place. Instead, for any kind of animation or long-heavy task (consider Thread.sleep as one), Swing Timers and Swing Workers should be used. In your case, a javax.swing.Timer fits better.
One example of its usage:
public class Blink {
private JPanel frame1;
private int pause1TimesRan;
private int pause2TimesRan;
private Timer pauser1, pauser2;
public Blink(Interface anInterface) {
this.frame1 = anInterface.getFrame1();
//Create pauser 1 with delay 200ms
pauser1 = new Timer(200, e -> {
if (pause1TimesRan == 10) {
pauser1.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #1 changed background to: " + color);
pause1TimesRan++;
});
//Create pauser 2 with delay 1000ms
pauser2 = new Timer(1000, e -> {
if (pause2TimesRan == 10) {
pauser2.stop();
return;
}
Color color = randomColor();
frame1.setBackground(color);
System.out.println("Pauser #2 changed background to: " + color);
pause2TimesRan++;
});
}
private static Color randomColor() {
return new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
}
public void blink() {
pauser1.start();
pauser2.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
Interface anInterface = new Interface("Title");
anInterface.setVisible(true);
Blink blink = new Blink(anInterface);
blink.blink();
});
}
static class Interface extends JFrame {
private JPanel frame1;
public Interface(String titel) {
super(titel);
setSize(600, 400);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame1 = new JPanel();
this.frame1.setPreferredSize(new Dimension(200, 200));
setLayout(new FlowLayout());
add(frame1);
this.setVisible(true);
}
public JPanel getFrame1() {
return frame1;
}
}
}
One off-topic advice is to name your methods (and variables) properly. You called the method getFrame1(), but it is actually a JPanel and not a JFrame. So, a better name could be getPanel(). Also, about the SwingUtilities.invokeLater part, read What does SwingUtilities.invokeLater does.
I want to print multiple label according to the number(no string allowed) you wrote in a text field first. I want it to be dynamical. I want it to change every time you type something in the text field.
So far it can read if it's a number or a string and throw exception if the text doesn't match the requirement.
I've try multiple thing to print multiple Jlabel on the screen, but it didn't work so far.
Here's the code: can you help me?
The main window class
public class MainWindow extends JFrame {
private MainPanel mp = new MainPanel();
public MainWindow()
{
this.setVisible(true);
this.setTitle("Calculateur sur 100");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(200, 400);
this.setLocationRelativeTo(null);
this.setContentPane(mp);
}}
The mainPanel class
public class MainPanel extends JPanel implements ActionListener, MouseListener, KeyListener {
private JTextField tI = new JTextField("Pourcentage");
private JOptionPane jop3 = new JOptionPane();
public MainPanel()
{
this.add(tI);
tI.addKeyListener(this);
tI.addMouseListener(this);
}
//Mathematic calculation
private double onHundred(int tot, int now)
{
return (tot / 100) * now;
}
public void keyReleased(KeyEvent e)
{
boolean ok = true;
try
{
int numbs = Integer.parseInt(tI.getText());
}
catch(Exception s)
{
tI.setText("");
jop3.showMessageDialog(null, "Veuillez entrer seulement des chiffres", "Erreur", JOptionPane.ERROR_MESSAGE);
ok = false;
}
if(ok)
{
System.out.print("Supposed to print");
JLabel[] label = new JLabel[Integer.parseInt(tI.getText())];
for(int i = Integer.parseInt(tI.getText()); i <= 0; i--)
{
label[i] = new JLabel(i + " = " + Math.ceil(onHundred(Integer.parseInt(tI.getText()), i)));
label[i].setVisible(true);
this.add(label[i]);
}
}
}
You MainWindow class should look something like this:
public class MainWindow extends JFrame {
private MainPanel mp = new MainPanel();
public static void main(String[] args) {
new MainWindow();
}
public MainWindow() {
setContentPane(mp);
setTitle("Calculateur sur 100");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
}
Note the order: setContentPane then pack then setVisible. pack replaces setSize as it determines the preferred size of the window based on its components.
I modified your MainPanel class:
public class MainPanel extends JPanel {
private JTextField tI = new JTextField("Pourcentage");
JPanel labelPanel = new JPanel();
public MainPanel() {
setLayout(new BorderLayout());
tI.getDocument().addDocumentListener(new MyDocumentListener());
add(tI, BorderLayout.PAGE_START);
add(labelPanel);
}
private int check() {
int numL;
try {
numL = Integer.parseInt(tI.getText());
} catch (NumberFormatException exc) {
return 0;
}
return numL > 100? 100 : numL;
}
private void update(int numL) {
labelPanel.removeAll();
for (int i = 0; i < numL; i++)
labelPanel.add(new JLabel(String.valueOf(i+1)));
JFrame mainWindow = ((JFrame) SwingUtilities.getWindowAncestor(this));
mainWindow.pack();
mainWindow.repaint();
}
class MyDocumentListener implements DocumentListener {
#Override
public void insertUpdate(DocumentEvent e) {
update(check());
}
#Override
public void removeUpdate(DocumentEvent e) {
update(check());
}
#Override
public void changedUpdate(DocumentEvent e) {
}
}
}
Explanation:
The main panel has the text field separately from another panel which updates dynamically to contain the labels.
The text field uses a DocumentListener instead of a KeyListener to listen to changes in its contents. This is the correct approach for many reasons I will not get into here unless really necessary.
Whenever the text changes, a check method verifies that the input is a number. If it's not it returns 0. If it's more than 100 it returns 100. You can change this behavior as you need.
The value from check is passed to update which clears all the previous labels and reconstructs them. (You can do a bit of optimization here if you want by keeping labels in memory but not displaying them. If the cap is 100 as in my example this won't be noticeable.). The main frame then recalculates the space it needs for all the labels and then repaints.
The labels appear next to each other because the default layout for JPanel is FlowLayout. You can change this as needed.
First - you have Integer.parseInt(tI.getText()) a number of times within the same keyReleased function. When you have done the first check to assign it to int numbs, then use numbs from then on, instead of referring back to tI.getText(). Theoretically the user input can change while you are processing your array, which will cause runtime exceptions or undesired results. Hint - declare numbs directly under ok.
Second - after you add controls programmatically, you need to invalidate the control on to which you are adding them, ie your MainPanel. The invalidate directive tells the control that it is not drawn correctly and needs to be repainted (do this at the completion of your loop). Look through the documentation for JPanel for invalidate and paint.
I am coding a little game in which i have taken a grid of JButtons in a JFrame and i want to refresh the colors of the buttons contained in a JFrame,which is already visible.As explained below
void foo(){
mainFrame.setVisible(true);//mainFrame is defined at class level.
//color update code for the buttons.
mainFrame.setVisible(true);
}
Result i am getting is not as expected and my screen gets freeze .Isn't it the right way to achieve what i wanted?
EDIT
ok i am explaining it in detail what i want to achieve.i have a class,as:-
import javax.swing.*;
import java.awt.*;
import java.util.*;
class Brick extends JButton{
public void setRandomColors(){
int random = (int) (Math.random()*50);
if(random%13==0){
this.setBackground(Color.MAGENTA);
}
else if(random%10==0){
this.setBackground(Color.red);
}
else if(random%9==0){
this.setBackground(Color.yellow);
}
else if(random%7==0){
this.setBackground(Color.orange);
}
else if(random%2==0){
this.setBackground(Color.cyan);
}
else{
this.setBackground(Color.PINK);
}
}
public void setBlackColor(){
this.setBackground(Color.black);
}
}
class Grid {
JFrame mainGrid = new JFrame();
ArrayList<Brick> bunchOfBricks = new ArrayList<>();
int gridLength = 8;//gridlenth is equals to gridweight as i have considered a Square grid.
int totalBricks = gridLength*gridLength;
public void formBunchOfBricks(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.add(new Brick());
}
}
public void formColoredGrid(){
Brick aBrick;
mainGrid.setLayout(new GridLayout(8,8));
for(int i=0;i<totalBricks;++i){
aBrick = (bunchOfBricks.get(i));
aBrick.setRandomColors();
mainGrid.add(aBrick);
}
mainGrid.setVisible(true);//its ok upto here iam getting randomly colored Frame of Bricks or so called JButtons.
delay(15);//Sorry for this one,i warn you not to laugh after looking its defination.
}
/*
I want following function to do following things:-
1.it should firstly display the Grid whose all buttons are black Colored.
2.After some time the original colored,first Row of grid formed by formColoredGrid should be displayed and all the rest Rows should be black.
3.Then second row turns colored and all other rows should be black......and so on upto last row of Grid.
*/
public void movingRows(){
setGridBlack();
delay(1);//see in upper method,for this horrible thing.
for(int i=0;i<gridLength;++i){
setGridBlack();
for (int j=0;j<gridLength;++j){
Brick aBrick = bunchOfBricks.get((gridLength*i)+j);
aBrick.setRandomColors();//Bricks are colored Row by Row.
}
delay(5);//already commented this nonsense.
mainGrid.setVisible(true);//using setVisible again,although this frame is already visible,when i called formColoredGrid.
setGridBlack();
}
//oh! disappointing,i have almost broken my arm slamming it on table that why the function result in a screen full of black buttons.
}
public void setGridBlack(){
for(int i=0;i<totalBricks;i++){
bunchOfBricks.get(i).setBlackColor();
}
}
public void delay(int a){
for ( int i=0;i<90000000;++i){
for(int j=0;j<a;++j){
}
}
}
public static void main(String args[]){
Grid g1 = new Grid();
g1.formBunchOfBricks();
g1.formColoredGrid();
g1.movingRows();
}
}
Please Help me what is the way out?
Your problem is in code not shown here:
//color update code for the buttons.
You're likely running a loop that never ends on the Swing event thread, possibly a never-ending while loop that polls the state of something(a guess), freezing your GUI. Solution: don't do this; don't use a continuous polling loop. Instead, change the colors based on responses to events as Swing is event-driven.
For better more specific help, please show the offending code and tell us more about your program.
Edit
If you're trying to show colored rows, one by one marching down the board, then my guess is right, you'll want to use a Swing Timer, one that uses an int index to indicate which row is being displayed in color. You'd increment the index inside of the Timer's ActionPerformed class, and then when all rows have been displayed stop the Timer. For example something like so:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import javax.swing.*;
#SuppressWarnings("serial")
public class MyGrid extends JPanel {
private static final int GRID_LENGTH = 8;
private static final Color BTN_BACKGROUND = Color.BLACK;
private static final Color[] COLORS = { Color.MAGENTA, Color.CYAN,
Color.RED, Color.YELLOW, Color.ORANGE, Color.PINK, Color.BLUE,
Color.GREEN };
private static final int PREF_W = 400;
private static final int PREF_H = PREF_W;
private static final int TIMER_DELAY = 800;
private JButton[][] buttonGrid = new JButton[GRID_LENGTH][GRID_LENGTH];
private Map<JButton, Color> btnColorMap = new HashMap<>();
private Random random = new Random();
public MyGrid() {
setLayout(new GridLayout(GRID_LENGTH, GRID_LENGTH));
for (int row = 0; row < buttonGrid.length; row++) {
for (int col = 0; col < buttonGrid[row].length; col++) {
JButton btn = new JButton();
btn.setBackground(BTN_BACKGROUND);
// !! add action listener here?
add(btn);
buttonGrid[row][col] = btn;
}
}
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
public void resetAllBtns() {
for (JButton[] row : buttonGrid) {
for (JButton btn : row) {
btn.setBackground(BTN_BACKGROUND);
}
}
}
private class TimerListener implements ActionListener {
private int row = 0;
#Override
public void actionPerformed(ActionEvent e) {
resetAllBtns(); // make all buttons black
if (row != buttonGrid.length) {
for (int c = 0; c < buttonGrid[row].length; c++) {
int colorIndex = random.nextInt(COLORS.length);
Color randomColor = COLORS[colorIndex];
buttonGrid[row][c].setBackground(randomColor);
// !! not sure if you need this
btnColorMap.put(buttonGrid[row][c], randomColor);
}
row++;
} else {
// else we've run out of rows -- stop the timer
((Timer) e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
MyGrid mainPanel = new MyGrid();
JFrame frame = new JFrame("MyGrid");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Please have a look at the Swing Timer Tutorial as well.
Edit 2
You ask:
but what is the reason of failure of this program,is it the useless delay function?
Your delay method does nothing but busy calculations on the Swing event thread:
public void delay(int a) {
for (int i = 0; i < 90000000; ++i) {
for (int j = 0; j < a; ++j) {
}
}
}
It's little different from a crude attempt at calling Thread.sleep(...), and is crude because you can't explicitly control how long it will run as you can with thread sleep. Again, the problem is that you're making these calls on the Swing event dispatch thread or EDT, the single thread that is responsible for all Swing drawing and user interactions. Blocking this thread will block your program making it non-running or frozen.
I'm trying to figure out how to make non-editable text (not a JTextField) whose background color changes when the mouse rolls over it. I tried using JButton implementing ActionListener and hiding elements to make the button appear to be just text, but it only allows me to change icons on rollover and detect when the button is clicked. Another thought was to use MouseListener and declare the specific coordinates of a rectangle around the text, where upon mouseMoved it could initiate the highlight. But w/ that there's a problem for varying string lengths and word wrap etc. What is the best object, and listener combo to achieve the effect of a highlighted text field on mouse rollover?
Hmm maybe use a foucs listener and when the field gains foucs select all the text?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TextField extends JTextField {
public TextField(String text) {
super(text);
addFocusListener(new FocusAdapter() {
#Override
public void focusGained(FocusEvent fe) {
selectAll();
}
});
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTextField tf = new JTextField("normal field");
f.add(tf, BorderLayout.CENTER);
TextField ftf =
new TextField("funny text field");
f.add(ftf, BorderLayout.SOUTH);
f.pack();
f.setVisible(true);
}
});
}
}
EDIT:
Hmmm actually found an even better way with the above method you'd have to click on the textfield to gain focus, now i've used a thread to check when the mouse is over the components co-ordinates and then to highlight the field, I used a boolean to control the highlighting as constant highlighting throws an error. Hope this is what you want:
import java.awt.*;
import javax.swing.*;
public class TextFieldHighlight extends JTextField {
static JTextField ftf;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ftf = new JTextField("Highlight");
ftf.setEditable(false);
f.add(ftf, BorderLayout.CENTER);
f.pack();
f.setVisible(true);
Thread thread = new Thread(new Runnable() {
boolean focused = false;
#Override
public void run() {
while (true) {
if (mouseIsOverDisplayPanel()) {
if (ftf.hasFocus() && focused == false) {
ftf.selectAll();
focused = true;
} else if (!ftf.hasFocus()) {
focused = false;
}
}
}
}
});
thread.start();
}
});
}
private static boolean mouseIsOverDisplayPanel() {
if (MouseInfo.getPointerInfo().getLocation().x >= ftf.getLocationOnScreen().x
&& MouseInfo.getPointerInfo().getLocation().x <= ftf.getLocationOnScreen().x + ftf.getWidth()
&& MouseInfo.getPointerInfo().getLocation().y >= ftf.getLocationOnScreen().y
&& MouseInfo.getPointerInfo().getLocation().y <= ftf.getLocationOnScreen().y + ftf.getHeight()) {
return true;
} else {
return false;
}
}
}
I have been searching this site and google for a solution to my problem, and I can't find anything. I think it's supposed to just work; however, it doesn't. The arrow icon for my JComboBox doesn't show up, and I can't find anywhere to set its visibility to true.
Here's my code:
public class Driver implements ActionListener {
private JTextField userIDField;
private JTextField[] documentIDField;
private JComboBox repository, environment;
private JButton close, clear, submit;
private JFrame window;
public Driver()
{
window = makeWindow();
makeContents(window);
window.repaint();
}
private JFrame makeWindow()
{
JFrame window = new JFrame("");
window.setSize(500,300);
window.setLocation(50,50);
window.getContentPane().setLayout(null);
window.setResizable(false);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
return window;
}
private void makeContents(JFrame w)
{
makeDropDowns(w);
w.repaint();
}
private void makeDropDowns(JFrame w)
{
String[] repositoryArray = {"Click to select", "NSA", "Finance", "Test"};
repository = new JComboBox(repositoryArray);
repository.setSelectedIndex(0);
repository.addActionListener(this);
repository.setSize(150,20);
repository.setLocation(175,165);
repository.setEditable(false);
w.add(repository);
String[] environmentArray = {"Click to select", "Dev", "Test", "Qual"};
environment = new JComboBox(environmentArray);
environment.setSelectedIndex(0);
environment.addActionListener(this);
environment.setSize(150,20);
environment.setLocation(175,195);
//environment.setEditable(false);
w.add(environment,0);
}
public void actionPerformed(ActionEvent e)
{
String repositoryID = "null", environmentID = "null";
if (e.getSource() == repository)
{
repositoryID = (String)repository.getSelectedItem();
}
if(e.getSource() == environment)
{
environmentID = (String)environment.getSelectedItem();
}
}
}
Here's a link to a picture of the problem:
If anyone could help that would be awesome.
It doesn't appear to be the issue you were suffering from, but I found this post due to the same resulting issue of the arrow disappearing.
In my case it was due to me mistakenly using .removeAll() on the JComboBox rather than .removeAllItems() when I was attempting to empty and then reuse the JComboBox after a refresh of the data I was using. Just thought I'd include it as an answer in case someone else comes across this thread for similar reasons.
The code you show works, but it looks like you're fighting the enclosing container's default layout. Here, ComboTest is a JPanel which defaults to FlowLayout.
Addendum: In general, do not use absolute positioning, as shown in your update. I've changed the example to use GridLayout; comment out the setLayout() call to see the default, FlowLayout.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/10824504/230513
*/
public class ComboTest extends JPanel {
private JComboBox repository = createCombo(new String[]{
"Click to select", "NSA", "Finance", "Test"});
private JComboBox environment = createCombo(new String[]{
"Click to select", "Dev", "Test", "Qual"});
public ComboTest() {
this.setLayout(new GridLayout(0, 1));
this.add(repository);
this.add(environment);
}
private JComboBox createCombo(String[] data) {
final JComboBox combo = new JComboBox(data);
combo.setSelectedIndex(1);
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(e.getActionCommand()
+ ": " + combo.getSelectedItem().toString());
}
});
return combo;
}
private void display() {
JFrame f = new JFrame("ComboTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ComboTest().display();
}
});
}
}
I had the same issue. I fixed it by revalidating and repainting the panel with the following code :
myPanel.revalidate();
myPanel.repaint();
Maybe a little late, but for those who are still looking for an easy and fail-safe way to use the JComboBox can use this:
public class FixedJComboBox<E>
extends JComboBox<E> {
// Copied constructors
public FixedJComboBox() {
super();
}
public FixedJComboBox(ComboBoxModel<E> aModel) {
super(aModel);
}
public FixedJComboBox(E[] items) {
super(items);
}
public FixedJComboBox(Vector<E> items) {
super(items);
}
#Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
// The arrow is the first (and only) component
// that is added by default
Component[] comps = getComponents();
if (comps != null && comps.length >= 1) {
Component arrow = comps[0];
// 20 is the default width of the arrow (for me at least)
arrow.setSize(20, height);
arrow.setLocation(width - arrow.getWidth(), 0);
}
}
}
As described here, the bug is caused by incorrectly setting both the location and the size of the arrow to (0,0), followed by some repainting issues. By simply overriding the setBounds() function, the arrow is always corrected after the UI/layout manager has wrongly updated the arrow.
Also, since new components are added after the old ones (i.e. higher index), the arrow will always be at the first element in the array (assuming you don't remove and re-add the arrow).
The disadvantage is of this class is that the width of the arrow is now determined by a constant instead of the UI/layout manager.