My ultimate goal is to have a JTextArea with a background image. I found code online that showed me how to do this, but now I'm having an issue with the text being on top of the image.
This Is what I mean:
Is there any way I can add a sort of inward indent so that the text is not overlapping the edges of the image?
Here is the raw comment bubble image.
Here is the code:
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.GrayFilter;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class myBackgroundSample {
String file;
public myBackgroundSample(String i) {
file = i;
setItUp();
}
public void setItUp() {
final ImageIcon imageIcon = new ImageIcon(file);
JTextArea textArea = new JTextArea() {
Image image = imageIcon.getImage();
public void paint(Graphics g) {
setOpaque(false);
g.drawImage(image, 0, 0, this);
super.paint(g);
}
};
JFrame frame = new JFrame("Background Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane scrollPane = new JScrollPane(textArea);
Container content = frame.getContentPane();
content.add(scrollPane, BorderLayout.CENTER);
frame.setSize(400, 400);
frame.setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
String right = "chat1.jpg";
myBackgroundSample temp = new myBackgroundSample(right);
}
}
Use a custom border that extends AbstractBorder. Something like this:
Getting the exact shape & color is left as an exercise for the reader. :)
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.AbstractBorder;
class TextBubbleBorder extends AbstractBorder {
private Color color;
private int thickness = 4;
private int radii = 8;
private int pointerSize = 7;
private Insets insets = null;
private BasicStroke stroke = null;
private int strokePad;
private int pointerPad = 4;
RenderingHints hints;
TextBubbleBorder(
Color color) {
new TextBubbleBorder(color, 4, 8, 7);
}
TextBubbleBorder(
Color color, int thickness, int radii, int pointerSize) {
this.thickness = thickness;
this.radii = radii;
this.pointerSize = pointerSize;
this.color = color;
stroke = new BasicStroke(thickness);
strokePad = thickness/2;
hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int pad = radii + strokePad;
int bottomPad = pad + pointerSize + strokePad;
insets = new Insets(pad,pad,bottomPad,pad);
}
#Override
public Insets getBorderInsets(Component c) {
return insets;
}
#Override
public Insets getBorderInsets(Component c, Insets insets) {
return getBorderInsets(c);
}
#Override
public void paintBorder(
Component c,
Graphics g,
int x, int y,
int width, int height) {
Graphics2D g2 = (Graphics2D)g;
int bottomLineY = height-thickness-pointerSize;
RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(
0+strokePad,
0+strokePad,
width-thickness,
bottomLineY,
radii,
radii
);
Polygon pointer = new Polygon();
// left point
pointer.addPoint(
strokePad+radii+pointerPad,
bottomLineY);
// right point
pointer.addPoint(
strokePad+radii+pointerPad+pointerSize,
bottomLineY);
// bottom point
pointer.addPoint(
strokePad+radii+pointerPad+(pointerSize/2),
height-strokePad);
Area area = new Area(bubble);
area.add(new Area(pointer));
g2.setRenderingHints(hints);
Area spareSpace = new Area(new Rectangle(0,0,width,height));
spareSpace.subtract(area);
g2.setClip(spareSpace);
g2.clearRect(0,0,width,height);
g2.setClip(null);
g2.setColor(color);
g2.setStroke(stroke);
g2.draw(area);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JLabel l = new JLabel(
"The quick brown fox jumped over the lazy dog!");
l.setBorder(new TextBubbleBorder(Color.MAGENTA.darker(),2,4,0));
l.setOpaque(true);
l.setBackground(Color.BLACK);
JOptionPane.showMessageDialog(null, l);
}
});
}
}
You should use an Border for that, more specifictly you should use BorderFactory.createEmptyBorder(int top, int left, int bottom, int right):
textArea.setBorder(BorderFactory.createEmptyBorder(10,10,15,10));
You should also override paintComponent instead of paint. Also, use setRows() and setColumns() to set the size of the textArea, then you can use pack() instead of setSize(400,400) which is not recommended. See this example:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class Test extends JFrame {
class MyTextArea extends JTextArea {
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setColor(Color.PINK);
g2.setStroke(new BasicStroke(4));
g2.drawRoundRect(3, 3, getWidth()-7, getHeight()-7, 5, 5);
}
}
public Test() {
JPanel panel = new JPanel(new BorderLayout());
JTextArea textArea = new MyTextArea();
textArea.setRows(3);
textArea.setColumns(25);
textArea.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(textArea, BorderLayout.NORTH);
add(panel);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new Test();
}
}
To post code, indent every line by four spaces.
I assume you are overriding paintComponent()* for your JTextArea. If you are, make sure that it is transparent by adding
setOpaque(false);
*This also works if you override paint(), but as trashgod correctly states, that would interfere with paintBorder().
A better version of TextBubbleBorder.
https://gist.github.com/wenerme/6940534
pointer padding control
pointer side control
dynamic change
Related
I am writing a Java Swing application for data processing.
one of the functions I need to add is to visualize the data in a graphical way.
For this I want to use the Graphics2D class.
I have a GUI created, integrated my program and also a panel that draws the graphics using the Graphics2D class.
But my problem is that I can't figure out how to call the drawLine method after selecting and loading a file from the GUI
Below is short code example, showing my issue.
It just contains a basic GUI with 2 panels and a menu with load option to explain my problem:
In de MyFrame.java file, I put a comment at line 87 to show exactly where I am stuck.
The appl is based on 3 files:
main: here it creates an instance of MyFrame of the GUI
Myframe: creates the GUI and further process of data
MyPanel: makes a Jpanel of the Graphics2D with a base blue rectangle frame as start view.
If Anyone could give me a hint on how to call this drawLine method from outside the MyFrame() constructor...
I still don't fully understand the whole point on how to interact between classes...
here is a picture of what the GUI looks:
Thank you for helping me on this
public class main {
public static void main(String[] args) {
new MyFrame();
}
}
MyFrame.java:
public class MyFrame extends JFrame {
JTextComponent tc;
String fileName;
MyFrame() {
this.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
this.setLayout(null);
this.setBounds(0,0,464,312);
tc = new JTextPane();
tc.setBounds(0,520,450,50);
tc.setPreferredSize(new Dimension(450,50));
JScrollPane sp = new JScrollPane(tc);
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {tc.setText("loading"+"\n");
SDprocess();});
fm.add(loadItem);
mb.add(fm);
MyPanel p1 = new MyPanel();
p1.setBounds(0,0,450,200);
p1.setPreferredSize(new Dimension(450,200));
JPanel p2 = new JPanel();
p2.setBounds(0,200,450,50);
p2.setPreferredSize(new Dimension(450,50));
p2.add(sp);
this.setJMenuBar(mb);
this.add(p1);
this.add(p2);
this.setResizable(false);
this.setVisible(true);
}
public void SDprocess() {
File fr = null;
JFileChooser fc = new JFileChooser();
int result = fc.showOpenDialog(this);
if (result == JFileChooser.APPROVE_OPTION) {
fr = fc.getSelectedFile();
fileName=fr.getName();
System.out.println(fileName);
}
try {
Scanner sc = new Scanner(fr);
tc.setText(fileName +" loading\n");
while (sc.hasNextLine()) {
String line = sc.nextLine();
// ...
// rest of code to get the x and y data for drawing
// lines using drawLine(x1,y1,x2,y2) method.
//
// at this point I need to call this drawLine method but how ???
// i just don't know how to call this method from this point and how to
// and update the graphics panel p1 after adding the data....
}
sc.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
MyPanel.java:
public class MyPanel extends JPanel {
Graphics2D g2D;
MyPanel() {
this.setPreferredSize(new Dimension (450,200));
}
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
}
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Netbeans section.
Here's the revised GUI before "reading the file".
Here's the revised GUI after "reading the file".
I created an application model to hold the line segments. This model is passed to the drawing JPanel so that the line segments can be drawn in the paintComponent method of the drawing JPanel.
I cleaned up your GUI. I used Swing layout managers to create the GUI. I separated the creation of the JPanels from the creation of the JFrame so the code is easier for people to read and understand.
Here's the complete runnable code. I made the additional classes inner classes so I could post this code as one block.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
public class ExampleDrawingGUI {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> new ExampleDrawingGUI().new MyFrame());
}
public class MyFrame extends JFrame {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
JTextComponent tc;
MyPanel p1;
public MyFrame() {
super("My Frame");
this.model = new ExampleDrawingModel();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setJMenuBar(createMenuBar());
p1 = new MyPanel(model);
this.add(p1, BorderLayout.CENTER);
this.add(createTextPanel(), BorderLayout.SOUTH);
this.pack();
this.setLocationByPlatform(true);
// this.setResizable(false);
this.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar mb = new JMenuBar();
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
tc.setText("loading" + "\n");
model.readFile();
p1.repaint();
});
fm.add(loadItem);
mb.add(fm);
return mb;
}
private JPanel createTextPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
tc = new JTextPane();
tc.setPreferredSize(new Dimension(450, 50));
JScrollPane sp = new JScrollPane(tc);
panel.add(sp, BorderLayout.CENTER);
return panel;
}
public void repaint() {
p1.repaint();
}
}
public class MyPanel extends JPanel {
private static final long serialVersionUID = 1L;
private ExampleDrawingModel model;
public MyPanel(ExampleDrawingModel model) {
this.model = model;
this.setPreferredSize(new Dimension(450, 200));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g;
paintBorder(g2D);
for (LineSegment line : model.getLines()) {
Point startPoint = line.getStartPoint();
Point endPoint = line.getEndPoint();
g2D.drawLine(startPoint.x, startPoint.y, endPoint.x, endPoint.y);
}
}
private void paintBorder(Graphics2D g2D) {
int margin = 5;
int x1 = margin;
int x2 = getWidth() - margin;
int y1 = margin;
int y2 = getHeight() - margin;
g2D.setStroke(new BasicStroke(3f));
g2D.setPaint(Color.blue);
g2D.drawLine(x1, y1, x1, y2);
g2D.drawLine(x1, y1, x2, y1);
g2D.drawLine(x2, y1, x2, y2);
g2D.drawLine(x1, y2, x2, y2);
}
}
public class ExampleDrawingModel {
private List<LineSegment> lines;
public ExampleDrawingModel() {
this.lines = new ArrayList<>();
}
public void readFile() {
this.lines.clear();
// Here's where you'd read a file and create a list of lines.
lines.add(new LineSegment(new Point(100, 100), new Point(100, 150)));
}
public List<LineSegment> getLines() {
return lines;
}
}
public class LineSegment {
private final Point startPoint, endPoint;
public LineSegment(Point startPoint, Point endPoint) {
this.startPoint = startPoint;
this.endPoint = endPoint;
}
public Point getStartPoint() {
return startPoint;
}
public Point getEndPoint() {
return endPoint;
}
}
}
First off, some problems with your code:
class MyFrame extends JFrame {
//....
MyFrame() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null); // !! Good God, no don't do this!
this.setBounds(0, 0, 464, 312); // and don't do this
Avoid null layouts and setBounds like the plague as this makes for very inflexible GUI's that while they might look good on one platform look terrible on most other platforms or screen resolutions and that are very difficult to update and maintain. Instead you will want to study and learn the layout managers and then nest JPanels, each using its own layout manager to create pleasing and complex GUI's that look good on all OS's.
Also, don't forget to call pack() on your JFrame after adding components and before setting visible, in order for the layout managers to do their things.
And then:
class MyPanel extends JPanel {
// ...
Graphics2D g2D; //!! -- no, don't do this
If you create a Graphics or Graphics2D field, you are tempted to use it outside of a painting method, and this is a recipe for disaster since any Graphics obtained from a component is short lived and doing this risks creating a brittle graphic or throwing a NullPointerException
public void paint (Graphics g) {
g2D = (Graphics2D) g;
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
g2D.drawLine(5, 5, 445,5);
g2D.drawLine(445, 5, 445,195);
g2D.drawLine(445, 195, 5,195);
g2D.drawLine(5, 195, 5,5);
}
Don't override paint but rather paintComponent since this is less risky (paint has greater responsibilities that you don't want to mess with) and smoother animations if needed since paintComponent uses double buffering by default.
Also, you almost always should call the super's painting method in your own override, and so instead do:
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
Graphics2D g2D = (Graphics2D) g.create();
Now as for your actual problem, creating images and drawing after the GUI has been rendered, probably the easiest way to do this is to create a BufferedImage and draw with it in your GUI. You can do this easily by calling Graphics method drawImage(...). And you can pass a BufferedImage into your drawing JPanel any time it is needed. So for instance, your code could look something like...
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class Foo {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MainPanel mainPanel = new MainPanel();
MyMenu myMenu = new MyMenu();
myMenu.setMyPanel(mainPanel.getMyPanel());
myMenu.setMainPanel(mainPanel);
frame.add(mainPanel);
frame.setJMenuBar(myMenu);
frame.pack();
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
class MainPanel extends JPanel {
private static final int GAP = 5;
private JTextArea textArea = new JTextArea(4, 40);
private MyPanel myPanel = new MyPanel();
public MainPanel() {
textArea.setFocusable(false);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(myPanel);
add(scrollPane, BorderLayout.PAGE_END);
}
public MyPanel getMyPanel() {
return myPanel;
}
public void appendTextAreaText(String text) {
textArea.append(text);
}
public void setBuffImg(BufferedImage bImage) {
myPanel.setBuffImg(bImage);
}
}
class MyMenu extends JMenuBar {
private MainPanel mainPanel;
private MyPanel myPanel;
public MyMenu() {
JMenu fm = new JMenu("File");
JMenuItem loadItem = new JMenuItem("Load file");
loadItem.addActionListener(e -> {
// Emulate reading file here in a background thread
if (myPanel != null) {
int width = MyPanel.MY_WIDTH;
int height = MyPanel.MY_HEIGHT;
BufferedImage bImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = bImg.createGraphics();
// draw with g2 here using data from file
// emulating this:
g2.setColor(Color.RED);
float strokeWidth = (float) (2 + 6 * Math.random());
g2.setStroke(new BasicStroke(strokeWidth));
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
int x2 = (int) (MyPanel.MY_WIDTH * (1 + Math.random()) / 2);
int y2 = (int) (MyPanel.MY_HEIGHT * (1 + Math.random()) / 2);
g2.drawLine(5, 5, x2, y2);
g2.dispose();
myPanel.setBuffImg(bImg);
if (mainPanel != null) {
mainPanel.appendTextAreaText("adding image \n");
}
}
});
fm.add(loadItem);
add(fm);
}
public void setMyPanel(MyPanel myPanel) {
this.myPanel = myPanel;
}
public void setMainPanel(MainPanel mainPanel) {
this.mainPanel = mainPanel;
}
}
class MyPanel extends JPanel {
private static final int GAP = 5;
public static final int MY_WIDTH = 450;
public static final int MY_HEIGHT = 200;
private BufferedImage bImg = null;
// Graphics2D g2D; //!! -- no, never do this!!
MyPanel() {
// this.setPreferredSize(new Dimension(450, 200));
setBackground(Color.WHITE);
}
// better to override getPreferredSize
#Override
public Dimension getPreferredSize() {
return new Dimension(MY_WIDTH + 2 * GAP, MY_HEIGHT + 2 * GAP);
}
public void setBuffImg(BufferedImage bImg) {
this.bImg = bImg;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
// first call the super's method:
super.paintComponent(g);
if (bImg != null) {
g.drawImage(bImg, GAP, GAP, null);
}
Graphics2D g2D = (Graphics2D) g.create();
g2D.setStroke(new BasicStroke(1));
g2D.setPaint(Color.blue);
Rectangle rect = new Rectangle(GAP, GAP, getWidth() - 2 * GAP, getHeight() - 2 * GAP);
g2D.draw(rect);
}
// Don't override paint but rather paintComponent
// public void paint (Graphics g) {
}
I have a class called DrawRectangles that takes an array of integers.
What I am trying to do is go through the numbers in the array and for each one, create a new panel using the number in the array as the panel's width and X position.
Let's say I pass in [2, 4, 6, 8]. I want to create a new panel to add to the JFrame with each of these numbers.
So the first panel should start at position 2 and have a width of 2. I also have a random color generator which is supposed to create a new color for each panel. Here is what I have:
public class DrawRectangles {
JFrame frame;
DrawPanel panel;
Random randomGenerator = new Random();
int red = randomGenerator.nextInt(255);
int green = randomGenerator.nextInt(255);
int blue = randomGenerator.nextInt(255);
Color randomColor;
int[] newWidth;
DrawRectangles(int[] width){
this.newWidth = width;
}
public void setUpFrame(){
frame = new JFrame();
frame.setSize(500,100);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for(int x = 0; x < newWidth.length; x++){
panel = new DrawPanel(newWidth[x]);
frame.add(panel);
}
}
class DrawPanel extends JPanel{
int newWidth;
DrawPanel(int width){
this.newWidth = width;
System.out.println(newWidth);
}
public void paint(Graphics g) {
super.paint(g);
randomColor = new Color(red,green,blue);
g.setColor(randomColor);
g.fillRect(newWidth, 10, newWidth, 30);
}
}
}
You have a number of issues...
Frame uses a BorderLayout by default. This will only allow a single component to occupy any one of the pre-defined position available to it. This means that only the last pane will be visible.
You don't control the paint process. This means that every time randomColor changes values, all the components referencing it
will also be repainted with it on the next paint cycle.
You should be using paintComponent instead of paint
This is a basic example using a single DrawRectangle component to paint all the rectangles.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
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 DrawRectangles {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
DrawRectangles dr = new DrawRectangles(new int[]{2, 4, 6, 8});
dr.setUpFrame();
}
});
}
JFrame frame;
DrawPanel panel;
Random randomGenerator = new Random();
int[] newWidth;
DrawRectangles(int[] width) {
this.newWidth = width;
}
public void setUpFrame() {
frame = new JFrame();
frame.setSize(500, 100);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DrawPanel panel = new DrawPanel();
for (int x = 0; x < newWidth.length; x++) {
int red = randomGenerator.nextInt(255);
int green = randomGenerator.nextInt(255);
int blue = randomGenerator.nextInt(255);
panel.addRectangle(new Color(red, green, blue), newWidth[x]);
}
frame.setSize(200, 200);
frame.add(panel);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public class MyRectangle {
private Color color;
private Rectangle rectangle;
public MyRectangle(Color color, int width) {
this.color = color;
rectangle = new Rectangle(width, 10, width, 30);
}
public Color getColor() {
return color;
}
public Rectangle getRectangle() {
return rectangle;
}
public void draw(Graphics2D g2d) {
g2d.setColor(color);
g2d.fill(rectangle);
}
}
public class DrawPanel extends JPanel {
private List<MyRectangle> rectangles;
public DrawPanel() {
rectangles = new ArrayList<>();
}
public void addRectangle(Color color, int width) {
rectangles.add(new MyRectangle(color, width));
}
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g.create();
for (MyRectangle rect : rectangles) {
rect.draw(g2d);
}
g2d.dispose();
}
}
}
You could use a different layout manager, but then your DrawRectangle component must also return a preferredSize so that the layout manager doesn't layout out the component with a size of 0x0
That is what i want to achieve:
As you see, horizontal scroll is changed and a JLabel("text") should be added in the same line. Currently i find the way to change horizontal scroll (like on image), but i can't find any way to add JLabel("text") in the place, where it is placed on the image.
Any suggestions? Thanks in advance!
import java.awt.Component;
import java.awt.Container;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneLayout;
import javax.swing.WindowConstants;
public class Test {
public static void main(String[] args) {
JFrame jFrame = new JFrame();
jFrame.setSize(300, 300);
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Check Check Check Check Check Check Check Check"));
MyScrollPane scrollPane = new MyScrollPane(myPanel);
jFrame.add(scrollPane);
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
}
}
class MyScrollPane extends JScrollPane {
JLabel label = new JLabel("text");
public MyScrollPane(Component view) {
super(view, VERTICAL_SCROLLBAR_ALWAYS, HORIZONTAL_SCROLLBAR_ALWAYS);
this.setLayout(new MyLayout(label));
add(label);
}
}
class MyLayout extends ScrollPaneLayout {
JLabel label;
public MyLayout(JLabel aLabel) {
super();
label = aLabel;
}
public void layoutContainer(Container parent) {
super.layoutContainer(parent);
hsb.setSize(hsb.getWidth() - 100, hsb.getHeight()); // drift
Point location = hsb.getLocation();
label.setLocation(location.x + 12, location.y - 12);
}
}
You might want to consider making your own implementation of JScrollPane. It may sound scary, but in essence all a JScrollPane is is 2 JScrollBars and some graphics logic calling g.translate() If you play around with g.translate you'll see that it's pretty easy to scroll your own stuff.
The advantage of making your own component is that you have full command over layout, display and events. This is the route I would take if I were in your position.
Here's my attempt:
import java.awt.*;
import java.awt.font.*;
import javax.swing.*;
import javax.swing.border.*;
public class Test2 {
public JComponent makeUI() {
JPanel myPanel = new JPanel();
myPanel.add(new JLabel("Check Check Check Check Check Check Check Check"));
JScrollPane scrollPane = new JScrollPane(myPanel,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
JScrollBar hsb = scrollPane.getHorizontalScrollBar();
hsb.setBorder(new StringBorder(hsb, "Test"));
JPanel p = new JPanel(new BorderLayout());
p.add(scrollPane);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new Test2().makeUI());
f.setSize(300, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class StringBorder implements Border {
private final JComponent parent;
private final Insets insets;
private final Rectangle rect;
private final String str;
public StringBorder(JComponent parent, String str) {
this.parent = parent;
this.str = str;
FontRenderContext frc = new FontRenderContext(null, true, true);
rect = parent.getFont().getStringBounds(str, frc).getBounds();
rect.width = Math.max(rect.width, 100);
insets = new Insets(0,5,0,rect.width);
}
#Override public Insets getBorderInsets(Component c) {
return insets;
}
#Override public boolean isBorderOpaque() {
return false;
}
#Override public void paintBorder(
Component c, Graphics g, int x, int y, int width, int height) {
Graphics2D g2 = (Graphics2D)g;
float tx = x + width - insets.right + insets.left;
float ty = y - rect.y + (height - rect.height)/2;
g2.setPaint(Color.BLACK);
g2.drawString(str, tx, ty);
}
}
Probably the best way to achieve something like this would be to use your own layout with a JScrollPane. Here is an example that allows any component as lower-left corner component:
public class CustomScrollPaneLayout extends ScrollPaneLayout {
#Override
public void layoutContainer(Container parent) {
super.layoutContainer(parent);
JScrollBar scrollBar = getHorizontalScrollBar();
if (lowerLeft == null || !lowerLeft.isVisible() || scrollBar == null)
return;
Dimension size = lowerLeft.getPreferredSize();
Rectangle bounds = lowerLeft.isVisible()
? lowerLeft.getBounds() : scrollBar.getBounds();
if (size.width > bounds.getWidth()) {
int right = scrollBar.getX()+scrollBar.getWidth();
if (size.width + scrollBar.getMinimumSize().width > right)
size.width = right - scrollBar.getMinimumSize().width;
if (bounds.x + size.width < scrollBar.getX())
size.width = scrollBar.getX() - bounds.x;
lowerLeft.setBounds(bounds.x, bounds.y, size.width, bounds.height);
int x = bounds.x + size.width;
scrollBar.setBounds(x, bounds.y, right - x, bounds.height);
}
lowerLeft.setVisible(true);
}
}
Please note that you need to use a horizontal scrollbar policy of ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS if you want your control to be always visible.
I am trying to draw a circle of a radius 60 centerd in the lower right quarter of the frame, and a square of radius 50 centered in the upper half of the frame.
The frame size is 300 x 300.
I've done this till now.
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
public class Test {
public static void main ( String[] args){
JFrameTest5 frame = new JFrameTest5();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.setTitle("Test");
}
}
class JFrameTest5 extends JFrame {
public JFrameTest5()
{
setLocation(0,0);
setSize(300,300);
PanelTest1 panel = new PanelTest1();
add(panel);
}
}
class PanelTest1 extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
Ellipse2D circle = new Ellipse2D.Double(250, 225, 120,120);
g2.draw(circle);
Rectangle2D rect = new Rectangle2D.Double(75,0,100,100);
g2.draw(rect);
}
}
The problem is the circle and the rectangle don't seem to be right , are there another methods to set the exact radius ?
The example below includes several important changes:
Use constants wherever possible.
Use panel-relative geometry.
Use initial threads correctly.
Use pack() to size the enclosing frame.
Code:
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** #see http://stackoverflow.com/a/10255685/230513 */
public class Test {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrameTest frame = new JFrameTest();
}
});
}
}
class JFrameTest extends JFrame {
public JFrameTest() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Test");
this.add(new JPanelTest());
this.pack();
this.setLocationByPlatform(true);
this.setVisible(true);
}
}
class JPanelTest extends JPanel {
private static final int R = 60;
private static final int D = 2 * R;
private static final int W = 50;
private static final int E = 2 * W;
#Override
public Dimension getPreferredSize() {
return new Dimension(300, 300);
}
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
Ellipse2D circle = new Ellipse2D.Double(
getWidth() - D, getHeight() - D, D, D);
g2.draw(circle);
Rectangle2D rect = new Rectangle2D.Double(0, 0, E, E);
g2.draw(rect);
}
}
Is there a way I can define my own font and color schemes for Text1 AND Text2 text
within the setBorder method. New to java and cannot find it in the SUN tutorials.
My code
//Create Positions Table
JPanel SpreadPanel = new JPanel();
SpreadPanel.setBorder(BorderFactory.createTitledBorder(" Text 1 Text 2"));
Regards
Simon
setBorder(BorderFactory.createTitledBorder(null, "text", TitledBorder.CENTER, TitledBorder.BOTTOM, new Font("times new roman",Font.PLAIN,12), Color.yellow));
the first parameter null or another border (for compound borders)
2nd param text that you're displaying
3rd and 4th param justification and location of the text from param 2
4th param
and 5th param are the two to set font and color
If you want a different font and color for each of the strings (e.g. Text1 and Text2) in the same TitledBorder, you may be need to extend AbstractBorder and override paintBorder(). The existing implementation only has one font and one color for a single title.
Text Font:
((javax.swing.border.TitledBorder) panel_1.getBorder()).setTitleFont(new Font("Tahoma", Font.PLAIN, 20));
Text Color:
((javax.swing.border.TitledBorder)panel_1.getBorder()).setTitleColor(Color.WHITE);
The JavaDocs for doing this are somewhat overwhelming if you are new to Java and Swing. The JavaDocs for BorderFactory are here: http://download.oracle.com/javase/1.5.0/docs/api/javax/swing/BorderFactory.html
Here's an example of making the text red in a sans serif font:
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.io.IOException;
public class ScratchSpace {
public static void main(String[] args) throws IOException {
Font myFont = new Font("SansSerif", Font.PLAIN, 10);
Color myColor = Color.RED;
TitledBorder titledBorder = BorderFactory.createTitledBorder(null, " Text 1 Text 2", TitledBorder.DEFAULT_JUSTIFICATION, TitledBorder.DEFAULT_POSITION, myFont, myColor);
JFrame frame = new JFrame();
final JLabel label = new JLabel("Hello gruel world");
label.setBorder(titledBorder);
frame.getContentPane().add(label);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
I know it is an old question.
Thought I would like to resurrect it as maybe someone knows how to solve this problem. I have only 'a partial solution'.
I have very quickly implemented the border which does what you want. I have reused what Java gives, i.e. interpretation of HTML in swing components.
All works sweet, the border is painted fine for a plain or HTML text, with exception for a situation where you are trying to have different font sizes for the texts.
I do not have idea how to solve this issue. But I am very much interested in a solution.
I know the procedure would be to sum up width of each string in its own font size when calculating the textLengthInPixels variable.
The problem is that I do not know how to get it, maybe from the View, but no idea how?
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.border.LineBorder;
import javax.swing.text.BadLocationException;
import javax.swing.text.View;
public class MultiColorTitleBorder extends AbstractBorder
{
private static final long serialVersionUID = 1L;
private JLabel label;
private int thicknessTop = 10;
private Border border;
private int thicknessLeft = 0;
private int thicknessRight = 0;
private int thicknessBottom = 0;
public MultiColorTitleBorder(String title)
{
this.label = new JLabel(title);
thicknessTop = label.getPreferredSize().height;
}
public MultiColorTitleBorder(String title, Border border)
{
this(title);
this.border = border;
thicknessLeft = border.getBorderInsets(null).left;
thicknessRight = border.getBorderInsets(null).right;
thicknessBottom = border.getBorderInsets(null).bottom;
}
#Override
public synchronized void paintBorder(Component c, Graphics g, int x, int y, int width, int height)
{
Graphics2D g2 = (Graphics2D) g;
View view = (View) label.getClientProperty("html");
String text = label.getText();
FontMetrics fm = g2.getFontMetrics(label.getFont());
int bY = y + fm.getAscent() - ((fm.getAscent() + fm.getDescent())) / 2;
if(border != null)
{
Insets in = border.getBorderInsets(c);
g2.setClip(x, y, thicknessLeft * 2, height);
border.paintBorder(c, g, x, bY, width, height - bY);
try
{
if(view != null)
text = view.getDocument().getText(0, view.getDocument().getLength());
}catch(BadLocationException ex)
{
Logger.getLogger(MultiColorTitleBorder.class.getName()).log(Level.SEVERE, null, ex);
}
int textLengthInPixels = fm.stringWidth(text);
System.out.println("textLengthInPixels=" + textLengthInPixels);
g2.setClip(x +thicknessLeft * 2+ textLengthInPixels, y, width - thicknessLeft * 2 -textLengthInPixels, height);
border.paintBorder(c, g, x, bY, width, height - bY);
int bottomIn = in.bottom;
g2.setClip(x, height - bottomIn, width, bottomIn);
border.paintBorder(c, g, x, bY, width, height - bY);
g2.setClip(x, y, width, height);
}
if(view != null)
view.paint(g2, new Rectangle(x + thicknessLeft * 2, y, width - thicknessLeft * 2, height));
else
{
Font prevFont = g2.getFont();
g2.setFont(label.getFont());
g2.drawString(text, x + thicknessLeft * 2, fm.getAscent());
g2.setFont(prevFont);
}
}
#Override
public Insets getBorderInsets(Component c)
{
return new Insets(thicknessTop, thicknessLeft, thicknessBottom, thicknessRight);
}
#Override
public Insets getBorderInsets(Component c, Insets insets)
{
insets.top = thicknessTop;
insets.left = thicknessLeft;
insets.right = thicknessRight;
insets.bottom = thicknessBottom;
return insets;
}
#Override
public boolean isBorderOpaque()
{
return false;
}
public static void main(String[] args)
{
JPanel p = new JPanel();
p.setPreferredSize(new Dimension(200, 200));
String title = "<html><color=red> Text 1</font><font color=blue> Text 2</font>";
//title = "<html><font color=red font size=5> Text 1</font><font color=blue> Text 2</font>";
//title = "Text 1 Text 2";
p.setBorder(new MultiColorTitleBorder(title, new LineBorder(Color.CYAN, 6)));
p.setBackground(Color.YELLOW);
p.add(new JTextField(5));
JPanel contentPane = new JPanel();
contentPane.add(p);
JFrame f = new JFrame();
f.setContentPane(contentPane);
f.setSize(800, 600);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
}
Try this:
.setBorder(UIManager.getBorder("TextField.border"));