I have a simple Swing application with a JEditorPane wrapped in a JScrollPane.
Unfortunately screen reader software like JAWS or NVDA does not behave correctly.
When focus enters the JEditorPane it only reads the accessible name followed by "text" and then stops, when the expected behavior is to continue reading the contents of the JEditorPane.
If I do not wrap the JEditorPane in the JScrollPane it works as expected.
I have tried inspecting the accessible tree using Monkey, but I cannot see any relevant difference between a JEditorPane wrapped in a JScrollPane and one that is not wrapped.
Any ideas?
Here is a brief sample that demonstrates the problem. If focus enters the first JEditorPane, JAWS reads "first editorpane - edit". If focus enters the second JEditorPane, JAWS reads "second editorpane - edit - bar".
public final class SmallExample {
public static void main(String... aArgs){
JFrame frame = new JFrame("Test Frame");
JPanel panel = new JPanel();
JEditorPane editorPane1 = new JEditorPane();
editorPane1.setText("Foo");
editorPane1.getAccessibleContext().setAccessibleName("first editorpane");
editorPane1.getAccessibleContext().setAccessibleDescription("");
JScrollPane scrollPane = new JScrollPane( editorPane1, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
panel.add(scrollPane);
JEditorPane editorPane2 = new JEditorPane();
panel.add(editorPane2);
editorPane2.setText("Bar");
editorPane2.getAccessibleContext().setAccessibleName("second editorpane");
editorPane2.getAccessibleContext().setAccessibleDescription("");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
I found a workaround myself:
If I
modify the accessible tree to skip the JScrollPane and JViewPort
avoid sending accessible property-changed events from the JEditorPane
then it works.
I would still very much appreciate any insight into why this works (I found the workaround by replacing the AccessibleContext for editorPane2 with the one for editorPane1 and gradually switching the methods back until I found the ones that I needed to override).
Here is a working example (it is not so brief anymore):
public final class Example {
public static void main(String... aArgs){
JFrame frame = new JFrame("Test Frame");
final JPanel panel = new JPanel(){
public AccessibleContext getAccessibleContext() {
if(accessibleContext==null){
accessibleContext = new AccessibleContextWrapper(super.getAccessibleContext()){
public Accessible getAccessibleChild(int i) {
Accessible accessibleChild = super.getAccessibleChild(i);
while(accessibleChild!=null && (accessibleChild instanceof JScrollPane || accessibleChild instanceof JViewport)){
accessibleChild = accessibleChild.getAccessibleContext().getAccessibleChild(0);
}
return accessibleChild;
}
};
}
return accessibleContext;
}
};
final JEditorPane editorPane = new JEditorPane(){
public AccessibleContext getSuperAccessibleContext() {
return super.getAccessibleContext();
}
#Override
public AccessibleContext getAccessibleContext() {
return new AccessibleContextWrapper(super.getAccessibleContext()){
public Accessible getAccessibleParent() {
Accessible parent = super.getAccessibleParent();
while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
parent = parent.getAccessibleContext().getAccessibleParent();
}
return parent;
}
public int getAccessibleIndexInParent() {
int res = super.getAccessibleIndexInParent();
Accessible parent = super.getAccessibleParent();
while(parent!=null && (parent instanceof JScrollPane || parent instanceof JViewport)){
res = parent.getAccessibleContext().getAccessibleIndexInParent();
parent = parent.getAccessibleContext().getAccessibleParent();
}
return res;
}
public void addPropertyChangeListener(
PropertyChangeListener listener) {
}
public void removePropertyChangeListener(
PropertyChangeListener listener) {
}
};
}
};
editorPane.setText("Foo");
editorPane.getAccessibleContext().setAccessibleName("first editorpane");
editorPane.getAccessibleContext().setAccessibleDescription("");
editorPane.getAccessibleContext();
JScrollPane scrollPane = new JScrollPane( editorPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED );
panel.add(scrollPane);
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static class AccessibleContextWrapper extends AccessibleContext {
private final AccessibleContext inner;
public AccessibleContextWrapper(AccessibleContext inner) {
this.inner = inner;
}
public String getAccessibleName() {
return inner.getAccessibleName();
}
public void setAccessibleName(String s) {
inner.setAccessibleName(s);
}
public String getAccessibleDescription() {
return inner.getAccessibleDescription();
}
public void setAccessibleDescription(String s) {
inner.setAccessibleDescription(s);
}
public AccessibleRole getAccessibleRole() {
return inner.getAccessibleRole();
}
public AccessibleStateSet getAccessibleStateSet() {
return inner.getAccessibleStateSet();
}
public Accessible getAccessibleParent() {
return inner.getAccessibleParent();
}
public void setAccessibleParent(Accessible a) {
inner.setAccessibleParent(a);
}
public int getAccessibleIndexInParent() {
return inner.getAccessibleIndexInParent();
}
public int getAccessibleChildrenCount() {
return inner.getAccessibleChildrenCount();
}
public Accessible getAccessibleChild(int i) {
return inner.getAccessibleChild(i);
}
public Locale getLocale() throws IllegalComponentStateException {
return inner.getLocale();
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
inner.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
inner.removePropertyChangeListener(listener);
}
public AccessibleAction getAccessibleAction() {
return inner.getAccessibleAction();
}
public AccessibleComponent getAccessibleComponent() {
return inner.getAccessibleComponent();
}
public AccessibleSelection getAccessibleSelection() {
return inner.getAccessibleSelection();
}
public AccessibleText getAccessibleText() {
return inner.getAccessibleText();
}
public AccessibleEditableText getAccessibleEditableText() {
return inner.getAccessibleEditableText();
}
public AccessibleValue getAccessibleValue() {
return inner.getAccessibleValue();
}
public AccessibleIcon[] getAccessibleIcon() {
return inner.getAccessibleIcon();
}
public AccessibleRelationSet getAccessibleRelationSet() {
return inner.getAccessibleRelationSet();
}
public AccessibleTable getAccessibleTable() {
return inner.getAccessibleTable();
}
public void firePropertyChange(String propertyName, Object oldValue,
Object newValue) {
inner.firePropertyChange(propertyName, oldValue, newValue);
}
}
}
Related
I've tried to apply the Observable/Observer pattern but there is something wrong with my code when I try to change a the textfield of a JTextPane.
I've got 3 classes, Play, Controller and SecondWindow here are a sample of their code.
public class Play() {
Controller c = new Controller();
SecondWindow sw = new SecondWindow();
c.addObserver(sw)
c.setText("blabla");
}
My class Controller:
public class Controller extends Observable(){
private String text ="";
private static Controller getInstance() {
if (instance == null) {
instance = new Controller();
}
return instance;
}
public void setText(String s) {
text = s;
setChanged();
notifyObservers();
}
}
and SecondWindow:
public class SecondWindow extends JFrame implements Observer{
private JPanel contentPane;
private Controller c;
private JTextPane txt = new JTextPane();
public SecondWindow () {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
SecondWindow frame = new SecondWindow();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public SecondWindow() {
initComponents();
createEvents();
c = Controller.getInstance();
}
public void initComponents() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(1000, 0, 300,500);
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
contentPane.setLayout(null);
txt.setBounds(0, 0, 280, 460);
txt.enable(false);
contentPane.add(txt);
}
public void update(Observable arg0 , Object arg1){
// Things to change here
}
I can't manage to put the variable c in the textField (like a txt.setText(c.getText) instruction). I'm sure that it reads the method update, but I don't know how to make sure it works.
Hint: Per the Observerable API the notifyObservers method has an overload that accepts any object as a parameter:
public void notifyObservers(Object arg)
This can even be a String. And as per the Observer API, this object is then passed into the update method in the observer, and you can use it there.
void update(Observable o,
Object arg)
arg - an argument passed to the notifyObservers method.
Separate side issue here:
contentPane.setLayout(null);
For most Swing aficionados, seeing this is like hearing nails on a chalkboard -- it's painful. While null layouts and setBounds() might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one. 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.
Side issue number two: your code is not Swing thread safe, since the Swing GUI could very well be notified by the observable off of the Swing event dispatch thread or EDT. While it is not likely to cause frequent or serious problems with this simple program, in general it would be better to use a SwingPropertyChangeSupport and PropertyChangeListeners rather than Observer / Observable if you can.
Next Side Issue
This:
public class Controller extends Observable(){
isn't compilable / kosher Java. Same for the duplicate parameter-less constructors for the SecondWindow class. Yes, we know what you're trying to do, but it's hard enough trying to understand someone else's code, you really don't want to make it harder by posting kind-of sort-of uncompilable code, trust me.
For example, something simple could be implemented in Swing using PropertyChangeListeners, like so:
import java.util.concurrent.TimeUnit;
public class Play2 {
public static void main(String[] args) {
Model2 model2 = new Model2();
View2 view2 = new View2();
new Controller2(model2, view2);
view2.show();
for (int i = 0; i < 10; i++) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// one of the few times it's OK to ignore an exception
}
String text = String.format("Counter Value: %d", i);
model2.setText(text);
}
}
}
import java.beans.PropertyChangeListener;
import javax.swing.event.SwingPropertyChangeSupport;
public class Model2 {
private SwingPropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
public static final String TEXT = "text"; // name of our "bound" property
private String text = "";
public String getText() {
return text;
}
public void setText(String text) {
String oldValue = this.text;
String newValue = text;
this.text = text;
pcSupport.firePropertyChange(TEXT, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(listener);
}
public void addPropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.addPropertyChangeListener(name, listener);
}
public void removePropertyChangeListener(String name, PropertyChangeListener listener) {
pcSupport.removePropertyChangeListener(name, listener);
}
}
import javax.swing.*;
public class View2 {
private JPanel mainPanel = new JPanel();
private JTextField textField = new JTextField(10);
public View2() {
textField.setFocusable(false);
mainPanel.add(new JLabel("Text:"));
mainPanel.add(textField);
}
public JPanel getMainPanel() {
return mainPanel;
}
public void setText(String text) {
textField.setText(text);
}
public void show() {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("View");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(getMainPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
public class Controller2 {
private Model2 model2;
private View2 view2;
public Controller2(Model2 model2, View2 view2) {
this.model2 = model2;
this.view2 = view2;
model2.addPropertyChangeListener(Model2.TEXT, new ModelListener());
}
private class ModelListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent pcEvt) {
view2.setText((String) pcEvt.getNewValue());
}
}
}
I've used java Spinner in Java Swing, but it had up and down arrows, is there a way to set it's orientation so that the arrows are left and right ?
Thanks to Jean-François Savard's answer, I'm one step closer, but still not quite right, I have the following lines :
public void installUI(JComponent c)
{
super.installUI(c);
c.removeAll();
FlowLayout FL=new FlowLayout();
FL.setHgap(0);
c.setLayout(FL);
JComponent editor=createEditor();
editor.setPreferredSize(new Dimension(30,16));
c.add(editor);
c.add(createPreviousButton());
c.add(createNextButton());
}
The spacing is not correct, how to fix it ? I hard coded in the above lines, how to automatically provide proper space for the text ?
A short search on google lead me to a custom implementation of JSpinner to do this.
public class LeftRightSpinnerDemo {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new LeftRightSpinnerDemo().makeUI();
}
});
}
public void makeUI() {
JSpinner spinner = new JSpinner();
spinner.setUI(new LeftRightSpinnerUI());
JFrame frame = new JFrame();
frame.add(spinner);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}
class LeftRightSpinnerUI extends BasicSpinnerUI {
public static ComponentUI createUI(JComponent c) {
return new LeftRightSpinnerUI();
}
#Override
protected Component createNextButton() {
Component c = createArrowButton(SwingConstants.EAST);
c.setName("Spinner.nextButton");
installNextButtonListeners(c);
return c;
}
#Override
protected Component createPreviousButton() {
Component c = createArrowButton(SwingConstants.WEST);
c.setName("Spinner.previousButton");
installPreviousButtonListeners(c);
return c;
}
// copied from BasicSpinnerUI
private Component createArrowButton(int direction) {
JButton b = new BasicArrowButton(direction);
Border buttonBorder = UIManager.getBorder("Spinner.arrowButtonBorder");
if (buttonBorder instanceof UIResource) {
b.setBorder(new CompoundBorder(buttonBorder, null));
} else {
b.setBorder(buttonBorder);
}
b.setInheritsPopupMenu(true);
return b;
}
#Override
public void installUI(JComponent c) {
super.installUI(c);
c.removeAll();
c.setLayout(new BorderLayout());
c.add(createNextButton(), BorderLayout.EAST);
c.add(createPreviousButton(), BorderLayout.WEST);
c.add(createEditor(), BorderLayout.CENTER);
}
}
Make sure to add the correct imports as I removed them to lighten the code.
Refer to this for the original post.
I'm trying to create a custom focus traversal policy for my swing app by passing a list of components (I do this as the traversal required is not the same as the Containers component order). Like this example:
public class ComponentTraversal extends FocusTraversalPolicy
{
private final java.util.List<Component> componentList;
public ComponentTraversal(java.util.List<Component> aList)
{
componentList = aList;
}
#Override
public Component getComponentAfter(Container aContainer, Component aComponent)
{
int index = componentList.indexOf(aComponent);
if (index == (componentList.size() - 1))
index = -1;
return componentList.get(index + 1);
}
#Override
public Component getComponentBefore(Container aContainer, Component aComponent)
{
int index = componentList.indexOf(aComponent);
if (index == 0)
index = componentList.size();
return componentList.get(index - 1);
}
#Override
public Component getFirstComponent(Container aContainer)
{
return componentList.get(0);
}
#Override
public Component getLastComponent(Container aContainer)
{
return componentList.get(componentList.size() - 1);
}
#Override
public Component getDefaultComponent(Container aContainer)
{
return componentList.get(0);
}
}
And run with this test class:
public class C extends JFrame
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(
new Runnable()
{
#Override
public void run()
{
new C();
}
}
);
}
public C()
{
super("A Test");
JPanel jp = new JPanel(new GridLayout(2,1));
JTextField jtf1 = new JTextField();
JTextField jtf2 = new JTextField();
jp.add(jtf1);
jp.add(jtf2);
java.util.List<Component> comps = new ArrayList<Component>();
comps.add(jtf1);
comps.add(jtf2); //This line
this.add(jp);
this.setFocusTraversalPolicy(new ComponentTraversal(comps));
this.setSize(200,100);
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
this.setVisible(true);
}
}
In most cases this works fine, as posted will work in both directions. If I comment out the line with the comment This line (i.e. adding only one component to the traversal list) it will work in the forwards direction (Tab)... retaining focus on itself.
But if I do it in reverse direction (Shift+Tab), the component loses focus and no exceptions are reported.
This isn't really a problem in terms of my application, as there will always be more than one component, but I tried this out and it bugs me and I was wondering if anyone had encountered this before, or knows what I'm doing wrong.
Below is the answer specific to my original question, but if you're trying to achieve a similar structure in general, it'd probably be more practical follow camickr's method below. To do it the way I originally wanted, you have to override JPanel and implement Scrollable for the outer container.
(credit to user Kylar - JTextArea on JPanel inside JScrollPane does not resize properly)
import javax.swing.*;
import java.awt.*;
import javax.swing.text.*;
public class MyEditor extends JTextPane {
JFrame window;
JScrollPane editScroll;
NumberLines numPane;
HoldMe please;
public static void main(String args[]) {
new MyEditor();
}
MyEditor() {
setOpaque(false);
setEditorKit(new TextWrapKit());
numPane = new NumberLines();
numPane.setPreferredSize(new Dimension(50,100));
please = new HoldMe();
please.setLayout(new BorderLayout());
please.add(this,BorderLayout.CENTER);
please.add(numPane,BorderLayout.WEST);
editScroll = new JScrollPane(please);
editScroll.setPreferredSize(new Dimension(500,500));
editScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
editScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.getContentPane().add(editScroll);
window.pack();
window.setVisible(true);
}
private class HoldMe extends JPanel implements Scrollable{
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize(); //tell the JScrollPane that we want to be our 'preferredSize' - but later, we'll say that vertically, it should scroll.
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
return 16;//set to 16 because that's what you had in your code.
}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return 16;//set to 16 because that's what you had set in your code.
}
public boolean getScrollableTracksViewportWidth() {
return true;//track the width, and re-size as needed.
}
public boolean getScrollableTracksViewportHeight() {
return false; //we don't want to track the height, because we want to scroll vertically.
}
}
private class NumberLines extends JPanel {
NumberLines() {
setBackground(new Color(120,120,120));
setOpaque(false);
repaint();
}
#Override
protected void paintComponent(Graphics g) {
g.fillRect(0,0,this.getWidth(),this.getHeight());
}
}
private class TextWrapKit extends StyledEditorKit {
ViewFactory defaultFactory=new TextWrapFactory();
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
private class TextWrapFactory implements ViewFactory {
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new TextWrapView(elem);
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
return new ParagraphView(elem);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
return new BoxView(elem, View.Y_AXIS);
} else if (kind.equals(StyleConstants.ComponentElementName)) {
return new ComponentView(elem);
} else if (kind.equals(StyleConstants.IconElementName)) {
return new IconView(elem);
}
}
// default to text display
return new LabelView(elem);
}
}
private class TextWrapView extends LabelView {
public TextWrapView(Element elem) {
super(elem);
}
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
}
EDIT: code replaced with SCCE, sorry for the trouble
Okay, so it's structured like this...
• Top class - extends JTextPane
• Place this JTextPane into a JPanel set to BorderLayout CENTER
• Place another JPanel into the same JPanel set to WEST
• Place the JPanel holding the two into a JScrollPane
• Ready for deployment (nope)
Basically, I can't figure out a way to do this without losing out on something critical. I can get them all together, but I'll lose scrolling. Or I'll get scrolling back, but then lose text wrapping. One time I got wrapping, and technically it scrolled but not as it should have, in otherwords, the scrollpane didn't detect the size of the text. I need the nested JPanel (1st one) to scroll with the JTextPane
The problem area is in the constructor below, sorry it's such a mess but I'm losing it over here trying different things... Probably isn't helping. I'd be so grateful if somebody could help me figure this out.
Thank you for reading.
import javax.swing.*;
import java.awt.*;
import javax.swing.text.*;
import java.awt.BorderLayout;
public class MyEditor extends JTextPane {
JFrame window;
JScrollPane editScroll;
public static void main(String args[]) {
new MyEditor();
}
MyEditor() {
setOpaque(false);
setEditorKit(new TextWrapKit());
JPanel numPane = new JPanel();
JPanel packEdit = new JPanel();
packEdit.setLayout(new BorderLayout());
packEdit.add(this,BorderLayout.CENTER);
packEdit.add(numPane,BorderLayout.WEST);
editScroll = new JScrollPane(packEdit);
editScroll.setPreferredSize(new Dimension(500,500));
editScroll.setViewportView(packEdit);
editScroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
editScroll.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.add(editScroll);
window.pack();
window.setVisible(true);
}
private class TextWrapKit extends StyledEditorKit {
ViewFactory defaultFactory=new TextWrapFactory();
public ViewFactory getViewFactory() {
return defaultFactory;
}
}
private class TextWrapFactory implements ViewFactory {
public View create(Element elem) {
String kind = elem.getName();
if (kind != null) {
if (kind.equals(AbstractDocument.ContentElementName)) {
return new TextWrapView(elem);
} else if (kind.equals(AbstractDocument.ParagraphElementName)) {
return new ParagraphView(elem);
} else if (kind.equals(AbstractDocument.SectionElementName)) {
return new BoxView(elem, View.Y_AXIS);
} else if (kind.equals(StyleConstants.ComponentElementName)) {
return new ComponentView(elem);
} else if (kind.equals(StyleConstants.IconElementName)) {
return new IconView(elem);
}
}
// default to text display
return new LabelView(elem);
}
}
private class TextWrapView extends LabelView {
public TextWrapView(Element elem) {
super(elem);
}
public float getMinimumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
return super.getMinimumSpan(axis);
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
}
}
extends JTextPane
Why?
setEditorKit(new TextWrapKit());
Why?
Get the scroll pane working with the default components. Then if for some reason you still need to customize you make the changes one at a time to make sure it still works. If it doesn't work any more then you know what the problem is.
packEdit.add(numPane,BorderLayout.WEST);
Looks to me like you are trying to add line number. The standard way to do this is to add your component to the row header of the scrollpane, instead of making it part of the panel added to the viewport. See Text Component Line Number for an example of this approach.
In the future a SSCCE should be posted with every question. You get one free (attempted) answer without a SSCCE.
I have a problem that the JFrame is not showing upmy components.
When i opened the GasStationPanel in WindowBuilder it show well, but the MainFrame is show as a blank windows.
Please, I need you help here.
Thanks!
The JFrame code is:
public class MainFrame extends JFrame {
private GasStationPanel pnlMainGasStation;
public MainFrame() throws SecurityException, IOException {
try { UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(this);
} catch (Exception e) {
e.printStackTrace();
}
getContentPane().setLayout(new BorderLayout());
this.pnlMainGasStation = new GasStationPanel("all cars","pumps","coffee");
this.add(pnlMainGasStation, BorderLayout.CENTER);
setLocationRelativeTo(null);
setTitle("GasStation");
addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
Utils.closeApplication(MainFrame.this);
}
});
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Dimension frameSize = new Dimension();
frameSize.setSize(screenSize.width*0.7, screenSize.height*0.9);
setSize(frameSize);
setVisible(true);
}
public GasStationPanel getMainPanel() {
return pnlMainGasStation;
}
}
The GasStationPanel Code:
public class GasStationPanel extends JPanel {
private JSplitPane splinterRight, splinterLeft;
private AllCarsPanel allCarsPanel;
private FuelPumpListPanel fuelPumpsListPanel;
private CoffeeHousePanel coffeeHousePanel;
private List<GasStationController> allListeners;
public AllCarsPanel getAllCarsPanel() {
return allCarsPanel;
}
public FuelPumpListPanel getFuelPumpsListPanel() {
return fuelPumpsListPanel;
}
public CoffeeHousePanel getCoffeeHousePanel() {
return coffeeHousePanel;
}
public GasStationPanel(String allCarsStr, String fuelPumpsListStr,
String coffeeHousePanelStr) throws SecurityException, IOException {
// Init Listeners List
this.allListeners = new ArrayList<GasStationController>();
// Layout and size
setLayout(new BorderLayout());
// Build panels
allCarsPanel = new AllCarsPanel();
fuelPumpsListPanel = new FuelPumpListPanel();
coffeeHousePanel = new CoffeeHousePanel();
// Split the screen to three
splinterRight = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splinterLeft = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
splinterLeft.setLeftComponent(allCarsPanel);
splinterLeft.setRightComponent(fuelPumpsListPanel);
splinterRight.setLeftComponent(splinterLeft);
splinterRight.setRightComponent(coffeeHousePanel);
}
public void registerListener(GasStationController gasStationController) {
this.allListeners.add(gasStationController);
}
In short, you never add any components to your container. For example, in your GasStationPanel code, perhaps you should try invoking add(Component) by passing in your JSplitPanes as an argument. For example:
add(splinterLeft, BorderLayout.CENTER);