I am having issues with a small project im working on. I am trying to create a Moveable message panel when holding down the mouse button but i am stuck on one part.
I want to place the A small panel with a size of 50x30 pixels that contains the message "java" in it and have this small panel in a larger panel and place that panel into my JFrame.
However, when i do so the message "java" disappears and only the the small panel in the larger panel appears. I added borders to my panels to make sure that my panels were actually visible. Please help and here is my code:
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
public class MovingPanel extends JFrame {
private String message;
private int x = 100;
private int y = 100;
public MovingPanel() {
JPanel panel = new JPanel();
MessagePanel p1 = new MessagePanel("Java");
panel.setBorder(new LineBorder(Color.RED, 2));
panel.setLayout(null);
p1.setLocation(x, y);
p1.setSize(50, 30);
p1.setBorder(new LineBorder(Color.BLACK, 2));
p1.setLayout(new BorderLayout());
panel.add(p1);
add(panel);
}
public static void main(String[] args) {
MovingPanel frame = new MovingPanel();
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setTitle("Test Message Panel");
frame.setVisible(true);
}
class MessagePanel extends JPanel {
public MessagePanel(String s) {
message = s;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString(message, x + 20, y + 10);
}
}
}
Maybe you can try to use a simple JLabel component instead of your "MessagePanel".
First thing you need to understand is this.
The second and third arguments of this g.drawString(message, x + 20, y + 10); method are the x and y location of the panel.
With the above being said, you have to remember that it is the x and y location of the containing panel, which is MessagePanel.
You have the size of your MessagePanel object set at 50, 30, yet you are trying to access a point 120 (x + 20) and 110 (100 + 10), which does not exist since you size the size of the panel.
So now that's understood, let's say you want to paint the message at the very left corner of the MessagePanel, so you try and do this g.drawString(message, 0, 0);. This still would show anything as the point starts from the bottom left corner of the message, so the message would actually be riding just above the visible area.
When drawing strings, you need to consider the FontMetrics, which allows you to get the size of the string you are trying to draw, so you can position the message exactly on the screen where you want it.
A simple fix would be just set an x and y a little above 0, 0, like 15, 15. Though this might get your message to draw, it wouldn't be centered. You can keep on changing and getting different numbers to check if it is aligned in the middle, but the proper way is to use FontMetrics
As a said a simple (but maybe not desired) fix is to just change this
g.drawString(message, x + 20, y + 10);
To
g.drawString(message, 15, 15);
And you will see the message.
Instead of what you are doing though, this is how I would do it.
Instead of using two panels, I would just use one - the one that's doing the painting.
Don't set the size of it, instead override getPrefferedSize inside that class, to whatever size you want the main panel to me.
When you draw, just draw a rectangle the size you want at the specified coordinates.
Also draw the message in the same paintComponent method.
call pack() on the JFrame.
If you do the above, there's no need to try and move the location of the MessagePanel. Instead move the x and y coordinates when you call repaint, You can have offsets for the message. Like
int boxX = 100;
int boxY = 100;
int messageOffset = 15;
Then you can paint like this
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawRect(boxX, boxY, 50, 30);
g.drawString(message, boxX + messageOffset, boxY + messageOffset);
}
Now in your action methods, just alter the boxX and/or boxY and call repaint.
Also, if you want a thicker line, look into Graphics2D API, you can setStroke.
Related
I need make a JTabbedPane like this (I made the image in Photoshop):
But in my look and feel (based on TabbedPaneUI: javax.swing.plaf.basic.BasicTabbedPaneUI) looks like this:
How can I do it?
I’ve tried change LAF properties, but I didn't find a solution.
If I use setBorder method the swing make this:
jtabbedpane1.setBorder(BorderFactory.createLineBorder(Color.WHITE, 1, true));
Java changed only the upper left corner as outer border as image above shows.
I need a solution that might use the Paint method on an extended JTabbedPane class, but I really don't know if this is correct or how do this.
I read the tutorial above and tried override paintComponent method in my extended JTabbedPane class, see:
public class MyTabbedPane extends JTabbedPane {
[...]
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.drawRoundRect(getX()-12, getY()-11, getWidth()-4, getHeight()-22, 6, 6);
}
}
The result:
https://i.imgur.com/YLXkVRS.jpg
Rounded corners are actually a boolean argument when instantiating a border, as can be seen here with BorderFactory.
So what we can do is something like this:
pane.setBorder(BorderFactory.createLineBorder(Color.DARK_GRAY, 2, true));
Where "true" refers to rounded corners.
If you are interested in customizing the border further, you will most likely have to paint it yourself, in which case I would look here for a further read.
Edit regarding your code:
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.YELLOW);
g.drawPolyLine(new int[]{getX(), getX() getX() + 12}, new int[]{getY() + 12, getY(), getY()});
g.drawPolyLine(.....); // next corner
g.drawPolyLine(.....); // next corner
}
etc. where you repeat for each corner that you want your L shape at.
Here is the start of an answer.
import javax.swing.*;
import java.awt.Dimension;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.plaf.metal.MetalTabbedPaneUI;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Insets;
public class Bordered{
public static void main(String[] args){
JFrame frame = new JFrame("border check");
JPanel content = new JPanel();
JTabbedPane tabs = new JTabbedPane();
JPanel one = new JPanel();
one.add(new JLabel("first tab"));
one.setOpaque(true);
one.setBackground(Color.WHITE);
JPanel two = new JPanel();
two.add(new JLabel("second tab"));
tabs.add("one", one);
tabs.add("two", two);
tabs.setUI( new MetalTabbedPaneUI(){
#Override
protected void paintContentBorder(Graphics g, int placement, int selectedIndex){
int width = tabPane.getWidth();
int height = tabPane.getHeight();
Insets insets = tabPane.getInsets();
Insets tabAreaInsets = getTabAreaInsets(placement);
int x = insets.left;
int y = insets.top;
int w = width - insets.right - insets.left;
int h = height - insets.top - insets.bottom;
y += calculateTabAreaHeight(placement, runCount, maxTabHeight);
h -= (y - insets.top);
//g.fillRoundRect(x, y, w, h, 5, 5);
}
});
tabs.setPreferredSize(new Dimension(400, 200));
content.add(tabs);
frame.setContentPane(content);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Somethings to note, The inner panel the ones holding the jlabel have square corners. I've shown this by making one white. I've taken some of the boundary code from BasicTabbedPaneUI source code.
They really did not make this easy to manage, but looking at the source for the MetalTabbedPaneUI you can see they draw each border as a line, and it would need to be modified to draw a curve at the ends.
I am having an issue getting this code to run properly. It compiles and initially the frame displays properly. The problem is that when I manually re-size the frame by either maximizing or by dragging the side of the frame over, the text disappears. I am using jGRASP, not sure if that is the issue or not. The code seems to make sense to me, and like I said, it compiles(I know that does not necessarily make it right). I'm still a newbie at this so if anyone can point me in the right direction I would be very appreciative.
import javax.swing.*;
import java.awt.*;
public class JFontSizes extends JFrame {
int x = 5;
int y = 50;
String homework = "This is the first homework assignment";
public JFontSizes() {
super("Increasing Font Sizes");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics brush) {
super.paint(brush);
// This works sometimes. I am not sure if it is a jGRASP issue or something else.
// If I resize the frame, the text disappears, and I cannot get the text to start at the top of the frame
for(int n = 6; n<= 20; ++n) {
brush.setFont(new Font("Serif", Font.PLAIN, n));
brush.drawString(homework, x, y);
y += 15;
}
}
public static void main(String[] args) {
JFontSizes frame = new JFontSizes();
frame.setSize(400, 500);
frame.setVisible(true);
}
}
When first time paint() is called the value of y was 5. And it is incremented in a loop. So that before leaving paint() its value will be 275.
But when you resize your frame paint() is called again and this time the value of y is 275 and when brush.drawString(homework, x, y); is called the homework is printed at 275px bottom from top left corner.
So what you need to do is re-initialize y every time :
public void paint(Graphics brush) {
y = 50;
....
Edit :
As commented by camickr you should override paintComponent(...) instead of paint(...) until you have some specific reason to override paint().
And you mean you are not able to print text at top (even in beginning) then it is because you had initialized y with 50. Which means the text will be drawn at 50px from top.
I have a small application that should demonstrate how the opaque property works in Swing.
however, what throws me off is the order in which paintComponent() is called. I'd come to think components are drawn in the order they were added (whats added first, gets drawn first) however in this example it appears that paintComponent() methods are drawn in reverse order(what's added last got painted first)
can someone explain this behavior, thanks
public class TwoPanels {
public static void main(String[] args) {
JPanel p = new JPanel();
// setting layout to null so we can make panels overlap
p.setLayout(new BorderLayout());
CirclePanel topPanel = new CirclePanel("topPanel1");
// drawing should be in blue
topPanel.setForeground(Color.blue);
// background should be black, except it's not opaque, so
// background will not be drawn
topPanel.setBackground(Color.black);
// set opaque to false - background not drawn
topPanel.setOpaque(false);
topPanel.setBounds(50, 50, 100, 100);
// add topPanel - components paint in order added,
// so add topPanel first
p.add(topPanel);
CirclePanel bottomPanel = new CirclePanel("buttomPanel1");
// drawing in green
bottomPanel.setForeground(Color.green);
// background in cyan
bottomPanel.setBackground(Color.cyan);
// and it will show this time, because opaque is true
bottomPanel.setOpaque(true);
bottomPanel.setBounds(30, 30, 100, 100);
// add bottomPanel last...
p.add(bottomPanel);
// frame handling code...
JFrame f = new JFrame("Two Panels");
f.setContentPane(p);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
// Panel with a circle drawn on it.
private static class CirclePanel extends JPanel {
String objName;
public CirclePanel(String objName) {
this.objName = objName;
}
// This is Swing, so override paint*Component* - not paint
protected void paintComponent(Graphics g) {
System.out.println(objName);
// call super.paintComponent to get default Swing
// painting behavior (opaque honored, etc.)
super.paintComponent(g);
int x = 10;
int y = 10;
int width = getWidth() - 20;
int height = getHeight() - 20;
g.fillArc(x, y, width, height, 0, 360);
}
}
}
From Swing Internals: Paint Order:
What's going wrong under the hood?
A container holds an array with all child components. For painting, Swing (more precise JComponent#paintChildren()) iterates over the array in reverse order - this means the first added component will be painted at last. Z-order modifies the child position within this array. In case that the layout manager makes use of Container#getComponents() (as many Swing core layout managers do) there's no guarantee that the array order represents the order in which the components have been added to the container.
Generally in Swing you can specify the paint order by applying component Z-Order (see Container#setComponentZOrder). This method is useful as long as you're using a null layout or a layout manager which makes use of constraints.
The disadvantage of using #setComponentZOrder is that it can affect component position.
I have a little problem, I need to add a ToolTipText to JPanel. How should I do this?
I want to have a tooltip when I have mouse over the circle.
This is part of my code.
JPanel component1 = new JPanel();
JPanel component11 = new JPanel();
okno.add(component1,"align left,cell 0 0, h 75!, grow,wrap");
component1.setLayout(new MigLayout("","[][grow][grow]", "[grow]"));
component1.add((okno.add(creLab("Kraj", i, czcionka, etykietki))),"left align, cell 0 0");
component1.add(t1,"cell 1 0,grow");
//component1.add(new circle1(),"right align, cell 2 0,h 50!, w 53!, gapleft 50, wrap");
component1.add(component11," right align, cell 2 0, h 30!, gapleft 300, wrap");
component11.setLayout(new MigLayout("","[]","[]"));
component11.add(new circle1(),"cell 0 0,h 50!, w 50!, dock north");
component11.setToolTipText("<html>W polu obok wpisz kraj pochodzenia towaru</html>");
I add also code of circle1:
class circle1 extends Applet{
public void paint(Graphics g){
setForeground(Color.yellow);
g.drawOval(0, 0, 50, 50);
g.fillOval(0, 0, 50, 50);
g.setColor(Color.black);
g.drawString("Jak", 14, 14);
g.drawString("wpisac", 3, 28);
g.setColor(Color.red);
g.drawString("kraj?", 14, 42);
//g.drawString(arg0, arg1, arg2)
}
}
Take a look at JComponent#getToolTipText(MouseEvent)
This will allow you to determine what text to return based on the location of the mouse.
It's difficult to determine for your code snippet, exactly where the circle is been drawen, but I would avoid drawing directly to the surface of the applet, but instead use a custom component (like a JPanel) instead (overriding its paintComponent method). This I would then either add to the applet or to the control panel.
This way your going to avoid issues with the mouse events been consumed
I would also take a look at Ellipse2D, which can be used to determine if the ellipse contains a given point
The first thing is to identify when the mouse is inside the circle. To do that you could verify the mouse position on a mouseMotionlister according to the circle area
http://www.java2s.com/Code/JavaAPI/javax.swing/JPaneladdMouseMotionListenerMouseMotionListenerlis.htm
Once you identify this situation you could proceed to change the tooltip
See Playing With Shapes. You can create a JLabel with a ShapeIcon. Then you just use the setToolTipText() method of the JLabel. You can then add the label to the panel like any other component.
Now that you can use a component to represent a Shape there is no need to do custom painting. Just create a panel add add components to the panel. You can also create JLabels for all your text strings.
Don't do custom painting, unless you have a good reason to do so.
I create a window with a panel with custom panel class and paint circles on it based on array data. How do I get both sets of circles to stay on screen?
import java.awt.Color;
import java.awt.*;
import javax.swing.*;
class DataPaint extends JPanel {
static int offsetY = 0;
int leftX = 20;
int[] guessColours;
Color purple = new Color(155, 10, 255);
Color pink = new Color(255, 125, 255);
Color[] Colours = { Color.blue, Color.cyan, Color.green, Color.orange,
pink, purple, Color.red, Color.yellow };
public DataPaint() {
setBackground(Color.WHITE);
}
public void paintClues(int[] guessColours) {
this.guessColours = guessColours;
offsetY += 30;
}
// naive attempt to make it work !!!!
// what is diff between paintComponent and paint?
public void update(Graphics g) {
paintComponent(g);
}
// paint circles based on array data
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < guessColours.length; i++) {
g2.setPaint(Colours[guessColours[i]]);
g2.fillOval(leftX + (i * 30), offsetY, 20, 20);
}
}
// create window with panel and paint circles on it based on array data
public static void main(String args[]) {
JFrame frame = new JFrame("data paint");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 300);
DataPaint panel = new DataPaint();
frame.add(panel);
frame.setVisible(true);
int[] cols = { 2, 4, 5, 3, 6 };
int[] cols2 = { 1, 3, 7, 3, 4 };
// the second call replaces the first call on the panel?
panel.paintClues(cols);
panel.paintClues(cols2);
}
}
You don't stop it. The way Swing, and most GUI frameworks, work is that the paintComponent method or its analogue is always supposed to draw everything from scratch.
There are several reasons for this. One is is that if the window were resized, or any other sort of layout change occurred, or if the data-set being drawn changed in a complex way, you will need to be able to redraw anyway. Also, some window systems do not even store what is drawn in the window permanently, so you need to be able to redraw if your window is covered then uncovered. It is possible to have a component which has a permanent image that you can draw into, but that is not the usual way to do things and is less efficient unless you're writing, say, a paint program.
Change your data structures so that you keep all the information, and write your paintComponent so that it draws everything you want on screen every time it is called.
(There are refinements to make this efficient for partial updates of complex graphics, but you don't need to worry about those yet, as this is such a simple case. If you did need to do this, you would ask Swing to repaint a small region of your component (such as with JComponent.repaint(Rectangle r); it would then automatically prohibit drawing to areas outside that region while it calls your paintComponent method. That works fine to prevent flicker and save some filling work; then if it really matters for efficiency, inside paintComponent you compare that clip region (Graphics.getClip()) to what you're drawing and skip everything that doesn't intersect. But you really shouldn't worry about that yet for this simple case. First get your code working and using Swing correctly, then optimize if it matters.)
In your particular example, which I take it is intended to be a Mastermind game (you should mention that up front to help us read your code), put cols and cols2 into an int[][] field of DataPaint, and then use a loop inside of paintComponent to read every sub-array in that array and paint all of them.