问题
I have 9 jbuttons added to jpanel and the panel added to jscrollpane and it added to jframe.
http://www.pic1.iran-forum.ir/images/up9/95426323683658592564.jpg
when i change frame orientation by:
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
the panel move to right and size of buttons got fixed and wouldn`t fill the panel but you see in the image below that scrolbar is fill all width of panel
http://www.pic1.iran-forum.ir/images/up9/60975202722295688553.jpg
(i used gridbaglayout for adding buttons and borderlayout.center for add scrollpane).
is that a bug in java or ?
EDIT: its the simplest view . Does it help?
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.*;
public class MyFrame extends JFrame{
private JButton[] arrayButton = new JButton[9];
private JButton btnLeft = new JButton("<");
private JButton btnRight = new JButton(">");
private JScrollPane scpButtons = new JScrollPane();
public MyFrame() {
for (int i = 0; i < arrayButton.length; i++)
arrayButton[i] = new JButton("btn");
JPanel pnlButton = initPnlButton();
scpButtons.setViewportView(pnlButton);
setLayout(new BorderLayout());
add(scpButtons, BorderLayout.CENTER);
// comment it and see the result
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
}
private JPanel initPnlButton() {
JPanel pnlButton = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1, 10,
1, new Insets(0, 0, 0, 0), 0, 0);
int ind = 0;
int row = 3;
int column = 4;
for (int i = 0; i < row; i++) {
for (int j = 1; j < column; j++) {
gbc.gridx = j;
gbc.gridy = i;
pnlButton.add(arrayButton[ind++], gbc);
}
}
gbc.weightx = 0;
gbc.gridheight = 3;
gbc.gridx = 0;
gbc.gridy = 0;
pnlButton.add(btnLeft, gbc);
gbc.gridx = 4;
gbc.gridy = 0;
pnlButton.add(btnRight, gbc);
pnlButton.setPreferredSize(new Dimension(1000, 700));
return pnlButton;
}
public static void main(String[] args) {
new MyFrame();
}
}
回答1:
Edit 4
(Hopefully the very last :-)
The ultimate culprit seems to be the main viewport of the scrollPane: it gets confused when sizing its view in RToL once it had been smaller than its preferred. Didn't track what exactly goes wrong, but looks like a viable (short of finding the bug in core and nudge snoracle into fixing it ;) solution is to make the view implement Scrollablable, specifically implement
- getPreferredScrollableViewportSize to return getPreferredSize
- implement getScrollableTracksViewportHeight/Width to return true if height/width is less than parent height/width and false otherwise
JXPanel (contained in SwingX) is-a Scrollable which does the first by default and can be configured for the latter by setting ScrollableSizeHints as appropriate:
private JPanel initPnlButton() {
JXPanel pnlButton = new JXPanel(new GridBagLayout());
pnlButton.setScrollableWidthHint(ScrollableSizeHint.PREFERRED_STRETCH);
pnlButton.setScrollableHeightHint(ScrollableSizeHint.PREFERRED_STRETCH);
...
}
With that in place, no further hacky lines needed, simply apply the CO after adding all components:
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
pack();
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
Edit 2
The more we (both @Reza Gh and me) play with options the more I tend to seeing the behaviour as a bug. To sum up our latest findings
- the scrollPane seems to be a major culprit (Reza)
- the misbehaviour starts once the panel had been resized at/below its preferredSize (note the arteficial - at least I hope it's not part of Reza's production code - setPreferred when creating the panel)
Edit
Playing a bit more, it looks to me like a very weird behaviour on instantiation. A snippet to play with (at the end of the instantiation)
pack();
// [1]
setSize(getWidth() + 1, getHeight() + 1);
// [2]
setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
}
});
and comment 1, 2 or both
- comment both: frame comes up in packed size, maximizing it doesn't fill the frame but child is docked at right edge
- comment 1: frame comes up in maximized size, child fills completed content, then resize to packed and maximize again: child doesn't fill
- comment 2: frame comes up with nearly (one pixel bigger) packed size, maximizing (and all other resizing) correctly fills the screen
The exact behaviour probably depending on native component orientation as well (mine is LToR). On the whole, I think this is a bug somewhere in core handling of component orientation (not surprisingly, it's not as stable as we would expect after all these years).
Looks like a hack around is to resize (slightly by 1 or so pixel, max alone doesn't work) after a pack and then invoke the applyCO.
Original
This does not solve the original problem (which is to apply the componentOrientation on instantiation of the frame), only demonstrates how to safely toggle CO at runtime
Action createCOToggle(final JFrame frame) {
Action toggleComponentOrientation = new AbstractAction("toggle orientation") {
@Override
public void actionPerformed(ActionEvent e) {
ComponentOrientation current = frame.getComponentOrientation();
if (current.isLeftToRight()) {
frame.applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
} else {
frame.applyComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
}
frame.getRootPane().revalidate();
frame.invalidate();
frame.validate();
frame.repaint();
}
};
return toggleComponentOrientation;
}
configuring an action-aware component with it, will make the frame behave as expected, that is fill the complete area. The many re/in/validates look weird - but turned out to be necessary (in jdk6) as we experienced in SwingX test coverage
Now my expectation would have been, that invoking that same action at the end of frame's instantiation would make it behave as well, that is
.... // configure/fill frame
setVisible(true);
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createCOToggle(MyFrame.this).actionPerformed(null);
}
});
Unfortunate, it doesn't. Currently no clue as to why not, sorry.
回答2:
after playing with lots of properties and moving up and down the statements i found the answer. if you put
scpButtons.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
after
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
everything will be okay and no need to setSize(getWidth() + 1, getHeight() + 1);
so funny it got a whle day from me :) does c# or other langs have bugs like this?
if you let SwingUtilities
bloc to run , befor resizing the window everything are yet LtR.
import java.awt.BorderLayout;
import java.awt.ComponentOrientation;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.*;
public class MyFrame extends JFrame {
private JButton[] arrayButton = new JButton[9];
private JButton btnLeft = new JButton("<");
private JButton btnRight = new JButton(">");
private JScrollPane scpButtons = new JScrollPane();
public MyFrame() {
for (int i = 0; i < arrayButton.length; i++)
arrayButton[i] = new JButton("btn");
JMenu mnuSettings = new JMenu("MENU");
JMenuBar menubar = new JMenuBar();
menubar.add(mnuSettings);
setJMenuBar(menubar);
JPanel pnlButton = initPnlButton();
scpButtons.setViewportView(pnlButton);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(scpButtons, BorderLayout.CENTER);
pack();
// [1]
setExtendedState(JFrame.MAXIMIZED_BOTH);
//setSize(getWidth() + 1, getHeight() + 1);
// [2]
setVisible(true);
// SwingUtilities.invokeLater(new Runnable() {
// public void run() {
applyComponentOrientation(ComponentOrientation.RIGHT_TO_LEFT);
//here
scpButtons.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
// }
// });
}
private JPanel initPnlButton() {
JPanel pnlButton = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints(0, 0, 1, 1, 1, 1, 10,
1, new Insets(0, 0, 0, 0), 0, 0);
int ind = 0;
int row = 3;
int column = 4;
for (int i = 0; i < row; i++) {
for (int j = 1; j < column; j++) {
gbc.gridx = j;
gbc.gridy = i;
pnlButton.add(arrayButton[ind++], gbc);
}
}
gbc.weightx = 0;
gbc.gridheight = 3;
gbc.gridx = 0;
gbc.gridy = 0;
pnlButton.add(btnRight, gbc);
gbc.gridx = 4;
gbc.gridy = 0;
pnlButton.add(btnLeft, gbc);
pnlButton.setPreferredSize(new Dimension(1000, 700));
return pnlButton;
}
public static void main(String[] args) {
new MyFrame();
}
}
来源:https://stackoverflow.com/questions/12128743/irrelevant-change-of-button-size-in-right-to-left-orientation