I'm designing an optimization system for public transport in a big city. So I have a map with some points on it, but don't care about it)
All I need is: my own JButton, which looks like a color-filled circle and a small text tag near it. I got some problems while overriding the paintComponent() method.. the round button is painted correctly, but not the text.
BUT, when i'm resizing the window manually, the text appears for a second, then it gets repainted again and dissapears.
Hope you guys understood my needs, thanks for help ;)
import java.awt.*;
import javax.swing.*;
public class JRoundButton extends JButton {
String label;
Color color;
int x,y;
public JRoundButton(Color color,int x,int y,String str)
{
label=str;
this.x=x;
this.y=y;
this.color=color;
}
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Dimension size = getPreferredSize();
setPreferredSize(size);
this.setBounds(0, 0, 10, 10);
setContentAreaFilled(false);
g.setFont(new Font("Arial",Font.BOLD,14));
g.drawChars(label.toCharArray(), 0, label.length(), 12,12);
g.fillOval(0,0,8,8);
}
public void paintBorder(Graphics g)
{
g.setColor(Color.white);
g.drawOval(0,0, 9, 9);
}
public static void main(String[] args)
{
JButton button = new JRoundButton(Color.GRAY,150,150,"Times Square");
JFrame frame = new JFrame();
frame.getContentPane().setBackground(Color.black);
frame.setSize(300, 300);
frame.setVisible(true);
frame.add(button);
}
}
Seems that the call to 'setBounds( 0, 0, 10, 10 )' sets a component footprint that is too small to accomodate the text string. Extending the bounds to 100px wide and bringing down the point size to 6 looks to work okay.
1) NEVER set properties of the button in the paintComponent() method.
Dimension size = getPreferredSize();
setPreferredSize(size);
this.setBounds(0, 0, 10, 10);
setContentAreaFilled(false);
Get rid of the above code.
2) Dont set the Font of the Graphics object in the paintComponent() method. Thats what the setFont(...) method is used for.
3) There is no need to do any custom painting. If you want a circle, then add an Icon to the JLabel.
4) Don't override the paintBorder() method. If you want a Border then create a custom border and add it to the button using the setBorder() method.
In short there is no need to extend the button. Get rid of your JRoundButton class. Your code should simply look something like:
JButton = new JButton("Times Square");
button.setFont( new Font("Arial",Font.BOLD,14) );
button.setIcon( new OvalIcon(Color.WHITE, iconSize) );
Of course you will need to create an OvalIcon class but that is easy to implement since there are only three methods and you already know what the painting code should be.
I'd just cheat and use a unicode circle in the JButton's text. E.g.:
import javax.swing.*;
JFrame frame = new JFrame();
frame.getContentPane().add(new JButton("<html><font size='+10' color='red'>●</font> I'm next to a red circle!</html>"));
frame.pack();
frame.show();
Related
Disclaimer: I'm new to Java. I'm new to Swing. And I'm sure it shows.
I've viewed quite a number of examples/tutorials of how to draw on a jpanel "canvas". But they mostly have the same basic format and put all of their drawLine/drawRect/drawArc inside the paintComponent() method. It seems assumed that people want to draw static things to the jpanel one time. But what if I want to change the jpanel object over the course of the program's runtime, like a paint program, or a game?
I suppose I need to be able to access the jpanel object, and internal methods to paint. I'm betting what I'm doing isn't best practices, but here is what I have:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class PaintPanel extends JPanel {
public static JFrame frame;
private Graphics g = getGraphics();
public static void main(String[] args) {
frame = new JFrame();
frame.getContentPane().setBackground(new Color(32, 32, 32));
frame.setResizable(false);
frame.setBounds(1, 1, 800, 600);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setVisible(true);
frame.setLocationRelativeTo(null); // center frame on screen
PaintPanel paintPanel = new PaintPanel();
paintPanel.setBounds(10, 10, 100, 100);
paintPanel.setBackground(Color.red);
frame.add(paintPanel);
}
// constructor
public PaintPanel() {
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
}
public void DrawRect(Integer x, Integer y, Integer w, Integer h, Color color) {
g.setColor(color);
g.fillRect(x, y, w, h);
this.repaint(); // doesn't seem to do anything
}
}
This code results in a red panel box, but my user method DrawRect() doesn't draw anything.
I've read in some places that it's necessary to override the paintComponent() method. If there's nothing in it, what's the purpose?
How can I get my DrawRect() method to work?
The piece of the puzzle you're missing is the model object. There should be an external object that describes what should be drawn. In a game for example, it would be something that describes the current state of the game.
Your custom component looks at this model and takes the necessary steps to paint it. This is implemented in paintComponent and in helper methods you see fit to add.
To make an animation, you make a loop that modifies the model over time, and asks the custom component to redraw itself with repaint().
I'm trying to draw a line in a JFrame, but line isn't drawn.
I tried to use the method setOpaque(true) for contentPane, lblNewLabel and l but nothing changed. I also tried call repaint(); outside this class but the situation is still the same. Here's the code:
public class DrawingClass extends JFrame
{
private JPanel contentPane;
public DrawingClass(int n, int s, int p) {
Line l= new Line();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
setBounds(700, 300, 480, 640);
contentPane = new JPanel();
contentPane.setOpaque(true);
setResizable(false);
setContentPane(contentPane);
contentPane.setLayout(null);
JLabel lblNewLabel = new JLabel("");
lblNewLabel.setIcon(new ImageIcon("image.png"));
lblNewLabel.setBounds(0, 0, 480, 640);
contentPane.add(lblNewLabel);
l.setBounds(0,0,480,640);
contentPane.add(l);
repaint();
}
class Line extends JPanel
{
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
}
}
I expect a little line on the top left of the JFrame, above the background wallpaper, but nothing happen. It shows only the wallpaper.
There are several errors in your code:
You're extending JFrame but you're not changing its behavior, so why are you doing that? JFrame is a rigid component, so it's never a good idea to extend from it, build your GUI based on JPanels instead. See: Extends JFrame vs. creating it inside the program
Don't explicitly set the size of the JFrame, call pack() on it and instead override getPreferredSize from the JPanel, see: Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing?
You don't need to call setOpaque(...) in this case.
Don't use a null-layout, it might lead to strange errors, because null Layout is Evil and frowned upon
We don't have access to your image so we cannot test the ImageIcon and it's also not related to your question. However you should load your images as resources
Don't explicitly set the bounds of each element, this is related to point (4) and you should use a Layout Manager or combinations of them to get your desired GUI.
Don't call repaint() that way, it has no effect, it is supposed to repaint your UI when there's a change in it. However there is no change at the start of your program.
You're breaking the paint-chain by not calling super.paintComponent(...) inside your paintComponent(...) method. Check the Tutorial on Custom Painting in Swing so that you learn how to do it properly
And be careful, as paintComponents(...) (With a trailing s) is different from paintComponent(...) (Look at your title)
So, after doing all of the above changes, we get to this simple program:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class DrawingClass {
private JPanel contentPane;
private JFrame frame;
public static void main(String args[]) {
SwingUtilities.invokeLater(() -> new DrawingClass().createAndShowGUI());
}
public void createAndShowGUI() {
frame = new JFrame(getClass().getSimpleName());
Line line = new Line();
frame.add(line);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
class Line extends JPanel {
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillRect(10, 10, 15, 12);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(480, 640);
}
}
}
Which produces the following output:
I want to draw some different JComponents in one JPanel. I create some JComponents with different paint methods. Then create the objects in the main and put them to the JFrame.
My problem is, that only the last Object is painted in the Window.
How can I put different JComponents in the window, without remove or repaint the old ones?
(Model2 works like Model1, but the paintComponent is a little bit different)
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class GuiModel{
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(600, 600);
frame.setLocation(150, 150);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.getContentPane().add(new Model1(0,0));
frame.getContentPane().add(new Model2(25,37,true));
}
}
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JComponent;
public class Model1 extends JComponent {
private int xPos, yPos;
Model1 (int x, int y){
this.xPos = x;
this.yPos = y;
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
g.drawOval(xPos, yPos, 5, 5);
}
}
When doing custom painting:
You need to override the getPreferredSize() method of your component to return the size of the component so a layout manager can display the component. Right now the size of your components are (0, 0) so there is nothing to paint.
The painting of a component is done from (0, 0), not (x, y). Then if you position the component at a specific point on the panel you use setLocation(x, y) to tell Swing where to painting the component.
If you want to paint the component at a random position then you also need to use a null layout on the panel and you must also set the size of the component. To set the size of the component you would use setSize( getPreferredSize()) in your constructor.
So your Model1 class would look something like:
public class Model1 extends JComponent {
//private int xPos, yPos;
Model1 (int x, int y){
//this.xPos = x;
//this.yPos = y;
setLocation(x, y);
setSize( getPreferredSize() );
}
#Override
public Dimension getPreferredSize()
{
return new Dimension(5, 5);
}
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.BLACK);
//g.drawOval(xPos, yPos, 5, 5);
g.drawOval(0, 0, 5, 5);
}
}
JFrame uses BorderLayout by default. When adding components without specifying the constraints, each component will be placed at CENTER.
Either specify the constraints, or if BorderLayout isn't suffice for you, switch to another layout.
To add constraints:
frame.add(new Model1(0,0), BorderLayout.NORTH);
I suggest reading How to use BorderLayout, as well as guides on the other predefined layouts. Also, feel free to look online for 3rd party layouts, if none of the layouts available through the JDK fit your needs. Or you could create your own LayoutManager if no layouts exist that fit your needs
I also recommend using pack() to size your frame based on the components inside instead of setting it's size with frame.setSize. This ensures your frame tightly wraps around anything inside of it, leaving no empty spaces. If you want empty spaces, it should be handled by the layout manager
You can specify size for components or use Layouts to place objects not in one place. Or you can create copy of Graphics and write in it:
protected void paintComponent(Graphics g) {
g = g.create();
g.setColor(Color.BLACK);
g.drawOval(xPos, yPos, 5, 5);
g.dispose();
}
But the last variant is not efficient: it makes copy of Graphics on each repaint.
I am able to draw a horizontal line but unable to draw a vertical line. Please help me.
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
class Success extends JFrame{
public Success(){
JPanel panel=new JPanel();
getContentPane().add(panel);
setSize(450,450);
JButton button =new JButton("press");
panel.add(button);
}
public void paint(Graphics g) {
super.paint(g); // fixes the immediate problem.
Graphics2D g2 = (Graphics2D) g;
Line2D lin = new Line2D.Float(20, 40, 850, 40);
g2.draw(lin);
}
public static void main(String []args){
Success s=new Success();
s.setVisible(true);
}
}
Thanks in advance.
Keep the x co-ordinates the same and change value of y co-ordinates as shown below
Line2D lin = new Line2D.Float(20, 40, 20, 150);
First two values are the (x1,y1) value of the starting point of the line and last two values (x2,y2) end point of the line. Now I hope you understand why your code produced a horizontal line and what needs to be done to draw vertical line.
I noticed a couple things, some of them were already pointed out:
To answer your question directly, this is what the (x, y) coordinates look like for Swing components
keep x coordinates the same for a vertical line. If you don't know where the x coordinates are in your line constructor when you create it, look at the documentation for the constructor. If you're using Eclipse, this means you should just hover your mouse over the code that contains the constructor.
Your line goes outside the range of your JFrame; instead, if you want it to go from end to end, use the getWidth() and getHeight() methods.
You shouldn't be creating a new line every time you repaint your components. Instead, you should create the line somewhere in you Success class, implement ActionListener so you can update your code every frame, and in that update, resize your line, then leave just the repainting to paintComponent.
You shouldn't override JFrame in this case, and you usually shouldn't have to.
You should override the paintComponent method, not the paint method.
I don't think you're double-buffering correctly, but I can't help you there.
Overriding the getPreferredSize method of JPanel is handy if you want to control its size, but it's not even necessary in this case, because adding it to the JFrame will automatically size it for you.
There's a lot of stuff that goes on in Swing behind the scenes, and it can get confusing because normally you have to say stuff explicitly, but keep playing with this example, and you should be safe for a while.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.*;
class Success extends JPanel implements ActionListener{
private final Timer timer = new Timer(20, this); // Create a timer that will go off every 20 ms
Line2D horizontalLine; // Declare your variables here, but don't initialize them
Line2D verticalLine; // That way, they can be accessed later in actionPerformed and repaint
// You might want to try frame.setResizable(false) if you want your frame
// and your panel to stay the same size.
private final Dimension prefPanelSize = new Dimension(450, 450);
public Success(){
super(); // Call the constructor of JPanel, the class this subclasses.
JButton button =new JButton("press");
this.add(button);
this.setSize(prefPanelSize);
horizontalLine = new Line2D.Float(0, 40, prefPanelSize.width, 40);
verticalLine = new Line2D.Float(prefPanelSize.width / 2, 0,
prefPanelSize.width / 2, prefPanelSize.height);
timer.start(); // Start the timer
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // fixes the immediate problem.
Graphics2D g2 = (Graphics2D) g;
g2.draw(horizontalLine);
g2.draw(verticalLine);
}
#Override
public Dimension getPreferredSize()
{
return prefPanelSize;
}
public static void main(String []args){
Success s = new Success();
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setSize(new Dimension(450, 450));
frame.add(s);
}
// This method is called ever 20 ms because of the timer.
#Override
public void actionPerformed(ActionEvent e) {
int currWidth = getWidth();
int currHeight = getHeight();
horizontalLine.setLine(0, 40, currWidth, 40);
verticalLine.setLine(currWidth / 2, 0, currWidth / 2, currHeight);
}
}
I'm writing a custom component that displays some bioinformatics data, and I'd like to be able to show additional information about the location the mouse is at when the user holds down a certain key. This seems like an obvious job for a tooltip, but there are a few problems that seem to be preventing this from working. First, I want to have the tooltip follow the mouse and change its text dynamically. This works somewhat by overriding getToolTipText and getToolTipLocation for the component, but the tooltip flickers as the mouse position is updated and doesn't display over the sub-components (it's a JPanel with some JTextPanes inside it). I also don't think there's any way to make it display immediately without a call to the ToolTipManager, which I believe would change the delay for all other components.
It looks like there are workarounds for some of these problems, but they're rather complicated and inelegant so I'm thinking a good solution would be to just create my own component, fill it with the relevant information and show it myself. However, this needs to be some kind of top-level component because it needs to be able to extend slightly beyond the borders of the parent component or even the containing JFrame and be drawn over everything else. The only objects I know of that have this functionality outside of JToolTip are JFrame and JDialog, which have borders with titles and close buttons which I don't want. Is there some way to accomplish this?
One option is to use a glass pane. In this case your tooltip won't be able to go outside of the frame, but you can easily position it relative to how near it is to a side of the frame. Some example code that draws a bubble (which you can fill with text in the paint method) that follows the mouse.
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setSize(new Dimension(500, 500));
JPanel glassPane = new JPanel();
glassPane.setOpaque(false);
glassPane.setLayout(null);
frame.setGlassPane(glassPane);
frame.getGlassPane().setVisible(true);
final MyInfoBubble mib = new MyInfoBubble();
mib.setBounds(10, 30, 100, 50);
((JPanel)frame.getGlassPane()).add(mib);
frame.getContentPane().addMouseMotionListener(new MouseMotionAdapter() {
public void mouseMoved(MouseEvent me) {
mib.setBounds(me.getPoint().x, me.getPoint().y, 100, 50);
}
});
((JPanel)frame.getGlassPane()).validate();
((JPanel)frame.getGlassPane()).repaint();
frame.setVisible(true);
}
static class MyInfoBubble extends JPanel
{
public MyInfoBubble()
{
setVisible(true);
}
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setColor(Color.BLUE);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 20, 20);
}
}