I am doing a chat server and I'm having trouble with the display. I have a JPanel inside a JScrollPane. The JPanel displays the text, but when the text goes lower than its lower border on the screen, the JScrollPane doesn't show any scroll bars.
My code for the window :
package com.main;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import com.panels.ChatPanel;
import com.panels.LinePanel;
public class Window extends JFrame
{
private ChatPanel chatPanel;
private JPanel content;
private TextField textField;
private ArrayList<LinePanel> linePanels = new ArrayList<LinePanel> ();
private Font font = new Font("Arial", Font.BOLD, 17);
private JScrollPane scrollPanel;
public Window(TextField textField)
{
//Sets the title of the window
this.setTitle("Chat");
//Sets the size of the window
this.setSize(1000, 800);
//Sets the location of the window on the screen
this.setLocation(50, 0);
//The program will close when the red X is pressed
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.chatPanel = new ChatPanel();
this.content = new JPanel();
this.scrollPanel = new JScrollPane(this.chatPanel);
this.scrollPanel.getVerticalScrollBar().setUnitIncrement(16);
this.scrollPanel.setBorder(BorderFactory.createEmptyBorder());
this.textField = textField;
this.textField.setPreferredSize(new Dimension(this.getWidth(), 30));
this.textField.setFont(this.font);
//Sets the content panel
this.setContentPane(content);
this.setLayout(new BorderLayout());
this.content.add(this.scrollPanel, BorderLayout.CENTER);
this.content.add(this.textField, BorderLayout.SOUTH);
this.content.setBackground(Color.white);
//Makes the window visible
this.setVisible(true);
}
public void screen()
{
this.chatPanel.repaint();
this.scrollPanel.repaint();
this.scrollPanel.revalidate();
this.getContentPane().repaint();
this.revalidate();
}
public void addLine(String newLine)
{
this.chatPanel.addLine(newLine);
}
}
The JPanel showing the text is chatPanel and the JScrollPane is scrollPanel.
Here is the code of chatPanel :
package com.panels;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.util.ArrayList;
import javax.swing.JPanel;
public class ChatPanel extends JPanel
{
private ArrayList<LinePanel> linePanels = new ArrayList<LinePanel> ();
private ArrayList<String> chat = new ArrayList<String> ();
private GridLayout layout = new GridLayout(1, 1);
private static final int LINE_HEIGHT = 25;
public ChatPanel()
{
super();
this.setLayout(layout);
}
public void paintComponent(Graphics g)
{
g.setColor(Color.white);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setFont(new Font("Arial", Font.BOLD, 20));
g.setColor(Color.black);
for (int i = 0; i < this.chat.size(); i ++)
g.drawString(this.chat.get(i), 10, (i + 1) * LINE_HEIGHT);
}
public void addLine(String newLine)
{
this.chat.add(newLine);
if (this.getHeight() < this.chat.size() * LINE_HEIGHT)
this.setSize(new Dimension(this.chat.size() * LINE_HEIGHT, this.getWidth()));
}
}
public void addLine(String newLine)
{
this.chat.add(newLine);
if (this.getHeight() < this.chat.size() * LINE_HEIGHT)
this.setSize(new Dimension(this.chat.size() * LINE_HEIGHT, this.getWidth()));
}
The above code is incorrect. You should not be attempting to play with the size of the component.
The scrollbars will appear automatically when the "preferred size" of the components is greater than the size of the scroll pane.
Therefore you should be overriding the getPreferredSize() method of your component. So your code should be something like:
public void addLine(String newLine)
{
this.chat.add(newLine);
revalidate();
repaint();
}
#Override
public void getPreferredSize()
{
int height = chat.size() * LINE_HEIGHT;
return new Dimension(100, height);
}
The "width" value should be a parameter that you pass to your class to suggest a default width for the component.
Also, you painting is reinventing the wheel. A JPanel will repaint its background. So the code should be:
//g.setColor(Color.white);
//g.fillRect(0, 0, this.getWidth(), this.getHeight());
super.paintComponent(g);
And then you would invoke:
setBackground( Color.WHITE );
in your constructor.
Related
I need to calculate window decorations somehow. So I override JDialog's constructor. But when I call get_decoration_size() it sometimes returns wrong values. And my thought was: window creates later than get_decoration_size() executes(strange, because both in same thread and in same constructor). So I decided to sleep for a second, and it worked, and now decorations always valid.
My question is: is there a way to "join" to the creating process(wait until window is shown by setVisible(true))? If so, it must be something to replace unsafe_sleep(1000).
package swing.window;
import db.db;
import javax.swing.*;
import java.awt.*;
import static swing.util.*;
import static util.util.unsafe_sleep;
public class calc_decor extends JDialog {
{
//some initializations
setLayout(null);
setResizable(false);
JLabel label = new JLabel("Loading...");
add(label);
setxy(label, 3, 3);
fit(label);
setsize(this, label.getWidth() + 100, label.getHeight() + 100);
window_to_center(this);
setVisible(true);//trying to draw
unsafe_sleep(1000);//without that it looks like get_decoratoin_size()
//is called before setVisible(true)
db.sysdecor = get_decoration_size();//trying to get decorations
dispose();
}
private Dimension get_decoration_size() {
Rectangle window = getBounds();
Rectangle content = getContentPane().getBounds();
int width = window.width - content.width;
int height = window.height - content.height;
return new Dimension(width, height);
}
}
I had to assume a lot to create a runnable example.
Here's the result of your getDecorationSize method. The line didn't print until I closed the JDialog.
java.awt.Dimension[width=16,height=39]
And here's the code I used.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JDialogTest implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new JDialogTest());
}
private JFrame frame;
#Override
public void run() {
frame = new JFrame("JDialog Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createMainPanel());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(
150, 100, 150, 100));
panel.setPreferredSize(new Dimension(400, 400));
JButton button = new JButton("Open JDialog");
button.addActionListener(new ButtonListener());
panel.add(button);
return panel;
}
public class ButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
new CalculateDecor(frame, "Spash Screen");
}
}
public class CalculateDecor extends JDialog {
private static final long serialVersionUID = 1L;
public CalculateDecor(JFrame frame, String title) {
super(frame, true);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
setTitle(title);
JPanel panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(200, 200));
JLabel label = new JLabel("Loading...");
label.setHorizontalAlignment(JLabel.CENTER);
panel.add(label);
add(panel);
pack();
setLocationRelativeTo(frame);
setVisible(true);
System.out.println(getDecorationSize());
}
private Dimension getDecorationSize() {
Rectangle window = getBounds();
Rectangle content = getContentPane().getBounds();
int width = window.width - content.width;
int height = window.height - content.height;
return new Dimension(width, height);
}
}
}
It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel. I've tried setting the layout manager of jpanel to null but no result. I want to use JComponents for drawing lines because I want them clickable.
Main.java file:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
class Line extends JComponent {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked");
}
});
}
}
It shows the line without jpanel on jframe, but it doesn't when I add it to jpanel
Swing components are responsible for determining their own preferred size.
When you add a component to a panel, the layout manager will then set the size/location of the component based on the rules of the layout manager.
When you add a component to the frame you really add it to the content pane of the frame which is a Jpanel which uses a BorderLayout by default. So the component is sized to fill the space available in the frame.
panel.setLayout(null);
You then added the component to a panel with a null layout. Now you are responsible for setting the size/location of the component. If you don't the size is (0, 0) so there is nothing to paint.
You should override the getPreferredSize() method of your class to return the preferred size of the component. Then layout managers can do their job.
If you really need a null layout, then the size of the component should be set in the application code, not it the Line class itself.
But now my line has a big container that listens for any clicks,
If you want hit detection then you override the contains(...) method.
Here is a basic example implementing the above suggestions:
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.Rectangle;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Line extends JComponent
{
private Line2D.Double line;
public Line( Point2D start, Point2D end)
{
line = new Line2D.Double(start, end);
addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
System.out.println("mouse clicked");
}
});
}
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor( Color.BLUE );
g2.setStroke( new BasicStroke(2.0F) );
g2.draw( line );
}
#Override
public Dimension getPreferredSize()
{
Rectangle bounds = line.getBounds();
int width = bounds.x + bounds.width;
int height = bounds.y + bounds.height;
return new Dimension(width, height);
}
#Override
public boolean contains(int x, int y)
{
double distance = line.ptSegDist( new Point2D.Double(x, y) );
return distance < 2;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setBackground(Color.YELLOW);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
panel.repaint();
frame.add(panel);
frame.setVisible(true);
}
}
Add custom size in Line constructor.
public Line( Point2D start, Point2D end){ ...
this.setSize(200, 200); }
Updated to fit also with painted Graph
Advice to change from JComponent to JPanel in order to see background
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setSize(500, 500);
//Parent Panel
JPanel panel = new JPanel();
panel.setSize(300,300);
frame.add(panel);
panel.setBackground(Color.YELLOW);
panel.setLayout(null);
//Add Line To Panel
Line line = new Line(new Point2D.Double(20,20), new Point2D.Double(180,180));
panel.add(line);
frame.setVisible(true);
}
}
class Line extends JPanel {
private final Point2D start, end;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setBackground(Color.RED);
g2.setColor(Color.BLUE);
g2.setStroke(new BasicStroke(2.0F));
g2.draw(new Line2D.Double(start,end));
Rectangle r = g2.getClipBounds();
System.out.println(r.x+":"+r.y);
}
public Line( Point2D start, Point2D end){
this.start = start;
this.end = end;
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
System.out.println("mouse clicked at "+e.getX()+":"+e.getY());
}
});
int max_x = (int) Math.max(start.getX(), end.getX());
int max_y = (int) Math.max(start.getY(), end.getY());
System.out.println("max x="+max_y+",y="+max_y);
setSize(max_x,max_y);
setVisible(true);
setBackground(Color.GREEN);
}
}
Note: Only inside_green clicks allowed !
I have a single class extended from JFrame. I have multiple JLayeredPanes inside it. I want to switch from one JLayeredPane to another. For that what I does is;
remove the current JLayeredPane like remove(pane1);
repaint();
add components to new JLayeredPane and set bounds
add(pane2)
This removes my current JLayeredPane and shows next JLayeredPane. But the components are not added in correct position. For example, I have a jscrollpane containing a textarea. Only the scroll pane is appearing and text area is missing. Also, the size of comboboxes has changed. Why is this happening?
Here is a zip file containing entire java files http://s000.tinyupload.com/index.php?file_id=33381866612121130517
I combined this JLayeredPane example with this CardLayout example to demonstrate multiple, selectable layered panes.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/36587584/230513
* #see https://stackoverflow.com/a/6432291/230513
* #see https://stackoverflow.com/questions/6432170
*/
public class CardPanel extends JLayeredPane {
private static final Dimension d = new Dimension(320, 240);
private static final Random random = new Random();
private static final JPanel cards = new JPanel(new CardLayout());
private static final JComboBox combo = new JComboBox();
private final String name;
public CardPanel(String name) {
this.name = name;
this.setBackground(new Color(random.nextInt()));
this.add(new LayerPanel(1 * d.height / 8), 100);
this.add(new LayerPanel(2 * d.height / 8), 101);
this.add(new LayerPanel(3 * d.height / 8), 102);
}
#Override
public Dimension getPreferredSize() {
return d;
}
#Override
public String toString() {
return name;
}
private static class LayerPanel extends JPanel {
private static final Random r = new Random();
private int n;
private Color color = new Color(r.nextInt());
public LayerPanel(int n) {
this.n = n;
this.setOpaque(false);
this.setBounds(n, n, d.width / 2, d.height / 2);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setColor(color);
g2d.fillRoundRect(0, 0, getWidth(), getHeight(), 16, 16);
g2d.setColor(Color.black);
g2d.drawString(String.valueOf(n), 5, getHeight() - 5);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
create();
}
});
}
private static void create() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
for (int i = 1; i < 9; i++) {
CardPanel p = new CardPanel("Panel " + String.valueOf(i));
combo.addItem(p);
cards.add(p, p.toString());
}
JPanel control = new JPanel();
combo.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JComboBox jcb = (JComboBox) e.getSource();
CardLayout cl = (CardLayout) cards.getLayout();
cl.show(cards, jcb.getSelectedItem().toString());
}
});
control.add(combo);
f.add(cards, BorderLayout.CENTER);
f.add(control, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
I have a problem with showing specific component placed in JScrollPane. I have horizontal JScrollPane with GridLayout(1,0) and it contains variable number of JPanels - each containing image. It's like a preview of frames in GIF image. I use button to move among these JPanels (by changing borders and keeping index of chosen one), but I don't know how to force JScrollPane to show me JPanel if it's chosen (and center it if possible).
So I want this
force to do this:
Thanks in advance!
EDIT: almost working code with scrollRectToVisible() method
public class MiniatursPanel extends JPanel{
private int indexOfChosenFrame = 0;
private ArrayList<JPanel> frames;
private JScrollPane scrollPane;
private JPanel innerPanel;
public MiniatursPanel(){
setBorder(BorderFactory.createCompoundBorder(BorderFactory.createRaisedBevelBorder(),BorderFactory.createLoweredBevelBorder()));
setPreferredSize(new Dimension(1200,170));
setLayout(null);
}
public void initialize(){
int width = GifImageStats.getInstance().getWidth();
int height = GifImageStats.getInstance().getHeight();
int numberOfFrames = GifImageStats.getInstance().getNumberOfFrames();
frames = new ArrayList(numberOfFrames);
for (int i = 0; i < numberOfFrames; i++) {
JPanel frameBox = new JPanel();
frameBox.setLayout(new FlowLayout(FlowLayout.CENTER));
JButton button = new JButton(String.valueOf(i+1));
button.setPreferredSize(new Dimension(2*width,2*height));
button.setBackground(Color.white);
button.setFocusable(false);
frameBox.add(button);
frames.add(frameBox);
}
innerPanel = new JPanel();
innerPanel.setLayout(new GridLayout(1,0,10,10));
for (JPanel button : frames) {
innerPanel.add(button);
}
scrollPane = new JScrollPane(innerPanel);
scrollPane.setBounds(10, 10, 1180, 145);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
highlightFrame(frames.get(0));
add(scrollPane);
}
public void nextFrame(){
if (indexOfChosenFrame == frames.size() - 1) {
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame = 0;
highlightFrame(frames.get(0));
}else{
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame++;
highlightFrame(frames.get(indexOfChosenFrame));
}
}
public void previousFrame(){
if (indexOfChosenFrame == 0) {
unhighlightFrame(frames.get(0));
indexOfChosenFrame = frames.size()-1;
highlightFrame(frames.get(indexOfChosenFrame));
}else{
unhighlightFrame(frames.get(indexOfChosenFrame));
indexOfChosenFrame--;
highlightFrame(frames.get(indexOfChosenFrame));
}
}
private void highlightFrame(JPanel frame){
Rectangle rect = frame.getBounds();
rect.setBounds(frame.getX()-550, frame.getY(), frame.getWidth()+1050, frame.getHeight());
innerPanel.scrollRectToVisible(rect);
frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}
private void unhighlightFrame(JPanel frame){
frame.setBorder(null);
}
The relevant method here is JComponent#scrollRectToVisible(Rectangle). It has to be called on the component that is in the viewport of the scroll pane. (In your case, this is the panel with the grid layout, which contains the other sub-panels).
The rectangle that is passed to this method can be the bounds of one sub-panel. In this case, the scoll pane will do the "minimum" scrolling that is necessary to make the given rectangle visible. If you want to make sure that the respective sub-panel is in the center, then you can increase the size of this rectangle - that is, you define a rectangle in a way that the desired sub-panel will be in the center.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class ScrollToVisible
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
int n = 20;
final JPanel panel = new JPanel(new GridLayout(1,0));
final List<JComponent> components = new ArrayList<JComponent>();
for (int i=0; i<n; i++)
{
JComponent component = new JLabel(String.valueOf(i), SwingConstants.CENTER);
component.setPreferredSize(new Dimension(100,100));
component.setBorder(BorderFactory.createLineBorder(Color.BLACK));
components.add(component);
panel.add(component);
}
final JScrollPane scrollPane = new JScrollPane(panel);
final JSpinner spinner = new JSpinner(new SpinnerNumberModel(0, 0, n-1, 1));
spinner.addChangeListener(new ChangeListener()
{
JComponent selectedComponent = components.get(0);
#Override
public void stateChanged(ChangeEvent e)
{
selectedComponent.setBorder(BorderFactory.createLineBorder(Color.BLACK));
int index = (Integer)spinner.getValue();
JComponent component = components.get(index);
Rectangle bounds = component.getBounds();
// This would make the component "just" visible:
//panel.scrollRectToVisible(bounds);
// This will center the component:
int cx = bounds.x + bounds.width / 2;
int w = scrollPane.getViewport().getWidth();
Rectangle r = new Rectangle(cx-w/2, bounds.y, w, bounds.height);
panel.scrollRectToVisible(r);
selectedComponent = component;
selectedComponent.setBorder(BorderFactory.createLineBorder(Color.RED));
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(scrollPane, BorderLayout.CENTER);
f.getContentPane().add(spinner, BorderLayout.NORTH);
f.setSize(800, 300);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
EDIT: You should NOT use setLayout(null), and you should not do manual calls to setBounds, and you should rarely use setPreferredSize. And... when you post code that already is so close to a https://stackoverflow.com/help/mcve (or even was created from a runnable example of another post) then you should make it really runnable. It's annoying to re-insert the boilerplate code and waste some time with debugging until you realize that initialize() is not called at all...
However, change the code according to this:
private void highlightFrame(JPanel frame){
Rectangle rect = frame.getBounds();
int c = rect.x + rect.width / 2;
int w = scrollPane.getViewport().getWidth();
int x = c-w/2;
rect.setBounds(x, rect.y, w, rect.height);
innerPanel.scrollRectToVisible(rect);
frame.setBorder(BorderFactory.createLineBorder(Color.red,2));
}
private void unhighlightFrame(JPanel frame){
frame.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
}
The most important thing is to make sure that the size of the components is correct, by setting an empty border with the same size as the "highlighting" border.
I am trying to create a GUI that will take in the number of circles to draw, and draw them in drawPanel with random locations/sizes. On my actionListener, when I try to draw the circle, it gives me red lines on my drawOval
1st class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
/**
*
* #author Chris
*
*/
public class CirclesPanel extends JPanel{
private JButton draw, clear;
private JTextArea textArea;
private JPanel panel, drawPanel, buttonPanel;
private int count;
/**constructor
* builds the frame
*/
public CirclesPanel(){
//creates buttons and textArea
draw = new JButton("Draw");
clear = new JButton("Clear");
textArea = new JTextArea(1,10);
textArea.setBorder(BorderFactory.createTitledBorder("Circles"));
//creats panel
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setPreferredSize(new Dimension(620, 425));
//creates subpanel drawPanel
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(450,400));
drawPanel.setBackground(Color.BLACK);
//creates subpanel buttonPanel
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(3,1));
//adds all the content to the frame
add(panel);
add(buttonPanel, BorderLayout.WEST);
add(drawPanel, BorderLayout.EAST);
buttonPanel.add(textArea);
buttonPanel.add(draw);
buttonPanel.add(clear);
//reads if the draw button is clicked
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count =Integer.parseInt(textArea.getText());//takes the count in
repaint();//repaints the picture to add the circles
}
});
//reads if the clear button is clicked
clear.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count=0;//sets the count to 0 so nothing is painted
repaint();//repaints the window
}
});
}
/**Paint component
* draws the random circles
*/
public void paintComponent(Graphics g) {
Random generator = new Random();
int x, y, diameter;
for(int i = 0; i < count; i++){ //loop that takes the count and does this "x" times
g.setColor(Color.BLUE);//sets color to blue
x = generator.nextInt(90);//random location for x
y = generator.nextInt(90);//random location for y
diameter = generator.nextInt(30);//random size
g.fillOval(x, y, diameter, diameter);//draws the circle
}
}
}
2nd class
import javax.swing.JFrame;
public class Circles {
public static void main(String[]args){
JFrame frame = new JFrame("Cicles HW9");
frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new CirclesPanel());
frame.pack();
frame.setVisible(true);
}
}
So in your, I did little addition, first of all, I made the whole program in one class(CIRLCES PANEL), IF You want to use the second class, you can use it....
Problem is coming, your program is not the reading the ActionPerformed method for the drawing, means it is not located with the button, now I directly added it with your button(DRAW), now whenever you click on the button, it automatically reads the your textArea value, and draw your circles. I made your text area FINAL, So you can use it anywhere......
Now things that you need to do----
- this program is drawing circle on the whole frame, means not on your drawing Panel, you need to set the values, so it will draw on your draw panel area
- Also you need to add color for your oval, because it will either draw black color circle, which you will not able to see.....
and also one thing I forget to mentioned you, is that your, you also need to add code for your clear method...
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTextArea;
public class CirclesPanel extends JPanel{
private JButton draw, clear;
private JTextArea textArea;
private JPanel panel, drawPanel, buttonPanel;
private int count;
public CirclesPanel(){
JButton draw = new JButton("Draw");
JButton clear = new JButton("Clear");
final JTextArea textArea = new JTextArea(1,10);
textArea.setBorder(BorderFactory.createTitledBorder("Circles"));
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
setPreferredSize(new Dimension(620, 425));
JPanel drawPanel = new JPanel();
drawPanel.setPreferredSize(new Dimension(450,400));
drawPanel.setBackground(Color.BLACK);
JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new GridLayout(3,1));
add(panel);
add(buttonPanel, BorderLayout.WEST);
add(drawPanel, BorderLayout.EAST);
buttonPanel.add(textArea);
buttonPanel.add(draw);
buttonPanel.add(clear);
draw.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
count =Integer.parseInt(textArea.getText());
repaint();
}
});
}
public void paintComponent(Graphics g) {
Random generator = new Random();
int x, y, diameter;
for(int i = 0; i < count; i++){
x = generator.nextInt(90);
y = generator.nextInt(90);
diameter = generator.nextInt(30);
g.drawOval(x, y, diameter, diameter);
}
}
}
What you want to do is drawing some random circles on the drawPanel when button clicked. I write you a simplified version to show how things work.
I only keep the drawButton and paintPanel to keep things simple.
public class PaintFrame extends JFrame {
private JPanel content = new JPanel();
private JButton drawButton = new JButton("Draw");
private PaintPanel paintPanel = new PaintPanel();
public PaintFrame() {
getContentPane().add(content);
content.setLayout(new BorderLayout());
drawButton.setSize(100, 500);
drawButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// drawButton is fired, repaint the paintPanel
paintPanel.repaint();
}
});
content.add(drawButton, BorderLayout.WEST);
content.add(paintPanel, BorderLayout.CENTER);
}
}
You need a new class extending the JPanel and override the paintComponent method to do the paint job for you. This makes sure you are drawing on the panel.
class PaintPanel extends JPanel {
public PaintPanel() {
setSize(500, 500);
setBackground(Color.BLACK);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Random random = new Random();
g.setColor(Color.WHITE);
// draw 5 random circles
int count = 5;
for (int i = 0; i < count; i++) {
g.drawOval(random.nextInt(250), random.nextInt(250),
random.nextInt(250), random.nextInt(250));
}
}
}
Main class
public class DrawMain {
public static void main(String[] args) {
JFrame frame = new PaintFrame();
frame.setDefaultCloseOperation(PaintFrame.EXIT_ON_CLOSE);
frame.setSize(600, 500);
frame.setVisible(true);
}
}