0

I have got a strange problem with displaying components in JFrame.

I have to write my own GUI engine (buttons, textboxes, etc...) without using Swing. Only JFrame/JPanel is allowed to be used.

Let's say I want to place 3 buttons.

My button class:

public class Button extends JPanel implements MouseListener {

Rectangle r = new Rectangle();
String text;

int X,Y,W,H;


public Button(int x, int y, int w, int h, String t)
{
    X=x;
    Y=y;
    W=w;
    H=h;

    this.setBackground(Color.CYAN);
    addMouseListener(this);

    r.setSize(w, h);
    r.setLocation(x, y);
    this.text = t;
}



@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D)g;
    g2d.draw(r);
    g2d.drawString(text, X+W/2, Y+H/2);


}

@Override
public void mouseClicked(MouseEvent arg0) {
    // TODO Auto-generated method stub

    if((arg0.getButton()==1) && r.contains(arg0.getPoint())) 
        System.out.println(arg0.getPoint().toString()); 

}


@Override
public void mouseEntered(MouseEvent arg0) {
    // TODO Auto-generated method stub

}


@Override
public void mouseExited(MouseEvent arg0) {
    // TODO Auto-generated method stub

}


@Override
public void mousePressed(MouseEvent arg0) {
    // TODO Auto-generated method stub

}


@Override
public void mouseReleased(MouseEvent arg0) {
    // TODO Auto-generated method stub

}

}

And in main class I create a JFrame and JPanel. I add to JPanel 3 buttons, and finally JPanel to JFrame, but only the last declared button shows up.

public static void main(String[] args) {
    // TODO Auto-generated method stub


    JFrame f = new JFrame("Demo");
    f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    f.setBackground(Color.cyan);

    JPanel j = new JPanel(new BorderLayout());

    j.add(new Button(10,10,100,50,"text"));
    j.add(new Button(10,100,100,50,"text2"));
    j.add(new Button(300,10,100,50,"text3"));

    f.add(j);
    f.pack();
    f.setSize(640, 400);;
    f.setVisible(true);


}

What am I doing wrong?

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Not sure what you are trying to do. Use components or paint pictures. If you use components, then you can use all built in Swing features like MouseListeners. If you just paint pictures then you also need to implement all those features. So what is the purpose of the assignment? – camickr Nov 14 '15 at 00:06
  • The purpose of this assigment is to create a GUI, which can be used when for example, there is no implemented JButton, JLabel etc. in a certain device (such as old mobile phones) – BigMacintosh Nov 14 '15 at 13:07

2 Answers2

4

Your code does not respect the BorderLayout rules. When adding 3 components to the BorderLayout using container without specifying BorderLayout location, they all get added to the default BorderLayout.CENTER spot, and the last one added covers the other 3. Consider using BorderLayout constants when adding components or using another layout manager(s).

Having said this, I think that you're better off completely changing route. Instead consider ...

  • making your Button class a logical and non-GUI class (i.e., by not having it extend JPanel),
  • having one single non-Button JPanel hold a List<Button>,
  • having this single JPanel draw all the Buttons by iterating through the list within its paintComponent method, calling a draw(Graphics g) method that each Button has
  • have the JPanel interact with the Buttons via a single MouseListener.

This will greatly simplify things.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.*;

public class FooGui extends JPanel {
    private static final int PREF_W = 640;
    private static final int PREF_H = 400;
    private List<MyButton> btnList = new ArrayList<>();

    public FooGui() {
        addMouseListener(new MyMouse());
    }

    public void addButton(MyButton btn) {
        btnList.add(btn);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for (MyButton myButton : btnList) {
            myButton.draw(g);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        if (isPreferredSizeSet()) {
            return super.getPreferredSize();
        }
        return new Dimension(PREF_W, PREF_H);
    }

    private class MyMouse extends MouseAdapter {
        @Override
        public void mousePressed(MouseEvent e) {
            for (MyButton myButton : btnList) {
                if (myButton.getRect().contains(e.getPoint())) {
                    System.out.println("Text: " + myButton.getText());
                }
            }
        }
    }

    private static void createAndShowGui() {
        FooGui fooGui = new FooGui();
        fooGui.addButton(new MyButton(10,10,100,50,"text"));
        fooGui.addButton(new MyButton(10,100,100,50,"text2"));
        fooGui.addButton(new MyButton(300,10,100,50,"text3"));

        JFrame frame = new JFrame("FooGui");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(fooGui);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }
}

class MyButton {
    private static final Color BK = Color.CYAN;
    private static final Color TEXT_COLOR = Color.BLACK;
    private int x;
    private int y;
    private int w;
    private int h;
    private String text;
    private Rectangle rect;

    public MyButton(int x, int y, int w, int h, String t) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.text = t;
        rect = new Rectangle(x, y, w, h);
    }

    public void draw(Graphics g) {
        Graphics2D g2 = (Graphics2D) g;
        g2.setColor(BK);
        g2.fill(rect);
        g2.setColor(TEXT_COLOR);
        FontMetrics metrics = g2.getFontMetrics();
        Rectangle2D bounds = metrics.getStringBounds(text, g2);
        int textX = (int) (x + (w - bounds.getWidth()) / 2);
        int textY = (int) (y + (h + bounds.getHeight()) / 2);

        g2.drawString(text, textX, textY);
    }

    public Rectangle getRect() {
        return rect;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int getW() {
        return w;
    }

    public int getH() {
        return h;
    }

    public String getText() {
        return text;
    }
}
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • Thank you for this code, I just invented something like that on my own. I have just one more question. Why in main method SwingUtilities[...] is used? – BigMacintosh Nov 13 '15 at 23:51
  • 1
    @BigMacintosh [this](http://stackoverflow.com/questions/3551542/swingutilities-invokelater-why-is-it-needed) might answer your question. – Frakcool Nov 14 '15 at 00:39
1

The purpose of this assigment is to create a GUI, which can be used when for example, there is no implemented JButton, JLabel etc. in a certain device (such as old mobile phones)

Well, that doesn't really answer my question. You addressed the issue of custom painting of buttons and labels, but there is more to a GUI then just painting things.

I also asked if you can use other features of AWT, like MouseListeners, KeyListeners, tabbing, layout managers etc. Because if you can use these features, then there is no reason to completely reinvent the wheel as has been done in the answer by hovercraft.

If all you need to do is extend JPanel and do custom painting for a button or label, then the problem with your code is that you are NOT using the layout managers properly. That is the default layout of a frame is a BorderLayout and you can't add multiple components to the CENTER of the BorderLayout.

The second problem with your posted code is you don't override the getPreferredSize() method of your components. Therefore the size will be 0 and the layout managers can't do their job properly.

camickr
  • 321,443
  • 19
  • 166
  • 288