I try to make a JFrame with a dynamical layout which should look like this
------------------------------------------|
| | Details |
| | |
| | |
| list | |
| | |
| | |
| | save |
-------------------------------------------
Where list is some tree structure where when I chose some element, the details of the element appear in Details. Now the Details consist of multiple JPanels which I put into a JPanel with BoxLayout. Below this JPanel I have a save button.
Since there are a lot of details I put the JPanel Details into a JScrollPane however, this scrollPane ignores its purpos and the result is that the Details panel is bigger than the window size. This is it goes till the bottom hiding the save button and the rest of it is not visible. The scrollbar never appears.
I don't know if it has todo with the fact that when an element is selected I update the JPanel's inside the JScrollPane or whatever.
Here is how I instantiate everything. First inside the right panel I add all JPanel's in firstPanel and I put a button inside the JPanel bottom. Then
setLayout(new BorderLayout());
add(bottom, BorderLayout.SOUTH);
scrollPane=new JScrollPane(firstPanel);
JPanel conScroll = new JPanel(new GridLayout());
conScroll.add(scrollPane);
add(conScroll, BorderLayout.CENTER);
On the JFrame level I instantiate the left and right side however I only put the left hand side and an empty JPanel for the right side called display.
When I select an element I update all elements inside firstPanel I call revalidate() and repaint() on the right side panel. Then on the Frame level I remove all elements of displayer and then add the right side to displayer.
As suggested I made a minimal working example (updated).
So for this minimal example I removed the left side. What is left is a JFrame EmpireEditor with a display JPanel inside, inside which I then put a unitEditor. Uniteditor contains two JPanel one in the center and one south. The panel in the center is in a JScrollPane.
package main;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main {
/**
* #param args
*/
public static void main(String[] args) {
try {
// Set System L&F
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
}
catch (UnsupportedLookAndFeelException e) {
// handle exception
}
catch (ClassNotFoundException e) {
// handle exception
}
catch (InstantiationException e) {
// handle exception
}
catch (IllegalAccessException e) {
// handle exception
}
#SuppressWarnings("unused")
EmpireEditor r = new EmpireEditor();
}
}
The EmpireEditor
package main;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class EmpireEditor extends JFrame {
private JPanel display;
private UnitEditor unitEditor;
public EmpireEditor() {
super("Editor");
display = new JPanel();
unitEditor = new UnitEditor();
add(display);
display.add(unitEditor);
pack();
setVisible(true);
}
}
and here the UnitEditor a center Pannel where I just fill with a PanelNumber but that's just so that there is something inside and that we can see that the scrollbars don't appear. The Idea is that the bottom is always there and the rest should be filled with the JScrollPane.
package main;
import java.awt.BorderLayout;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
#SuppressWarnings("serial")
public class UnitEditor extends JPanel {
private PanelNumbers numbers = new PanelNumbers();
private JScrollPane scrollPane;
public UnitEditor() {
super();
JPanel firstPanel = new JPanel();
firstPanel.setLayout(new BoxLayout(firstPanel, BoxLayout.Y_AXIS));
firstPanel.add(numbers);
JPanel bottom = new JPanel();
JButton save = new JButton("save");
bottom.add(save);
scrollPane=new JScrollPane(firstPanel);
setLayout(new BorderLayout());
add(scrollPane, BorderLayout.CENTER);
add(bottom,BorderLayout.SOUTH);
}
}
finally the PanelNumber but one could exchange this with anything.
package main;
import java.awt.GridLayout;
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Map;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
#SuppressWarnings("serial")
public class PanelNumbers extends JPanel{
private Map<Value, JFormattedTextField> nums = new HashMap<>();
public PanelNumbers(){
super();
setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
JFormattedTextField tmp;
JPanel numbers;
numbers = new JPanel();
numbers.setBorder(BorderFactory.createTitledBorder("Numbers"));
numbers.setLayout(new GridLayout(0, 6));
for (Value s : Value.values()) {
numbers.add(new JLabel(s.n()));
tmp = new JFormattedTextField(NumberFormat.getInstance());
setFocus(tmp);
numbers.add(tmp);
nums.put(s, tmp);
}
add(numbers);
}
public void setUnit(String name){
for (Value key : nums.keySet())
nums.get(key).setValue(0);
}
public void save(){
}
private int toNumber(String t) {
int res = 0;
try {
res = Integer.parseInt(t);
} catch (Exception e) {
}
return res;
}
private void setFocus(final JFormattedTextField num) {
num.addFocusListener(new java.awt.event.FocusAdapter() {
public void focusGained(java.awt.event.FocusEvent evt) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
num.selectAll();
}
});
}
});
//num.setPreferredSize(new Dimension(20, 10));
num.setMaximumSize(num.getPreferredSize());
}
public enum Value {
// Kampf
GROESSE("Groesse"),
WAFFENFERTIGKEIT("Waffenfertigkeit"),
FERNKAMPFFERTIGKEIT("Fernkampffertigkeit"),
ANGRIFFSBONUS("Angriffs Bonus"),
NAHKAMPFPANZERUNG("Panzerung nah"),
FERNKAMPFPANZERUNG("Panzerung fern"),
INITIATIVE("Initiative"),
TP("Treffer"),
// Mouvement
FELDER("Felder"),
BWS("Bewegungspunkte"),
B("B Befehl"),
A("A Befehl"),
P("P Befehl"),
H("H Befehl"),
M("Manoever"),
UEBERR("Ueberrenen"),
//Moral
FURCHTFAKTOR("Furchtfaktor"),
MORALERST("Erster Moralwert"),
MORALZWEIT("Zweiter Moralwert"),
//Rest
MAGIE("Magie"),
KONTROL("Kontrollbereich"),
STERNE("Sterne"),
ELEMENTE("Elemente"),
ELEMENTEPROFELD("Elemente pro Feld"),
KOSTEN("Kosten");
private String name;
Value(String name){
this.name = name;
}
public String n(){
return name;
}
}
}
setting the layout of display to GridLayout(1,1) solved the problem, somehow the original layout just let the inner panel become as big as it wants and hence the JScrollPane didn't feel the urge to make it self smaller and have scrollbars.
Related
Hello first of all when I run the program a button appear , when I press the button the image will go from top to down.
I try the code when the image go from top to down , it work very well
BUT when I put all the codes together there is an error in ( frame.add(new AnimationPane() ); )
Question : How to add AnimationPane() to the frame ???
because this is my problem.
The idea that I want to make two scenes , the first one have a button to make go to the second scene which will have an image (it must be pushed from top until reach down ).
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package maincontentpaneswitching;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching {
private static class ChangeContentPaneListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
// I want to put the image here
JPanel newFrameContents = new JPanel(); //Uses FlowLayout by default.
newFrameContents.add(new JLabel("You have successfully changed the content pane of the frame!", JLabel.CENTER));
/*We assume that the source is a JButton and that the Window is of type JFrame, hence
the following utility method call is possible without letting any errors appear:*/
JFrame frame = (JFrame) SwingUtilities.getWindowAncestor((JButton) e.getSource());
frame.setSize(600, 300);
frame.setContentPane(newFrameContents); //Change the content pane of the frame.
frame.revalidate(); //Notify the frame that the component hierarchy has changed.
frame.add(new AnimationPane() );
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.repaint(); //Repaint frame with all its contents.
}
}
public class AnimationPane extends JPanel {
private BufferedImage boat;
private int yPos = 0;
private int direction = 1;
public AnimationPane() {
try {
boat = ImageIO.read(new URL("https://i.stack.imgur.com/memI0.png"));
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
yPos += direction;
if (yPos + boat.getHeight() > getHeight()) {
yPos = getHeight() - boat.getHeight();
direction *= +1;
} else if (yPos < 0) {
yPos = 0;
direction *= +1;
}
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
} catch (IOException ex) {
ex.printStackTrace();
}
}
#Override
public Dimension getPreferredSize() {
return boat == null ? super.getPreferredSize() : new Dimension(boat.getHeight()*2 , boat.getWidth() *2);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int x = getWidth() - boat.getWidth();
g.drawImage(boat, x, yPos, this);
}
}
private static class MainRunnable implements Runnable {
#Override
public void run() {
JButton changeContentPaneButton = new JButton("Click to go to the next image!");
changeContentPaneButton.addActionListener(new ChangeContentPaneListener());
JPanel frameContents = new JPanel(); //Uses FlowLayout by default.
frameContents.add(changeContentPaneButton);
JFrame frame = new JFrame("My application");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Tells the frame that when the user closes it, it must terminate the application.
frame.setContentPane(frameContents); //Add contents to the frame.
frame.pack(); //Resize the frame as necessary in order to fit as many contents as possible in the screen.
frame.setLocationRelativeTo(null); //Place the frame in the center of the screen. As you can tell, this needs its size to calculate the location, so we made sure in the previous line of code that it is set.
frame.setVisible(true);
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new MainRunnable()); //Swing code must always be used in the Event Dispatch Thread.
}
}
Introduction
As I said in my comment, I couldn't get the image animation to work properly. At least this code would give you a solid foundation to start with.
Here's the GUI I came up with.
Here's the GUI after you left-click on the button.
If you're going to add comments to your code, put the comments on separate lines from the code. Not everyone has a large monitor and can read 200+ character lines of code.
Explanation
Oracle has a rad tutorial, Creating a GUI With Swing. Skip the Netbeans section.
When I create a Swing GUI, I use the model/view/controller (MVC) pattern. This pattern allows me to separate my concerns and focus on one part of the application at a time.
In Swing, the MVC pattern means:
The view reads information from the model
The view may not update the model
The controller updates the model and repaints/revalidates the view.
There's usually not one controller to "rule them all". Each listener controls its portion of the model and the view.
When I put together an application, I code one tiny tiny piece of it, then run tests. I probably ran two to three dozen tests, and this was mostly coded by you.
Model
I created a BoatImage class to read the boat image. It's a separate class, so I can read the image before I start to construct the GUI.
View
I created a JFrame. I created a main JPanel with a CardLayout.
I use a CardLayout to layout the button JPanel and the image JPanel. This way, the JFrame is not constantly changing size.
I create the JFrame and JPanels as separate methods/classes. This makes it much easier for people, including yourself, to read and understand the view code.
Controller
I coded the ChangeContentPaneListener to change from the button JPanel to the image JPanel. This is where you would put your image animation code.
Code
Here's the complete runnable code. I made all the additional classes inner classes so I could post this code as one block.
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MainContentPaneSwitching implements Runnable {
public static void main(String[] args) {
// Swing code must always be used in the Event Dispatch Thread.
SwingUtilities.invokeLater(new MainContentPaneSwitching());
}
private AnimationPane animationPane;
private BoatImage boatImage;
private CardLayout cardLayout;
private JPanel mainPanel;
public MainContentPaneSwitching() {
this.boatImage = new BoatImage();
}
#Override
public void run() {
JFrame frame = new JFrame("My application");
// Tells the frame that when the user closes it, it
// must terminate the application.
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.mainPanel = createMainPanel();
frame.add(mainPanel, BorderLayout.CENTER);
// Resize the frame as necessary in order to fit as many contents
// as possible in the screen.
frame.pack();
// Place the frame in the center of the screen. As you can tell, this
// needs its size to calculate the location, so we made sure in the
// previous line of code that it is set.
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private JPanel createMainPanel() {
cardLayout = new CardLayout();
JPanel panel = new JPanel(cardLayout);
panel.add(createButtonPanel(), "button");
animationPane = new AnimationPane(boatImage);
panel.add(animationPane, "image");
return panel;
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton changeContentPaneButton = new JButton(
"Click to go to the next image!");
changeContentPaneButton.addActionListener(
new ChangeContentPaneListener(this, boatImage));
panel.add(changeContentPaneButton);
return panel;
}
public JPanel getAnimationPane() {
return animationPane;
}
public void repaint() {
animationPane.repaint();
}
public class AnimationPane extends JPanel {
private static final long serialVersionUID = 1L;
private BoatImage boat;
public AnimationPane(BoatImage boat) {
this.boat = boat;
BufferedImage image = boat.getBoat();
this.setPreferredSize(new Dimension(image.getWidth(),
image.getHeight()));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = boat.getBoat();
int x = getWidth() - image.getWidth();
g.drawImage(image, x, boat.getyPos(), this);
}
}
private class ChangeContentPaneListener implements ActionListener {
private int direction, yPos;
private final MainContentPaneSwitching view;
private final BoatImage model;
public ChangeContentPaneListener(MainContentPaneSwitching view,
BoatImage model) {
this.view = view;
this.model = model;
this.direction = 1;
this.yPos = 0;
}
#Override
public void actionPerformed(ActionEvent e) {
cardLayout.show(mainPanel, "image");
}
}
public class BoatImage {
private int yPos;
private BufferedImage boat;
public BoatImage() {
try {
URL url = new URL("https://i.stack.imgur.com/memI0.png");
boat = ImageIO.read(url); // boat.jpg
} catch (MalformedURLException e) {
e.printStackTrace();
boat = null;
} catch (IOException e) {
e.printStackTrace();
boat = null;
}
this.yPos = 0;
}
public BufferedImage getBoat() {
return boat;
}
public void setyPos(int yPos) {
this.yPos = yPos;
}
public int getyPos() {
return yPos;
}
}
}
In my application I am extracting data from a project DB. When I do that I want to signal to user that data extraction is in progress. I want to achieve this by making my current frame view.getFrame2() invisible and making view.getFrame3() visible. Frame 3 will have GIF image with frame titled as "Extraction is in progress". I am able to achieve my target but I am not able to view the image in the frame. It's blank.
Below is the code snippet I am using; one class is used for view, another one for controller and yet another one for the model. I also have a main class to initialize all the M-V-C classes. I don't want to complicate the code by creating more classes.
View
My View class:-
/** View**/
package mvc.views;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import javax.swing.*;
public class View {
JFrame frame2, frame3;
JPanel panel3;
Toolkit tk;
Image icon;
Color background;
public View() {
/** processing image view **/
frame3 =new JFrame();
frame3.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame3.setTitle( "Extraction in process...." );
try {
icon = new ImageIcon("C:\\Users\\pc033k\\Desktop\\gears_animated.gif").getImage();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
panel3 = new JPanel();
background = Color.white;
panel3.setOpaque(true);
frame3.setContentPane(panel3);;
frame3.setBounds(100, 100, 400, 400);
frame3.setLocationRelativeTo(null);
frame3.setVisible(false);
/** End of processing image view **/
}
public void paint (Graphics g)
{
panel3.paint(g);
panel3.setBackground(background);
g.drawImage(icon,100,100,500,500,null);
}
}
/** End of View**/
Controller
/** Start Controller**/
package mvc.controllers;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import mvc.models.*;
import mvc.views.*;
public class Controller {
public Model model;
public View view;
public ActionListener myButtonListener;
//public MyDateListener listener;
boolean status, process1;
public String user, password, FN, LN, type;
JTextField text1, text2;
JCalendarCombo cal1, cal2;
public Date date1, date2;
public Controller(Model model, View view) {
this.model = model;
this.view = view;
}
public class MyButtonListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == view.getGetcrmdataButton()){
FN = view.getUserText1().getText();
LN = view.getUserText2().getText();
date1 = view.getCalendar2().getDate();
date2 = view.getCalendar3().getDate();
type = (String) view.getComb1().getSelectedItem();
view.getFrame2().dispose();
view.getFrame3().setVisible(true);
view.getFrame3().repaint();
try {
process1 = model.CRMDataExtract(FN, LN, date1, date2, type);
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
if(process1 == true){
view.getFrame3().setVisible(false);
view.getFrame2().setVisible(true);
JOptionPane.showMessageDialog((Component) (e.getSource()),
"pealse Check the output file for the data");
}
else
{
view.getFrame3().setVisible(false);
view.getFrame2().setVisible(true);
JOptionPane.showMessageDialog((Component) (e.getSource()),
" No Records found or Data Extraction failed!!");
}
}
}
}
I want to achieve this by making my current frame view.getFrame2() invisible and make a view.getFrame3() visible.
Don't be hiding/showing different windows. Users don't like frames disappearing. Instead you can simply display a modal JDialog containing your image on top of your main frame.
Or another approach is to use a GlassPane that paints over top of your main frame. See Disabled Glass Pane for an example of this approach. The existing example doesn't use an Icon, but you can easily change that.
You can use a label for insert the image.
Also I think CardLayout more suitable to your work. Here is some sample code. Consider that the Sample is a JFrame. You will see the Panel changes as you press the "Next" button.
public Sample() {
p = new JPanel();
p.setLayout(new CardLayout());
p1 = new JPanel();
p1.setLayout(new BorderLayout());
p1.add(new JLabel(new ImageIcon(new URL("http://icons.iconarchive.com/icons/chrisbanks2/cold-fusion-hd/128/ace-of-spades-icon.png"))));
p2 = new JPanel();
p2.setLayout(new BorderLayout());
p2.add(new JLabel(new ImageIcon(new URL("http://icons.iconarchive.com/icons/chrisbanks2/cold-fusion-hd/128/ace-of-hearts-icon.png"))));
p3 = new JPanel();
p3.setLayout(new BorderLayout());
p3.add(new JLabel(new ImageIcon(new URL("http://icons.iconarchive.com/icons/chrisbanks2/cold-fusion-hd/128/angrybirds-2-icon.png"))));
p.add(p1, "0");
p.add(p2, "1");
p.add(p3, "2");
b = new Button("Next");
b.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
rotator++;
if (rotator == 3) {
rotator = 0;
}
CardLayout layout = (CardLayout) p.getLayout();
layout.show(p, "" + rotator);
System.out.println("Rotator " + rotator);
}
});
add(p, "Center");
add(b, "North");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
The short version of the question: I have a JScrollPane with a large number of items being scrolled. At certain times I'd like to add some items to the JScrollPane and have them appear NOT at the end of the list, but after (for example) the 17th item.
Normally if the code in question is within the JScrollPane derived class, I just say this.add(); But I believe this goes to the end of the list.
Rationales: Case 1: The underlying data I'm scrolling is an ordered list of items but additions are allowed at any point of the list. When I add things to the middle of the list, I want to avoid loading the whole list again into the JScrollPane.
Case 2: I'm simulating a scrolling list with expanding subcategories. So if the user picks an item that has expanding subcategories, my Action code recognizes this case, looks into program data to find the subcategories, and then adds them to the JScrollPane beneath the chosen category. Don't worry about it, but there will also be some visual indication that it is a subcategory, supplied by my action code.
I can code all of this. All I am hoping to learn is how to add something to a JScrollPane without it being added to the end of the list.
The answer depends on what this component is that you are adding your items to. If you are using a JList, you could use something like DefaultListModel#add(int, Object) to add an item to a specific location.
If you're using something based on a JComponent (like a JPanel), you can use JComponent#add(Component, int) to specify the location that the component should be added, this assumes that the location exists (ie you can't add a component to position 100 if the container only contains a single component).
The following example generates a random number, determines the location that the number should be inserted in order to maintain a ordered list and inserts a component at that point. It will also attempt to scroll the JScrollPane to make the item visible
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test10 {
public static void main(String[] args) {
new Test10();
}
public Test10() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JPanel numbers;
private List<Integer> values;
public TestPane() {
values = new ArrayList<>(25);
setLayout(new BorderLayout());
numbers = new JPanel(new GridLayout(0, 1));
add(new JScrollPane(numbers));
Timer timer = new Timer(500, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int next = (int) (Math.random() * Integer.MAX_VALUE);
int insertion = Collections.binarySearch(values, next);
if (insertion < 0) {
insertion = (Math.abs(insertion)) - 1;
}
values.add(insertion, next);
JLabel label = new JLabel(Integer.toString(next));
numbers.add(label, insertion);
numbers.revalidate();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Point p = label.getLocation();
JViewport vp = (JViewport) numbers.getParent();
JScrollPane sp = (JScrollPane) vp.getParent();
vp.setViewPosition(p);
}
});
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
}
}
I have a JFrame with it's content pane. JMenuBar is docked on the north of the pane and JLabel (status bar of sorts) on the south.
In the middle is a JTabbedPane. Each tab is a "document". It contains a JScrollBar and a JPanel in it's viewport.
It goes on and on (JPanel of the viewport has more JPanels, that can have more of them, etc...), but for this example, lets just say that that JPanel (in the viewport) can, or cannot fit into the window space (so it cannot, or can force scrollBars to be represented on the screen).
When it fits the window, everyting is fine, but as soon as I set it's height to be too hight to fit inside a window, JMenuBar gets squished on the top.
I'd like to prevent that (without having to specify the absolute height for the JMenuBar, it'd probably work, but it's kind of cheap), since it shouldn't happen in the first place.
Here's SCCE (It's not really short, but you only need to look at the lines 37 to 117, and I have marked all the lines that have something to do with layout with //TODO). Also, to see when problem occurs or when it doesn't occur, change height value in the line 88 inbetween 2000 and 200. You also need a MiG Layout library, of course.
Here's the code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ResourceBundle;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.UnsupportedLookAndFeelException;
import net.miginfocom.swing.MigLayout;
class Menu extends JMenuBar
{
private static final long serialVersionUID = 1L;
public Menu()
{
JMenu fileMenu = new JMenu("file");
this.add(fileMenu);
}
}
class DisplayGUI
{
JTabbedPane documentSelector;
void addNewDocument(String name)
{
Document newDocument = new Document();
newDocument.addChapter(new Chapter(), 1);
documentSelector.add(newDocument, name);
}
public DisplayGUI()
{
JFrame masterWindow = new JFrame("name");
masterWindow.setSize(1100, 800);
masterWindow.setMinimumSize(new Dimension(400, 400));
masterWindow.setLocationRelativeTo(null);
masterWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel rootPanel = new JPanel();
rootPanel.setLayout(new MigLayout()); //TODO Here is layout set for the content pane of the main JFrame
Menu menuBar = new Menu();
rootPanel.add(menuBar, "span, north"); //TODO Here is menu bar added to the JFrame, it's docked north
JLabel statusBar = new JLabel("Welcome to PLabScript editor! Press File>New to create a new file or go to File>Open to open an existing one.");
statusBar.setOpaque(true);
statusBar.setBorder(BorderFactory.createLoweredSoftBevelBorder());
rootPanel.add(statusBar, "span, south"); //TODO Here is status bar added to the JFrame, it's docked south
documentSelector = new JTabbedPane(JTabbedPane.NORTH); //TODO JTabbedPane set so the tab chooser is on the top
rootPanel.add(documentSelector, "grow, push"); //TODO setup so it will take up all the remaining space
addNewDocument("Brand new document");
masterWindow.setContentPane(rootPanel);
masterWindow.setVisible(true);
}
}
class Document extends JScrollPane
{
private static final long serialVersionUID = 1L;
JPanel basePanel;
//methods
void addChapter(Chapter chapter, int index)
{
basePanel.add(chapter, "grow, push, h 2000", index-1); //TODO this here adds a chapter to the basePanel of the JScrollPane which is the a representative of a single document
//TODO it height is set to 2000 (and the problem occurs), but if you reduce it enough so it fits the window, problem will dissaper
}
//constructors
public Document()
{
super(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
getVerticalScrollBar().setUnitIncrement(20);
basePanel = new JPanel();
basePanel.setBackground(Color.RED);
basePanel.setLayout(new MigLayout("insets 0")); //TODO "insets 0" is so there is no border thingy around all of the child components
setViewportView(basePanel);
}
}
class Chapter extends JPanel
{
private static final long serialVersionUID = 1L;
//constructors
Chapter()
{
setLayout(new MigLayout("insets 0")); //TODO "insets 0" is so there is no border thingy around all of the child components
setBackground(Color.MAGENTA);
}
}
public class Main
{
public static ResourceBundle language;
static boolean setUpLAF()
{
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(info.getName()))
{
try
{
UIManager.setLookAndFeel(info.getClassName());
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e)
{
return false;
}
break;
}
}
return true;
}
public static void main(String[] args)
{
//SetUpLookAndFeel
setUpLAF();
//Display actual GUI
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
new DisplayGUI();
}
});
}
}
Line 88 should read:
basePanel.add(chapter, "grow, push", index-1); //TODO this here adds a chapter to the basePanel of the JScrollPane which is the a representative of a single document
Line 100 should read:
basePanel.setLayout(new MigLayout("fill,insets 0")); //TODO "insets 0" is so there is no border thingy around all of the child components
Try this.
I have JScrollPane with JTextArea inside it and I am trying to set the JTextArea's orientation from right to left so the text inside it will start from the right and the scrollbar will be on the left
I've tried the following but they didn't affect the direction of the orientation:
txt.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
txt.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
txt.setAlignmentX(JTextArea.RIGHT_ALIGNMENT);
EDIT:
the two answers camickr & trashgod provided work fine but not in my program where I use my JTextArea as an object Message and pass it to OptionPane.
EDIT2:
I figured out that setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT); doesn't work if I apply it on the JOptionPane contents .. is there an alternative solution to this issue?
Similar to my code:
import java.awt.*;
import java.util.*;
import javax.swing.*;
public class TextArea extends JPanel
{
private JTextArea txt = new JTextArea();
public TextArea()
{
setLayout(new GridLayout());
txt.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
JScrollPane scroll = new JScrollPane(txt);
scroll.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
setPreferredSize(new Dimension(200,200));
this.add(scroll);
}
private void display()
{
Object[] options = {this};
JOptionPane pane = new JOptionPane();
int option = pane.showOptionDialog(null, null, "Title", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, options, options[0]);
}
public static void main(String[] args)
{
new TextArea().display();
}
}
and the scrollbar will be on the left
scrollPane.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
so the text inside it will start from the right
textArea.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
The text starts on the right side, but still gets append to the end as you type instead of being inserted at the beginning of the line.
Update:
I don't know why it doesn't work in an option pane. Here is a simple solution:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
public class Test
{
public static void main(String args[]) throws Exception
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JTextArea textArea = new JTextArea(4, 20);
JScrollPane scrollPane = new JScrollPane( textArea );
JPanel panel = new JPanel();
panel.add( scrollPane );
scrollPane.addAncestorListener( new AncestorListener()
{
public void ancestorAdded(AncestorEvent e)
{
JScrollPane scrollPane = (JScrollPane)e.getComponent();
scrollPane.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
}
public void ancestorMoved(AncestorEvent e) {}
public void ancestorRemoved(AncestorEvent e) {}
});
JOptionPane.showMessageDialog(null, panel);
}
});
}
}
This seems to work.
import java.awt.ComponentOrientation;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/** #see http://stackoverflow.com/questions/6475320 */
public class RTLTextArea extends JPanel {
private static final String s = "مرحبا العالم";
private JTextArea jta = new JTextArea(7, 5);
private Locale arabic = new Locale("ar", "KW");
private ComponentOrientation arabicOrientation =
ComponentOrientation.getOrientation(arabic);
public RTLTextArea() {
this.setLayout(new GridLayout());
this.add(new JScrollPane(jta));
this.applyComponentOrientation(arabicOrientation);
for (int i = 0; i < 8; i++) {
jta.append(s + "\n");
}
}
private void display() {
JFrame f = new JFrame("RTLTextAre");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new RTLTextArea().display();
}
});
}
}
this line
setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT)
change the correct order of the words.
i have this result
KBytes 80.78
The following lines solved my problem:
jTextArea1.setComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
jTextArea1.setText(<text>);
They serve to:
setComponentOrientation() changes the orientation of the TextArea; and,
setText() refreshes TextArea immediately so it displays properly
Simply setting ComponentOrientation to RIGHT_TO_LEFT is not sufficient by itself. repaint() doesn't force the text to realign itself. A quick solution for me was to update the contents of the TextArea. That forced the text to realign itself.