I'm trying to write a program that allows user to specify a circle with 2 mouse presses, the first one on the center and the second on a point on the periphery, and the program I wrote is this.
import javax.swing.*;
import java.awt.geom.*;
import java.awt.event.*;
import java.awt.*;
public class CircleFrame extends JFrame
{
public CircleFrame()
{
circle = new Ellipse2D.Double();
hasCenter = false;
createComponent();
setSize(400, 400);
}
private void createComponent()
{
class CircleComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
circle.setFrame(xTop, yTop, radius*2, radius*2);
g2.draw(circle);
}
}
class MousePressListener1 extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
if(!hasCenter)
{
xCenter = event.getX();
yCenter = event.getY();
hasCenter = true;
}
}
}
class MousePressListener2 extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
if (hasCenter)
{
xOut = event.getX();
yOut = event.getY();
xTop = xCenter - Math.abs(xOut - xCenter);
yTop = yCenter - Math.abs(yOut - yCenter);
radius =
Math.sqrt((xOut - xCenter)*(xOut - xCenter) + (yOut - yCenter)*(yOut - yCenter));
hasCenter = false;
}
}
}
addMouseListener(new MousePressListener1());
addMouseListener(new MousePressListener2());
CircleComponent component = new CircleComponent();
add(component);
}
private double xTop;
private double yTop;
private int xCenter;
private int yCenter;
private int xOut;
private int yOut;
private Ellipse2D.Double circle;
private double radius;
private boolean hasCenter;
}
And this is the main class
import javax.swing.JFrame;
public class CircleFrameViewer
{
public static void main(String[] args)
{
JFrame frame = new CircleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Somehow it only shows a blank frame, and no clicks make anything happen.
Anyone can help me?
Don't use two listeners, use one, both listeners will be called whenever a mouse event occurs.
MouseEvents are contextual to the component which generated them. This means you should be adding your listener to the CircleComponent
Make sure you are calling repaint when you want the UI to be updated.
Something like...for example
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test2 {
public static void main(String[] args) {
new Test2();
}
public Test2() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private Point centerPoint;
private Shape circle;
public TestPane() {
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (centerPoint == null) {
centerPoint = e.getPoint();
repaint();
} else {
double xTop = Math.min(e.getPoint().x, centerPoint.x);
double yTop = Math.min(e.getPoint().y, centerPoint.y);
double xBottom = Math.max(e.getPoint().x, centerPoint.x);
double yBottom = Math.max(e.getPoint().y, centerPoint.y);
double radius = Math.max(xBottom - xTop, yBottom - yTop);
xTop = centerPoint.x - radius;
yTop = centerPoint.y - radius;
radius *= 2;
circle = new Ellipse2D.Double(xTop, yTop, radius, radius);
repaint();
}
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (circle != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.draw(circle);
g2d.dispose();
}
}
}
}
Related
OK so i'm working on a school project (little animation) and I am currently trying to make rain. I'm not sure how I would go about drawing individual "drops" using JPanel. My Code so far:
Main Class:
public class RainPanel extends JPanel {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new RainPanel();
}
private final int WIDTH = 800, HEIGHT = 800;
Drop drop;
public RainPanel() {
init();
}
public void init() {
JFrame frame = new JFrame("Rain");
JPanel drop = new Drop();
frame.setVisible(true);
frame.setSize(WIDTH, HEIGHT);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.add(drop);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
drop.paint(g);
}
Drop class:
public class Drop extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
int x,y;
int yVel = 2;
Timer t = new Timer(5, this);
Random r = new Random();
ArrayList<Drop> DropArray;
public Drop() {
x = r.nextInt(800);
y = r.nextInt(800);
t.start();
}
public void paint(Graphics g) {
super.paintComponent(g);
DropArray = new ArrayList<>(100);
for (int i = 0; i < DropArray.size(); i++) {
DropArray.add(new Drop());
}
g.setColor(Color.BLUE);
g.fillRect(x, y, 3, 15);
}
public void update() {
y += yVel;
if (y > 800)
y = r.nextInt(800);
}
#Override
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
I understand if you might be cringing hard right now (I'm fairly new to graphics coding and mostly familiar with Java itself). All i'm getting drawn currently is a single rain drop. Any suggestions are appreciated.
Don't call super.paintComponent from within paint, you're breaking the paint chain which could cause no end of issues. Override paintComponent directly instead
You shouldn't be modifying the state of a component or anything the component relies on from within any paint method, paint can be called a number of times in quick succession and this can cause no end of issues
Component based animation is not a simple task and unless you really, really need it, you should try and avoid it. Instead, write a class which is "paintable", which you can call from your paintComponent method
For example..
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class RainDropsKeepFalling {
public static void main(String[] args) {
new RainDropsKeepFalling();
}
public RainDropsKeepFalling() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new RainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class RainPane extends JPanel {
private List<Drop> drops = new ArrayList<>(100);
public RainPane() {
for (int index = 0; index < 100; index++) {
drops.add(new Drop(getPreferredSize()));
}
Timer timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Drop drop : drops) {
drop.update(getSize());
repaint();
}
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Drop drop : drops) {
Graphics2D g2d = (Graphics2D) g.create();
drop.paint(g2d);
g2d.dispose();
}
}
}
protected static final Random random = new Random();
public static class Drop {
private double vDelta = random.nextDouble() + 0.5;
private int height = 15;
private int width = 3;
private double x;
private double y = -height;
private Rectangle2D shape;
public Drop(Dimension size) {
x = random.nextInt(size.width - width) + width;
y = random.nextInt(size.height - height) + height;
shape = new Rectangle2D.Double(x, y, width, height);
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLUE);
g2d.fill(shape);
}
public void update(Dimension size) {
y += vDelta;
if (y > size.height) {
y = -height;
x = random.nextInt(size.width - width) + width;
}
shape.setRect(x, y, width, height);
}
}
}
I am building up a system with a canvas where user can draw lines by dragging the mouse in Java. I want all the strokes to be stored and displayed. However, when I press the mouse to draw a new line, previous lines are automatically erased, which is not what I was expecting. How can I solve this problem?
Here is my code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FreeDrawing extends JPanel implements MouseListener, MouseMotionListener {
private int indexOfStroke = 0;
private int indexOfPoint = 0;
private Stroke[] strokes = new Stroke[50];
private Point[] currentPoints = new Point[500];
public FreeDrawing(String name) {
super();
this.addMouseListener(this);
this.addMouseMotionListener(this);
JFrame fr = new JFrame(name);
fr.add(this);
fr.setSize(500, 500);
setBackground(Color.GRAY);
fr.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
fr.setVisible(true);
}
public void paintComponent(Graphics g) {
System.out.println("Paint");
super.paintComponents(g);
strokes[indexOfStroke] = new Stroke();
for (int i = 0; i < indexOfPoint - 1; i++) {
System.out.println("Really draw");
g.drawLine(currentPoints[i].x, currentPoints[i].y, currentPoints[i + 1].x, currentPoints[i + 1].y);
}
}
public void mouseDragged(MouseEvent e) {
System.out.println("Drag");
currentPoints[indexOfPoint] = new Point(e.getX(), e.getY());
indexOfPoint++;
repaint();
}
public void mousePressed(MouseEvent e) {
System.out.println("Press");
currentPoints[indexOfPoint] = new Point(e.getX(), e.getY());
indexOfPoint = 0;
repaint();
}
public void mouseReleased(MouseEvent e) {
indexOfPoint = 0;
indexOfStroke++;
}
public void mouseExited(MouseEvent e) {
}
public void mouseClicked(MouseEvent e) {
}
public void mouseEntered(MouseEvent e) {
}
public void mouseMoved(MouseEvent e) {
}
public static void main(String[] args) {
FreeDrawing canvas = new FreeDrawing("Mouse");
}
}
class Stroke {
public Stroke() {
System.out.println("Stroke initiated");
points = new Point[500];
}
Point[] points;
}
Because, that's how painting works, see Painting in AWT and Swing and Performing Custom Painting for more details.
Basically, painting is destructive, meaning that each time paintComponent is called, you are expected to repaint the entire state of the component from scratch
One thing you might consider doing, is creating a class which contains the stroke, color and points you need to each line and store that in a List each time you click
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Stroke;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private List<Drawing> drawings;
private Drawing current;
private Random rnd = new Random();
public TestPane() {
drawings = new ArrayList<>(25);
addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
Stroke stroke = new BasicStroke(rnd.nextInt(9) + 1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
Color color = new Color(rnd.nextInt(255), rnd.nextInt(255), rnd.nextInt(255));
current = new Drawing(stroke, color);
current.addPoint(e.getPoint());
drawings.add(current);
}
});
addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
current.addPoint(e.getPoint());
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (Drawing drawing : drawings) {
drawing.paint(g2d);
}
g2d.dispose();
}
}
public class Drawing {
private Stroke stroke;
private Color color;
private List<Point> points;
public Drawing(Stroke stroke, Color color) {
this.stroke = stroke;
this.color = color;
this.points = new ArrayList<>(25);
}
public void addPoint(Point p) {
points.add(p);
}
public void paint(Graphics2D g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(color);
g2d.setStroke(stroke);
if (!points.isEmpty()) {
Point from = points.get(0);
for (Point to : points.subList(1, points.size())) {
g2d.draw(new Line2D.Double(from, to));
from = to;
}
}
g2d.dispose();
}
}
}
I want to draw a rectangle on an image when the mouse button is pressed and released. And this part works just fine. Now I want to be able to see the rectangle while I drag the mouse, what I get is lots of rectangles being drawn please help.
class ActionTemp implements ActionListener {
public void actionPerformed(ActionEvent e) {
myPanel.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent event) {
letsdraw = tempimage.createGraphics();
Point panelPoint = event.getPoint();
sX = panelPoint.x;
sY = panelPoint.y;
}
#Override
public void mouseReleased(MouseEvent event) {
letsdraw.draw(new Rectangle2D.Float(Math.min(sX, curX),
Math.min(sY, curY), Math.abs(sX - curX),
Math.abs(sY - curY)));
letsdraw.dispose();
myPanel.repaint();
}
});
myPanel.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
Point panelPoint = event.getPoint();
curX = panelPoint.x;
curY = panelPoint.y;
letsdraw.draw(new Rectangle2D.Float(Math.min(sX, curX),
Math.min(sY, curY), Math.abs(sX - curX),
Math.abs(sY - curY)));
myPanel.repaint();
}
});
}
}
Start by having a look at Painting in AWT and Swing and Performing Custom Painting.
The basic problem is, you painting directly to the image, which means, unless you have a separate copy, you're just compounding each successive rectangle on top of the last.
Instead, you want to paint each of them separately, something like...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SelectionExample {
public static void main(String[] args) {
new SelectionExample();
}
public SelectionExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
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 {
private Rectangle selection = new Rectangle();
private Point clickPoint;
private BufferedImage tempimage;
public TestPane() {
try {
tempimage = ImageIO.read(new File("/Users/shane/Dropbox/MegaTokyo/thumnails/2.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int minX = Math.min(e.getX(), clickPoint.x);
int minY = Math.min(e.getY(), clickPoint.y);
int maxX = Math.max(e.getX(), clickPoint.x);
int maxY = Math.max(e.getY(), clickPoint.y);
selection.x = minX;
selection.y = minY;
selection.width = maxX - minX;
selection.height = maxY - minY;
repaint();
}
#Override
public void mousePressed(MouseEvent e) {
clickPoint = new Point(e.getPoint());
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
#Override
public Dimension getPreferredSize() {
return tempimage == null ? new Dimension(200, 200) : new Dimension(tempimage.getWidth(), tempimage.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
int x = (getWidth() - tempimage.getWidth()) / 2;
int y = (getHeight() - tempimage.getHeight()) / 2;
g2d.drawImage(tempimage, x, y, this);
if (selection.width > 0 && selection.height > 0) {
g2d.setColor(new Color(0, 0, 255, 64));
g2d.fill(selection);
g2d.setColor(Color.BLUE);
g2d.draw(selection);
}
g2d.dispose();
}
}
}
I am trying to design a GUI with triangle shaped buttons. I have create the triangle button class correctly in so far as creating a JButton with my class constructor results in a triangle button on the page, but I fall short when it comes to placement of the button.
Could any direct me or have an example for creating a hexagonal shape from triangle buttons?
Here is my TriangleButton class for reference:
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
class TriangleButton extends JButton {
final static double side_len = 52; //Change for variable triangle size
final static double y_offset = (Math.sqrt(3) * side_len / 2);
private Shape triangle;
public TriangleButton(int spot){
triangle = createTriangle(spot);
}
public void paintBorder( Graphics g ) {
((Graphics2D)g).draw(triangle);
}
public void paintComponent( Graphics g ) {
((Graphics2D)g).fill(triangle);
}
public Dimension getPreferredSize() {
return new Dimension((int)side_len, (int)y_offset);
}
public boolean contains(int x, int y) {
return triangle.contains(x, y);
}
private Shape createTriangle(int spot) {
Polygon p = new Polygon();
p.addPoint( 0 , 0 );
p.addPoint( (int)side_len , 0 );
p.addPoint( (int)side_len/2, (int)(y_offset) );
return p;
}
}
The look I had in mind would be something like..
With space between the buttons.. basically just up-pointing and down-pointing triangles lined up.
But anything to put me in the right direction would be appreciated!
As an alternative, due to the complexities of generating a suitable layout to allow components to overlap, you could simply create a single button which housed all the triangles and which provided centralised control, for example
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Path2D;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.DefaultButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new GridBagLayout());
HexagonButton btn = new HexagonButton();
btn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println(Arrays.toString(btn.getSelectedObjects()));
System.out.println(e.getActionCommand());
}
});
add(btn);
}
}
public class HexagonButton extends AbstractButton {
public static final String TOP_RIGHT_QUAD = "Top.right";
public static final String TOP_QUAD = "Top";
public static final String TOP_LEFT_QUAD = "Top.left";
public static final String BOTTOM_LEFT_QUAD = "Bottom.left";
public static final String BOTTOM_QUAD = "Bottom";
public static final String BOTTOM_RIGHT_QUAD = "Bottom.right";
private Shape top;
private Shape topRight;
private Shape topLeft;
private Shape bottomLeft;
private Shape bottomRight;
private Shape bottom;
private Map<String, Shape> paths;
private String selectedQuad;
public HexagonButton() {
setModel(new DefaultButtonModel());
createPaths();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
String previousQuad = selectedQuad;
selectedQuad = null;
for (String quad : paths.keySet()) {
Shape shape = paths.get(quad);
if (shape.contains(e.getPoint())) {
getModel().setPressed(true);
getModel().setArmed(true);
selectedQuad = quad;
if (!selectedQuad.equals(previousQuad)) {
fireActionPerformed(new ActionEvent(HexagonButton.this, ActionEvent.ACTION_PERFORMED, selectedQuad));
}
break;
}
}
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
getModel().setArmed(false);
getModel().setPressed(false);
}
});
}
#Override
public Object[] getSelectedObjects() {
return new Object[]{selectedQuad};
}
#Override
public void invalidate() {
super.invalidate();
createPaths();
}
protected void createPaths() {
topRight = create(0d, -60d);
top = create(-60d, -120d);
topLeft = create(-120d, -180d);
bottomLeft = create(-180d, -240d);
bottom = create(-240d, -300d);
bottomRight = create(-300d, -360d);
paths = new HashMap<>(6);
paths.put(TOP_RIGHT_QUAD, topRight);
paths.put(TOP_QUAD, top);
paths.put(TOP_LEFT_QUAD, topLeft);
paths.put(BOTTOM_LEFT_QUAD, bottomLeft);
paths.put(BOTTOM_QUAD, bottom);
paths.put(BOTTOM_RIGHT_QUAD, bottomRight);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(104, 104);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); //To change body of generated methods, choose Tools | Templates.
Graphics2D g2d = (Graphics2D) g.create();
if (selectedQuad != null) {
Shape path = paths.get(selectedQuad);
g2d.setColor(UIManager.getColor("List.selectionBackground"));
g2d.fill(path);
}
g2d.setColor(getForeground());
g2d.draw(topRight);
g2d.draw(top);
g2d.draw(topLeft);
g2d.draw(bottomLeft);
g2d.draw(bottom);
g2d.draw(bottomRight);
g2d.dispose();
}
public Shape create(double startAngle, double endAngle) {
double width = getWidth();
double height = getHeight();
double radius = Math.min(width, height) / 2;
double xOffset = width - radius;
double yOffset = height - radius;
double startX = xOffset + radius * (Math.cos(Math.toRadians(startAngle)));
double startY = yOffset + radius * (Math.sin(Math.toRadians(startAngle)));
double endX = xOffset + radius * (Math.cos(Math.toRadians(endAngle)));
double endY = yOffset + radius * (Math.sin(Math.toRadians(endAngle)));
Path2D path = new Path2D.Double();
path.moveTo(xOffset, yOffset);
path.lineTo(startX, startY);
path.lineTo(endX, endY);
path.closePath();
return path;
}
}
public static class TriangleButton extends JButton {
final static double side_len = 52; //Change for variable triangle size
final static double y_offset = (Math.sqrt(3) * side_len / 2);
private Shape triangle;
public TriangleButton(int spot) {
triangle = createTriangle(spot);
}
#Override
public void paintBorder(Graphics g) {
super.paintBorder(g);
((Graphics2D) g).draw(triangle);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).fill(triangle);
}
#Override
public Dimension getPreferredSize() {
return new Dimension((int) side_len, (int) y_offset);
}
#Override
public boolean contains(int x, int y) {
return triangle.contains(x, y);
}
private Shape createTriangle(int spot) {
Polygon p = new Polygon();
p.addPoint(0, 0);
p.addPoint((int) side_len, 0);
p.addPoint((int) side_len / 2, (int) (y_offset));
return p;
}
}
}
Using your class I made some changes and came up with the following:
import java.awt.*;
import java.awt.Shape;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.*;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class TriangleButton2 extends JButton {
final static double side_len = 52; //Change for variable triangle size
final static double y_offset = (Math.sqrt(3) * side_len / 2);
private Shape triangle;
public TriangleButton2(int degrees){
triangle = createTriangle(degrees);
setRolloverEnabled( false );
setContentAreaFilled( false );
setBorderPainted( false );
}
public void paintBorder( Graphics g ) {
((Graphics2D)g).draw(triangle);
}
public void paintComponent( Graphics g ) {
super.paintComponent(g);
((Graphics2D)g).fill(triangle);
}
public Dimension getPreferredSize() {
return new Dimension((int)side_len, (int)y_offset);
}
public boolean contains(int x, int y) {
return triangle.contains(x, y);
}
private Shape createTriangle(int degrees) {
Polygon p = new Polygon();
p.addPoint( 0 , 0 );
p.addPoint( (int)side_len , 0 );
p.addPoint( (int)side_len/2, (int)(y_offset) );
return ShapeUtils.rotate(p, degrees);
// return p;
}
private static void createAndShowGUI()
{
JPanel panelNorth = new JPanel( new FlowLayout(FlowLayout.CENTER, -22, 2) );
panelNorth.add( new TriangleButton2(180) );
panelNorth.add( new TriangleButton2(0) );
panelNorth.add( new TriangleButton2(180) );
JPanel panelSouth = new JPanel( new FlowLayout(FlowLayout.CENTER, -22, 1) );
panelSouth.add( new TriangleButton2(0) );
panelSouth.add( new TriangleButton2(180) );
panelSouth.add( new TriangleButton2(0) );
JFrame frame = new JFrame("SSCCE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(panelNorth, BorderLayout.NORTH);
frame.add(panelSouth, BorderLayout.SOUTH);
frame.setLocationByPlatform( true );
frame.pack();
frame.setVisible( true );
}
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
}
The above code uses the ShapeUtils class found in Playing With Shapes.
Not sure of the exact functionality you want from the button. Your current implantation doesn't have any visual effects when you click on the button or mouse over the button.
In this case you might want to consider just creating an Icon to represent your triangle. you can use the ShapeIcon class found in Playing With Shapes to create your triangle icons. Then you can use the ShapeComponent class also found in Playing With Shapes to create an actual component that you add to the panel.
The FlowLayout shows how you can overlap the buttons to get your desired layout effect.
Check out this page for some relatable information:
Creating custom JButton from images containing transparent pixels
It just might be easier creating JButtons from triangle images.
If I'm working with a JPanel, what's the difference between the Graphics object returned by getGraphics, and the Graphics object that is passed in paintComponent method?
getGraphics
Can be null
Is a "snap shot" of the last paint process
Anything painted to it will be lost on the next paint cycle
You should avoid using getGraphics and simply use what is past to the paintComponent method.
In theory, there is no difference between them, but if you want what you have painted to survive between repaints, then you should be using paintComponent
Updated with example
Without an example code to go by I'm guessing...but...
Basically, this has a JPanel that acts as the primary "canvas" that draws an image and, when set, a Rectangle which acts as the selection.
It uses a second class as the MouseListener to actually make the decisions about what to paint
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
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;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class MouseSelection {
public static void main(String[] args) {
new MouseSelection();
}
public MouseSelection() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
ImagePane imgPane = new ImagePane();
new MouseHandler(imgPane);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(imgPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MouseHandler extends MouseAdapter {
private ImagePane imgPane;
private Point clickPoint;
public MouseHandler(ImagePane imgPane) {
this.imgPane = imgPane;
imgPane.addMouseMotionListener(this);
imgPane.addMouseListener(this);
}
#Override
public void mousePressed(MouseEvent e) {
imgPane.clearSelection();
clickPoint = e.getPoint();
}
#Override
public void mouseReleased(MouseEvent e) {
clickPoint = null;
}
#Override
public void mouseDragged(MouseEvent e) {
if (clickPoint != null) {
Point dragPoint = e.getPoint();
int x = Math.min(clickPoint.x, dragPoint.x);
int y = Math.min(clickPoint.y, dragPoint.y);
int width = Math.max(clickPoint.x, dragPoint.x) - x;
int height = Math.max(clickPoint.y, dragPoint.y) - y;
imgPane.setSelection(new Rectangle(x, y, width, height));
}
}
}
public class ImagePane extends JPanel {
private BufferedImage img;
private Rectangle selection;
public ImagePane() {
try {
img = ImageIO.read(new File("C:\\hold\\thumbnails\\issue459.jpg"));
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(), img.getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (img != null) {
int x = (getWidth() - img.getWidth()) / 2;
int y = (getHeight() - img.getHeight()) / 2;
g2d.drawImage(img, x, y, this);
}
if (selection != null) {
Color color = UIManager.getColor("Table.selectionBackground");
g2d.setColor(color);
Composite comp = g2d.getComposite();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(selection);
g2d.setComposite(comp);
g2d.draw(selection);
}
g2d.dispose();
}
protected void clearSelection() {
selection = null;
repaint();
}
protected void setSelection(Rectangle rectangle) {
selection = rectangle;
repaint();
}
}
}