I am working on a simple object drawing program using Swing in Java.
My program simply should draw shapes according to buttons when clicked, and move any shapes with the mouse. I have four buttons which draw rectangle, circle and square on screen. So far I did managed to draw to shapes when you click on buttons. but i want to move the shapes on screen which it did not work out.
The problem is this: When I click on circle shape to drag it around with mouse, it clears all the screen and noting is on the screen.
And, is there a way to clean all the screen when I click on clear button?
Thank you?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class PaintProject extends JComponent implements ActionListener,
MouseMotionListener {
private int CircleX=0;
private int CircleY=0;
private int RectX=100;
private int RectY=100;
private int SquareX=300;
private int SquareY=200;
public static void main(String[] args) {
JFrame frame = new JFrame("NEW PAINT PROGRAME!");
JButton CircleButton = new JButton("Circle");
CircleButton.setActionCommand("Circle");
JButton RectangleButton = new JButton("Rectangle");
RectangleButton.setActionCommand("Rectangle");
JButton SquareButton = new JButton("Square");
SquareButton.setActionCommand("Square");
PaintProject paint = new PaintProject();
CircleButton.addActionListener(paint);
RectangleButton.addActionListener(paint);
SquareButton.addActionListener(paint);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setLayout(new FlowLayout());
frame.add(paint);
frame.add(CircleButton);
frame.add(RectangleButton);
frame.add(SquareButton);
frame.addMouseMotionListener(paint);
frame.pack();
frame.setVisible(true);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
private void drawCircle() {
Graphics g = this.getGraphics();
g.setColor(Color.red);
g.fillOval(CircleX, CircleY, 100, 100);
}
private void drawRectangle() {
Graphics g = this.getGraphics();
g.setColor(Color.green);
g.fillRect(RectX, RectY, 100, 300);
}
private void drawSquare() {
Graphics g = this.getGraphics();
g.setColor(Color.blue);
g.fillRect(SquareX, SquareY, 100, 100);
}
#Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
if (command.equals("Circle")) {
drawCircle();
}
else if (command.equals("Rectangle")) {
drawRectangle();
}
else if (command.equals("Square")) {
drawSquare();
}
}
#Override
public void mouseDragged(MouseEvent e) {
CircleX=e.getX();
CircleY=e.getY();
repaint();
}
}
As noted here and here, getGraphics() is not how to perform custom painting in Swing. Instead, override paintComponent() to render the desired content. It looks like you want to drag shapes using the mouse. A basic approach to moving a selected object is shown here; substitute your fillXxx() invocation for the drawString() shown there. For multiple shapes, use a List<Shape>; the clear command then becomes simply List::clear. A complete example is cited here; it features moveable, selectable, resizable, colored nodes connected by edges.
Related
I want to create a grid of squares in my Java Swing GUI. I need to toggle their state so I'm thinking a JToggleButton is appropriate for each of the squares.
The problem that I have is that I want to partially colour each toggle button according to given percentages. e.g. if 50% and 50% I want the left half of the button to be green and the right to be red. If 25%,25%,50% I'd need 3 colours. I also need to use the button Text field so hiding that isn't allowed in the solution.
Is it possible to do something like this with a JToggleButton? Is there a better element to use? Or how might I go about it?
I apologise for not posting my work so far but I can't find anything close to an example of this type of thing.
I want to end up with something like this where each square is a button.
You can construct a button with changeable 2-color background as required by overriding
paintComponent:
import java.awt.*;
import javax.swing.*;
public class TwoColorsButton extends JButton{
private final Color leftColor, rightColor;
private int percentOfLeftColor;
public TwoColorsButton(String text) {
this(text,Color.RED, Color.GREEN, 50);
}
public TwoColorsButton(String text, Color leftColor,Color rightColor, int percentOfLeftColor) {
super(text);
this.leftColor = leftColor;
this.rightColor = rightColor;
this.percentOfLeftColor = percentOfLeftColor;
//make button transparent
setOpaque(false);
setContentAreaFilled(false);
setBorderPainted(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
int leftWidth = getWidth() * percentOfLeftColor/100;
g2.setColor(leftColor);
g2.fillRect(0, 0, leftWidth , getHeight());
g2.setColor(rightColor);
g2.fillRect(leftWidth, 0, getWidth() -leftWidth, getHeight());
g2.setPaint(Color.BLACK);
super.paintComponent(g2); //button is transparent so super paints text only
g2.dispose();
}
public void setPercentOfLeftColor(int percentOfLeftColor) {
this.percentOfLeftColor = percentOfLeftColor;
repaint();
}
public int getPercentOfLeftColor() {
return percentOfLeftColor;
}
public static void main(String[] args) {
//run button test
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationByPlatform(true);
JPanel topPanel = new JPanel();
TwoColorsButton twoColorBtn = new TwoColorsButton("Some Text");
topPanel.add(twoColorBtn);
frame.add(topPanel, BorderLayout.PAGE_START);
JPanel bottomPanel = new JPanel();
JButton runTestBtn = new JButton("Run test");
runTestBtn.addActionListener(e->{
runTestBtn.setEnabled(false);
new Timer(1000, e1 ->{
int percent = twoColorBtn.getPercentOfLeftColor() +25;
percent = percent > 100 ? 0 : percent;
twoColorBtn.setPercentOfLeftColor(percent);
}).start();
});
bottomPanel.add(runTestBtn);
frame.add(bottomPanel, BorderLayout.PAGE_END);
frame.pack();
frame.setVisible(true);
}
}
The code can easily be modified to allow 3 colors, if needed.
(Test it online here)
(See a basic 3 colors toggle button here)
I'm developing a Java application that demands a customized button. I'm using Swing for the GUI and found myself limited to some tricky solutions.
Here's one I found (from this website). It's supposed to use a custom image for the button and make it round.
public class RoundButton extends JButton
{
private static final long serialVersionUID = 1L;
protected Shape shape, base;
public RoundButton()
{
this(null, null);
}
public RoundButton(Icon icon)
{
this(null, icon);
}
public RoundButton(String text)
{
this(text, null);
}
public RoundButton(Action a)
{
this();
setAction(a);
}
public RoundButton(String text, Icon icon)
{
setModel(new DefaultButtonModel());
init(text, icon);
if(icon==null) return;
setBorder(BorderFactory.createEmptyBorder(1,1,1,1));
setBackground(Color.BLACK);
setContentAreaFilled(false);
setFocusPainted(false);
//setVerticalAlignment(SwingConstants.TOP);
setAlignmentY(Component.TOP_ALIGNMENT);
init_shape();
}
protected void init_shape()
{
if(!getBounds().equals(base))
{
Dimension s = getPreferredSize();
base = getBounds();
shape = new Ellipse2D.Float(0, 0, s.width-1, s.height-1);
}
}
#Override
public Dimension getPreferredSize()
{
Icon icon = getIcon();
Insets i = getInsets();
int iw = Math.max(icon.getIconWidth(), icon.getIconHeight());
return new Dimension(iw+i.right+i.left, iw+i.top+i.bottom);
}
#Override
protected void paintBorder(Graphics g)
{
init_shape();
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(getBackground());
//g2.setStroke(new BasicStroke(1.0f));
g2.draw(shape);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
}
#Override
public boolean contains(int x, int y)
{
init_shape();
return shape.contains(x, y);
//or return super.contains(x, y) && ((image.getRGB(x, y) >> 24) & 0xff) > 0;
}
}
Here's some test code I wrote:
public class BtnTest extends JFrame
{
private static final long serialVersionUID = 1L;
private RoundButton btn;
public BtnTest()
{
init_components();
}
private void init_components()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setBackground(Color.BLACK);
setResizable(false);
btn = new RoundButton(
new ImageIcon("/path/to/file.png"));
btn.setBounds(50, 50, 50, 50);
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("click");
}
});
add(btn);
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
BtnTest frame = new BtnTest();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
This is the result:
The problem is: the image's rendered away from the button location, so my ActionListener isn't triggered when I press the icon, but when the region inside the black circle (top left) is clicked. Can someone explain me why, and offer a solution?
PS: I'm in my first year of Java programming, so make it as simple as possible please.
PS2: JavaFX and other external solutions are out of question, this must be done pure Java.
The problem is: the image's rendered away from the button location,
What is happening is that you are adding the button to the frame without using a constraint. By default this means the component is added to BorderLayout.CENTER, which means the component will be sized to fill the entire frame.
Also, by default, when you paint an Icon in a JLabel, the Icon is centered in the spaced available to the label, so you see the Icon in the center of the frame. Try resizing the frame to see the Icon move.
However, you hard code the painting of the Border to be painted at the (0, 0) location of the label so it paints at the top/left.
The contains() method is also defined from the top/left of the component, so mouse detection only works from the top/left, not the center.
This means your button must always be painted at its "preferred size" in order for it to be painted properly and for the contains(...) method to work.
A simple way to demonstrate this is to use:
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout( new FlowLayout() );
The FlowLayout does respect the preferred size of the button, so the Icon and Border will be painted properly.
Other options (instead of changing the layout of the frame) are to use a "wrapper" panel for the button. For example:
//add(btn);
JPanel wrapper = new JPanel(); // default to FlowLayout
wrapper.add( btn );
add( wrapper );
Now when you add the panel to the frame, the panel will grow in size, but the button will still be painted at its preferred size.
btn.setBounds(50, 50, 50, 50);
Also, not you should NOT be using this method. It is the job of the layout manager to set the size/location of the component. The above statement is effectively ignored.
what I want to achieve is two buttons that enable the user choose the color ( black or red ) and then, depends on the color, draw red or black shape, e.g. rectangle on canvas.
I have a problem with relating MouseListeners connected with buttons to setting color of the graphics in Canvas class. Where should I define the color?
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class Can extends Canvas{
int x,y;
ArrayList<Point> points = new ArrayList<Point>();
Can(){
super();
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent k){
x = k.getX();
y = k.getY();
points.add(new Point(x,y));
repaint();
}
});
}
public void paint(Graphics g){
int x2, y2;
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.black); //here is only black
for(Point p:points)
{
x2=(int)p.getX();
y2=(int)p.getY();
g2.fillRect(x2, y2, 10, 5);
}
}
}
class Win extends JFrame{
Win(String name){
super(name);
setLayout(new GridLayout());
JPanel p1 = new JPanel(new FlowLayout());
p1.setBackground(Color.cyan);
CheckboxGroup cg = new CheckboxGroup();
Checkbox red = new Checkbox("red", cg, true);
Checkbox black = new Checkbox("black", cg, false);
p1.add(red);
p1.add(black);
add(p1);
Can k = new Can();
add(k);
red.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent me){
System.out.println("Mouse click on red");
}
});
black.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent me){
System.out.println("Mouse click on black");
}
});
setSize(600, 400);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
public class ItM {
public static void main(String[] args) {
Win o = new Win("that's the window");
o.setVisible(true);
}
}
First of all you are using the Swing tag so your application should be using Swing components, not AWT components. Swing components start with "J", except for Canvas, where the Swing component is JPanel. You should then be overriding the paintComponent(...) method, not paint().
If you want to paint objects with different color then you have two options:
Paint directly to a BufferedImage. The then shape will be painted with the currently selected color
Paint from an ArrayList of object that contain information about the shape to be painted, including the size/location/color
For working examples of both approaches check out Custom Painting Approaches.
MASSIVE EDIT: I added more descritpion and code.
I recently encounter a problem where a panel that I added to another panel won't display properly (only display a black dot instead of the image). The flow of the code is: the Menu class has a button panel. When the button start is press, the Menu remove the button panel, create a Board object(that implement panel) and add it to Menu. In Board constructor, an image is loaded (a .png), then a PlayerPanel (that implement panel) is added to the board panel. In PlayerPanl constructor, an image is loaded.
The plan is to make the menu repaint() method able to call Board paintcomponent. Board will then ask PlayerPanel to paintComponent. PlayerPanel paint his image, Board paint his image and that's it, both image should display.
public class Menu extends JFrame implements ActionListener
{
Board theBoard;
JPanel pnlButton = new JPanel();
JButton btnStart = new Jbutton("Start");
public Menu(String s)
{
pnlButton.add(btnStart);
super.add(pnlButton);
super.setLocation(0,0);
super.setSize(600, 500);
super.setResizable(false);
super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
#Override
public void actionPerformed(ActionEvent evt)
{
if(evt.getSource() == btnStart)
{
theBoard = new Board ("TestBoard");
super.remove(pnlButton);
super.add(theBoard);
super.repaint();
}
}
}
public class Board extends JPanel
{
BufferedImage boardImage;
PlayerPanel playerPanel;
public Board (String boardName)
{
boardImage = Tools.loadImage(boardName);
playerPanel = new PlayerPanel();
this.add(playerPanel);
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(boardImage, 0, 200, null);
}
}
public class PlayerPanel extends JPanel
{
BufferedImage playerImage;
public PlayerPanel()
{
playerImage = Tools.loadImage("TestPlayer");
}
#Override
public void paintComponent(Graphics g)
{
g.drawImage(playerImage, 0, 0, null);
}
}
Both image load successfully as tested, but when the repaint() is call from the JFrame holding the Board panel, only the image in Board is painted, and the image in PlayerPanel is replace with a black dot.
Any help? Thanks!
Your problem seems to be due to the size of your PlayerPanel JPanel. Sometimes it helps to debug things by testing to see what size things are when they're rendered. For instance, if you change your Board paintComponent method to this:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (boardImage != null) {
g.drawImage(boardImage, 0, 200, null);
}
System.out.printf("Board size: [%d, %d]%n", getWidth(), getHeight());
System.out.printf("Player size: [%d, %d]%n", playerPanel.getWidth(), playerPanel.getHeight());
}
it will tell you exactly what the sizes are of itself and its constituent playerPanel.
To solve the issue, you could give Board a layout manager that expands its constituent components such as a BorderLayout. Another possible solution is to give PlayerPanel its own getPreferredSize() method override, especially if you want it to size itself to its image. e.g.,
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (playerImage != null) {
g.drawImage(playerImage, 0, 0, null);
}
}
Alternatively, you could use a JLabel that holds an ImageIcon rather than draw in the JPanel.
Note, you should swap views with a CardLayout rather than calling remove(...), add(...), revalidate(), repaint()
I have a poker game where I designed a nice pretty GUI that displays the cards and players. I did it all extending JPanel inside paint() with a lot of g2d.drawImage's and g2d.drawString()'s, with definite x and y locations.
My problem now is that I need to have a couple interactive buttons underneath it.. but whenever I try to add a JButton, it appears top and center. I've used setLocation(x, y) and setLayout(null) and everything I've seen in other replies, but none of them seem to match my need (Or at least I don't have a very well understanding of where to put it)
This is how my code is set up:
pokerserver.java
public class pokerserver extends JFrame {
public pokerserver() {
add(new drawing());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(720, 640);
setLocationRelativeTo(null);
setTitle("Poker HANGOUTS");
setResizable(false);
setVisible(true);
}
public static void main(String args[]) {
new pokerserver();
}
And then in drawing.class
public drawing() {
setFocusable(true);
setBackground(new Color(39,91,46));
setDoubleBuffered(true);
gameCards = new cards();
gameCards.shuffle();
for (int i = 0; i < 10; i++)
seats[i] = -1;
HQ = new HeadQuarters(this);
HQ.start();
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D)g;
//All my UI code
}
My last attempt was trying to add
JButton button = new JButton("TEST");
add(button);
button.setLocation(10, 500);
at the end of public drawing(). I Keep seeing things on layout management, but it's not helping me -- mainly because I'm not sure how to implement it
Here's a screenshot to help visualize what I'm talking about->
http://i.imgur.com/ttvif.png
Trying to get the button underneath. Unless there's a way to add an ActionListener to a drawImage()?
For your main panel use a BorderLayout.
Then to the "CENTER" you can add your game panel with all your custom painting.
Then create a panel and add the buttons to it. Now you can add this panel to the NORTH of the main panel.
In other words you are not restricted to using a single panel.
Also, custom painting should be done in the paintComponent() method of your panel, NOT the paint() method.
I'm not really sure what you are after, but here are two interpretations.
I suspect you want the 1st one 'Buttons over custom painting', but as a user I'd prefer the 2nd, with 'Buttons below custom painting'.
import java.awt.image.*;
import java.awt.*;
import javax.swing.*;
class PaintPanel extends JPanel {
BufferedImage bg;
PaintPanel(LayoutManager2 layout) {
super(layout);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (bg==null) {
bg = new BufferedImage(500,500,BufferedImage.TYPE_INT_RGB);
Graphics2D g2 = bg.createGraphics();
GradientPaint gp = new GradientPaint(
0,0,Color.RED,500,500,Color.BLUE);
g2.setPaint(gp);
g2.fillRect(0,0,500,500);
g2.dispose();
}
g.drawImage(bg,0,0,getWidth(),getHeight(),this);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JPanel buttons = new JPanel(
new FlowLayout(FlowLayout.CENTER));
buttons.setOpaque(false);
buttons.add(new JButton("Start"));
buttons.add(new JButton("Stop"));
PaintPanel pp = new PaintPanel(new BorderLayout());
pp.setPreferredSize(new Dimension(200,100));
pp.add(buttons, BorderLayout.SOUTH);
JOptionPane.showMessageDialog(null,pp);
JPanel gui = new JPanel(new BorderLayout());
gui.setBackground(Color.ORANGE);
gui.add(pp, BorderLayout.CENTER);
gui.add(buttons, BorderLayout.SOUTH);
JOptionPane.showMessageDialog(null,gui);
}
});
}
}