I'm implementing a simple chat application, using Java. I want my chat application to have the "bubble" message style like modern message apps, so I've built 2 classes LeftArrowBubble and RightArrowBubble which extend JPanel to illustrate sender & receiver bubbles, like this:
This is the code for my LeftArrowBubble class (quite alike for RightArrowBubble):
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JPanel;
/**
* #author harsh
*/
public class LeftArrowBubble extends JPanel {
private static final long serialVersionUID = -5389178141802153305L;
private int radius = 10;
private int arrowSize = 12;
private int strokeThickness = 3;
private int padding = strokeThickness / 2;
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(0.5f, 0.8f, 1f));
int x = padding + strokeThickness + arrowSize;
int width = getWidth() - arrowSize - (strokeThickness * 2);
int bottomLineY = getHeight() - strokeThickness;
g2d.fillRect(x, padding, width, bottomLineY);
g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2d.setStroke(new BasicStroke(strokeThickness));
RoundRectangle2D.Double rect = new RoundRectangle2D.Double(x, padding, width, bottomLineY, radius, radius);
Polygon arrow = new Polygon();
arrow.addPoint(20, 8);
arrow.addPoint(0, 10);
arrow.addPoint(20, 12);
Area area = new Area(rect);
area.add(new Area(arrow));
g2d.draw(area);
}
}
Now I have a JFrame window with a JScrollPane on it, which looks like this:
What I want to do now is when I click on that CreateNewBubble button, a new Left(or Right)ArrowBubble JPanel will be created & displayed inside that JScrollPane (and this JScrollPane will be vertical scrollable if there're more bubbles inside of it). I've already tried this way:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
BubbleTest.LeftArrowBubble leftArrowBubble = new BubbleTest.LeftArrowBubble();
jScrollPane1.add(leftArrowBubble);
}
But it didn't work as I expected: nothing shows up in the JScrollPane after clicking the button!
I've been stuck at this problem for hours, really appreciate if you guys can help!
You can't use .add that way on a JScrollPane. A JScrollPane can only scroll a single component, which is set by either passing it to its constructor, or by calling .setViewportView.
Instead, create a separate container for the bubbles, such as a vertical Box, and set that as the single component scrolled by the scroll pane:
Box box = new Box(BoxLayout.Y_AXIS);
JScrollPane jScrollPane1 = new JScrollPane(box);
When you add a bubble, add it to the box (and call .revalidate() to lay it out):
box.add(leftArrowBubble);
box.revalidate();
Edit: Also, your bubbles will not, by default, have any size, unless you give them a size such as by calling setPreferredSize or by overriding getPreferredSize or by putting components inside them.
With JScrollPane you should always add components to the scroll pane's JViewPort. Look at the documentation here, it explains the concept behind the class rather well.
Short summary: A JScrollPane holds the scroll bars and a view port. The view port is a component that displays only a portion of its content - in this case the part that is visible on screen. The scroll bars tell the view port which portion to show.
Related
Just trying to draw some lines to the screen.
I've checked to ensure all the relevant code's being run
I've tried calling repaint (and ensuring that's being run)
Since this is a JSplitPane, the layout must be the JSplitPane layout
I'm setting the color to ensure it isn't drawing using the background color.
I've checked the height and width to ensure its size isn't 0 or something
I've tried drawing text as well; same result
I've changed the coordinates all over the place, tried both arbitrary and proportional values
Or at least I think. Swing is unintuitively quirky. I'd use AWT, but I need the specificity Swing offers. Anyway, the code. It's just a split pane, which is actually displaying - resizable and all - but the contents of the top pane (the only one I've attempted to put anything in) don't show.
package derange;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
public class Derange {
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("Derange");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Display the window.
frame.setExtendedState(frame.getExtendedState() | JFrame.MAXIMIZED_BOTH);
frame.pack();
frame.setVisible(true);
//Create a split pane with the two scroll panes in it.
PanelScore scorePane = new PanelScore();
JScrollPane instrumentPane = new JScrollPane();
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
scorePane, instrumentPane);
splitPane.setOneTouchExpandable(true);
splitPane.setDividerLocation((frame.getHeight() / 4) * 3 );// Three-quarters of the way down
splitPane.setDividerSize(20);
//Provide minimum sizes for the two components in the split pane
Dimension minimumSize = new Dimension(frame.getWidth(), frame.getHeight()/ 2);//width, height
scorePane.setMinimumSize(minimumSize); //Score takes up at least half the screen
instrumentPane.setMinimumSize(new Dimension(0,0));//no minimum size on the instrument panel; collapsible
frame.getContentPane().add(splitPane);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
.
package derange;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.Insets;
import javax.swing.JScrollPane;
#SuppressWarnings("serial")//wtf is this needed for?
public class PanelScore extends JScrollPane{
public int strings = 6;
public void drawStaffTablature(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.black);
int xStart = 30;//insets.left;
int xEnd = getParent().getWidth() - 30;
int yCoord = this.getHeight() / 2;
System.out.println(this.isShowing());
//Space between tablature lines
int lineSpacing = 15;
//Space between staffs.
int staffSpacing = 60;`enter code here`
for(int x = 0; x < strings; x++){
g2d.drawLine(xStart, yCoord + (lineSpacing * x), xEnd, yCoord + (lineSpacing * x));
//System.out.println("String: " + (x + 1));
g.drawString("Test", xStart, yCoord); //change the co-odrinates
}
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
drawStaffTablature(g);
}
}
Short answer, don't extend from JScrollPane, a JScrollPane contains single component known as a JViewport, which covers the most of the scroll pane (the rest is taken up by the JScrollBars
Instead, try extending from something like JPanel.
I'd also advise you against using anything like int xEnd = getParent().getWidth() - 30; within your paint code, the Graphics context is translated to the components location, making the top/left corner 0x0 and clipped to the components current width and height
So I have an image being drawn inside a JPanel which itself is added to a JFrame. However when the Image is first drawn it appears to be very small. I'm not sure if this is a problem with the panel or a problem with the image. It is illustrated below:
![enter image description here][1]
I have drawn a rectangle around the image.
Now the JPanel is supposed to be contained within the JFrame. The JFrame is not supposed to be coloured in as is seen above. The JPanel is meant to be about a quarter of the size of the JFrame and the image is supposed to take up almost all of the JPanel.
Could you please tell me if it's the image which is the problem or the Panel. Sorry if it seems obvious.
Awaiting SSCCE
I have no idea what you are doing based on the few random lines of code you posted. Nowhere in the code do you actually create/read an image.
As far as I know a Mandelbrot Set is actually done by painting code. If so the problem is probably that you did not override the getPreferredSize() (don't use the setSize() method) of you painting panel to return the size of the image you are painting. Read the section from the Swing tutorial on Custom Paining for more information.
Or if you are actually using an existing image then read the section from the Swing tutorial on How to Use Icons for working examples of using images.
Also, components should be added to the frame BEFORE you make the frame visible.
If you need more help then post a proper SSCCE that demonstrates the problem.
BufferedImage image = ImageIO.read(file); //Read image through BufferedReader
labelimage.setIcon(new ImageIcon(image.getScaledInstance(labelimage.getWidth(), labelimage.getHeight(), image.SCALE_SMOOTH))); // This line will automaticallically set Image size equal to size of Jlabel
how about adding a label inside the panel, and draw the image using setIcon(..)
usually I use the following class to make the image fit to the label size (I make my label has static size - not resizable).. you might wanna modify it to suit your need..
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.net.URL;
import javax.swing.ImageIcon;
public class CustomImageIcon extends ImageIcon {
private BufferedImage dest;
public CustomImageIcon(String filename) {
super(filename);
}
public CustomImageIcon(Image image) {
super(image);
}
public CustomImageIcon(URL location) {
super(location);
}
#Override
public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
if(c!=null)
dest = new BufferedImage(c.getWidth(), c.getHeight(), BufferedImage.TYPE_INT_ARGB);
else dest = new BufferedImage(getIconWidth(), getIconHeight(), BufferedImage.TYPE_INT_ARGB);
ImageObserver imgObs = getImageObserver();
if(imgObs==null) imgObs = c;
int width;
int height;
if(c!=null)
{
width = c.getWidth();
height = c.getHeight();
}
else
{
width = getIconWidth();
height = getIconHeight();
}
g.drawImage(dest, 0, 0, c);
g.drawImage(
getImage(),
0,
0,
width,
height,
imgObs);
}
}
I am sorry for the lack of a better title, but I have no idea how to specify the error further, since I don't understand its nature. Maybe someone can edit it, when the problem is understood.
I am writing an application in which the user can add icons into a text field. I obviously picked a JTextPane to display the text and the icons. After playing around with the insertComponent() function of the class I ran into some weird layout problems, so I decided to lookup the tutorial at oracle.com. After looking at the source code of the example, I decided to do the same and also use styles to add components to the underlying StyledDocument. It was when I started the first test run, when I discovered, that the layout problems stayed the same.
So, what is actually happening?
What I intended the text pane to show is "abcOdefO", but as you can tell by the screenshot, the two icons (circles) have some space to the right of them. I want the icon to be treated as a slightly larger character, so it should only occupy as much space as it needs, not (availableSpace / numberOfIcons), which seems to be what it actually occupies.
When typing another character at the caret position:
This is even weirder. If the icons have MouseListeners, all 4 visible circles trigger the event. If I drag the frame to another window or minimize and restore it, the weird parts vanish and the frame looks like the first image (except for the additional character). So I guess, this part of my problem is fixed with a call to repaint() at the correct location - but where?
This is the code that produces the images seen above:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;
public class TextPaneTestPanel extends JPanel {
private class myIcon extends JPanel {
private final int side;
private final int padding = 1;
public myIcon(int size) {
this.side = size - 2 * padding;
this.setSize(size, size);
this.setPreferredSize(getSize());
this.setMinimumSize(getSize());
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(2));
g2d.drawOval(padding, padding, side, side);
}
}
private final JTextPane textPane;
public TextPaneTestPanel() {
textPane = new JTextPane();
StyledDocument doc = textPane.getStyledDocument();
Style def = StyleContext.getDefaultStyleContext().getStyle(StyleContext.DEFAULT_STYLE);
StyleConstants.setFontFamily(def, "Monospaced");
Style regular = doc.addStyle("regular", def);
try {
doc.insertString(0, "abc", regular);
Style s1 = doc.addStyle("icon1", regular);
StyleConstants.setComponent(s1, new myIcon(20));
doc.insertString(3, " ", s1);
doc.insertString(4, "def", regular);
Style s2 = doc.addStyle("icon2", regular);
StyleConstants.setComponent(s2, new myIcon(20));
doc.insertString(7, " ", s2);
} catch (BadLocationException e1) {
e1.printStackTrace();
}
this.setLayout(new GridBagLayout());
this.add(textPane, new GridBagConstraints(0, 0, 1, 1, 1, 1, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
new Insets(4, 4, 4, 4), 0, 0));
}
public static void main(String[] args) {
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setResizable(false);
TextPaneTestPanel panel = new TextPaneTestPanel();
frame.getContentPane().add(panel);
frame.setSize(300, 100);
frame.setVisible(true);
}
}
To sum up my questions:
What causes the space to appear after an icon?
Where do I add the repaint() or revalidate() to fix the problem seen in image #2?
P.S.: I know, that my "icon" does not implement Icon, but it shouldn't be necessary, since JTextPanes can handle all sorts of Components.
1.What causes the space to appear after an icon?
Well, you have the following code:
this.setMinimumSize(getSize());
What about the maximum size?
How to fix the problem seen in image #2?
Custom painting is done by overriding the paintComponent() method, not the paint() method and don't forget to invoke super.paintComponent.
An Icon would be more appropriate here since all you are doing is custom painting. Or even a JComponent, but not a JPanel which is a Container used for holding other components. Plus is you use an Icon you don't have the size problems that you have with a panel, or JComponent since that method is specifically implemented as part of the interface.
I have the following code. Basically I have a frame which has a background image. I also have three panels within the frame: panels 1, 2 and 3. 2 & 3 work fine as I haven't subclassed them. However, panel 1 as soon as I subclassed it i.e. put the logic inside the paintComponent method of JPanel stopped working as that method is never called and foo is never printed. I'm not able to figure out why. Would appreciate your help. I've tried a few suggestions from other similar threads and they haven't helped.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) throws IOException {
JFrame.setDefaultLookAndFeelDecorated(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int fooPanelX = 5;
int fooPanelY = 160;
int fooPanelWidth = 470;
int fooPanelHeight = 305;
int bar0PanelX = 5;
int bar0PanelY = 550;
int bar0PanelWidth = 230;
int bar0PanelHeight = 210;
int bar1PanelX = bar0PanelX * 2 + bar0PanelWidth + bar0PanelX;
int bar1PanelY = bar0PanelY;
int bar1PanelWidth = bar0PanelWidth;
int bar1PanelHeight = bar0PanelHeight;
JPanel panel1 = new Panel1(fooPanelX, fooPanelY, fooPanelWidth, fooPanelHeight);
JPanel panel2 = new JPanel();
panel2.setBackground(Color.WHITE);
panel2.setLocation(bar0PanelX, bar0PanelY);
panel2.setSize(bar0PanelWidth, bar0PanelHeight);
panel2.setOpaque(false);
panel2.setBorder(BorderFactory.createLineBorder(Color.WHITE));
panel2.setBounds(bar0PanelX, bar0PanelY, bar0PanelWidth, bar0PanelHeight);
JPanel panel3 = new JPanel();
panel3.setBackground(Color.WHITE);
panel3.setLocation(bar1PanelX, bar1PanelX);
panel3.setSize(bar1PanelWidth, bar1PanelHeight);
panel3.setOpaque(false);
panel3.setBorder(BorderFactory.createLineBorder(Color.WHITE));
panel3.setBounds(bar1PanelX, bar1PanelY, bar1PanelWidth, bar1PanelHeight);
JLabel imagePanel = new JLabel(new ImageIcon(ImageIO.read(new File("image.png"))));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
System.exit(0);
}
}
});
frame.setContentPane(imagePanel);
frame.getContentPane().add(panel1);
frame.getContentPane().add(panel2);
frame.getContentPane().add(panel3);
frame.setLocation((int) (screenSize.getWidth() * 0.75),
(int) (screenSize.getHeight() * 0.25));
frame.pack();
frame.setVisible(true);
}
#SuppressWarnings("serial")
static class Panel1 extends JPanel {
int x, y, w, h;
public Panel1(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
#Override
public void paintComponent(Graphics graphics) {
System.out.println("foo");
super.paintComponents(graphics);
setBackground(Color.WHITE);
setLocation(x, y);
setSize(w, h);
setOpaque(true);
setBorder(BorderFactory.createLineBorder(Color.WHITE));
}
}
}
Update: If you aren't able to find the issue then please could you provide me with an alternative way of doing the following. I need a frame with a background image and three panels on top of the background image. The three panels have to have pixel perfect locations and sizes on the background image to look right. That's it pretty much. I'll be repainting the three panels but the background image will remain the same.
Well, the problem is that you are not using an appropriate LayoutManager.
JLabel does not come with any LayoutManager by default. So when you add your Panel1, it has a size of 0x0 and is located in (0,0) and since no LayoutManager will change that, it will keep that size and location. With empty bounds, your component is never painted, hence your paintComponent method is never called.
Now, you should NEVER do this in paintComponent:
setBackground(Color.WHITE);
setLocation(x, y);
setSize(w, h);
setOpaque(true);
setBorder(BorderFactory.createLineBorder(Color.WHITE));
Do that in the constructor or some other method. paintComponent is meant for "painting a component", not changing its properties.
I decided to tackle the problem in a very different way. This is how I did it. I startedc completely from scratch with my code. I created a JFrame instance and a Canvas instance (the canvas was subclassed). In the canvas I used drawImage() to apply the background image. Then for each of the three areas that I wanted to animate on the background image, instead of creating three JPanels, I simply used fillRect() within the canvas to fill the right areas on the image. That's it. Nice and simple. The repaint() every second does flickr on the three areas and that's the next challenge. I'm guessing I have to use double buffering but it's not something I've used before so I'll look into that next. Anyway, using a single canvas in place of three JPanels proved a heck of a lot simpler and the reason I was able to do that was because the background image provided everything else visually. All I had to do was drawImage() and fillRect(). Thanks for all contributions.
Update: I have now completed this task. There was one thing I changed about the above. While attempting to double buffer with Canvas I had a few issues: the usual "component must have valid peer" exception. While looking into that I learnt that one should not use Canvas in Swing and that the practice of using it was mixing AWT and Swing. So I swapped it out for JComponent (as I didn't need anything that JPanel offered). And as Swing is double buffered by default my work was complete. No flicker and simplified code.
I have picture as a JLabel and what I want to do is when I click the JLabel, there would be another Jlabel in a form of rectangle would appear.
I tried using paintComponent or paint and unfortunately, it doesn't work for my program. So I have to think that there are other ways, other than paintComponent and/or paint.
The aim is tag to a certain part of an image with a name, like in Facebook.
Wow.
Based on Pace's description of the problem, you're going to have to do the following (I think):
The picture will have to be painted directly on a JPanel. This is so when someone clicks on the picture, you can get the mouse x and y coordinates through the JPanel action listener.
You will then create a JDialog with a transparent JPanel that has a border, a text box for the name, and an OK button together. (Might as well put everything together in one dialog window.) The JDialog will be movable, but you're going to have to create a JDialog listener that keeps track of the x and y coordinates of the top left edge or the center of the transparent JPanel in the JDialog.
The JDialog JPanel won't really be transparent. You'll have to create the illusion of transparency by noting the position of the JDialog JPanel on the picture JPanel, and copying the part of the image from the picture JPanel to the JDialog JPanel.
The rest should be rather straightforward, compared to getting the JDialog to work properly.
Edited to add: Here's an extension of JPanel that will draw a picture directly on the JPanel and process mouse pressed events.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class PicturePanel extends JPanel {
private static final long serialVersionUID = 1L;
protected Image picture;
public PicturePanel(Image picture) {
this.picture = picture;
createPartControl();
}
protected void createPartControl() {
new JPanel();
int width = picture.getWidth(getParent());
int height = picture.getHeight(getParent());
addMouseListener(new CoordinateListener());
setPreferredSize(new Dimension(width, height));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = picture.getWidth(getParent());
int height = picture.getHeight(getParent());
g.drawImage(picture, 0, 0, width, height, null);
}
public class CoordinateListener extends MouseInputAdapter {
#Override
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
System.out.println("(" + x + ", " + y + ")");
}
}
}