Aligning components of a JScrollPane to the left? - java

Currently, I have a JScrollPane nested within a Panel having a BorderLayout. More importantly, within the JScrollPane, I intend to have multiple text areas listed vertically, with a width that does not exceed the JScrollPane's, and as much height as they need (with the scroll pane having only a vertical scroll bar).
My problem is that the text areas appear to be centered in the JScrollPane relative to the largest text area, and they are all one line.
Can anyone tell me, looking at my code, how I can get the text areas to wrap before the right side of the scroll pane, and how they can be left aligned?
private JPanel generateStateView(State state) {
//Create a new panel, and the info label.
JPanel container = new JPanel(new BorderLayout());
JLabel infoLabel = new JLabel("The state of " + state.getName() +
", with " + state.getElectoralVotes() + " Electoral Votes.");
container.add(infoLabel, BorderLayout.NORTH); //Put the label on top.
//Will scroll through polls.
JScrollPane pollViewer = new JScrollPane();
//This will be the scroll pane's "viewport".
JViewport pollViewport = new JViewport();
//And finally, this will actually hold the individual polls.
JPanel pollPanel = new JPanel(new GridLayout(
state.getPolls().size(), 1));
pollPanel.setAlignmentX(LEFT_ALIGNMENT);
//Iteratively add the polls to the container
for (Poll poll : state.getPolls()) {
//Holds individual polls
Box pollBox = Box.createHorizontalBox();
//Create the poll's panel and add it to the poll container.
pollBox.add(generatePollPanel(poll));
pollBox.add(Box.createHorizontalGlue()); //Fill to the right
pollBox.setAlignmentX(LEFT_ALIGNMENT);
pollPanel.add(pollBox); //Put the box into the pollPanel.
}
//Put the panel into the viewport.
pollViewport.add(pollPanel);
//Put the viewport "into" the scroll pane
pollViewer.setViewport(pollViewport);
//Put the pane into the state view.
container.add(pollViewer, BorderLayout.CENTER);
return container; //And give back the container.
}
/**
* Method: generatePollPanel
* Purpose: Generates a panel containing information on a particular poll.
* #param poll The poll to have information generated from.
* #return The JPanel.
*/
private JPanel generatePollPanel(Poll poll) {
//Create a new panel, then a text area to fill with the info.
JPanel pollPanel = new JPanel();
JTextArea pollInfo = new JTextArea("Conducted by " + poll.getAgency() +
" on day " + poll.getDate() + " for " + poll.getNumDays() +
" days, " + poll.getPercentVoteDem() +
"% voted Democrat, and " + poll.getPercentVoteRep() +
"% voted Republican.");
pollInfo.setEditable(false); //We don't want the user editing this.
pollPanel.add(pollInfo); //Throw the area in, and return the panel.
pollPanel.setAlignmentX(LEFT_ALIGNMENT);
return pollPanel;
}
/**
* Method: valueChanged
* Purpose: Handle the event of a different list item being selected.
* #param event The object containing information about this event.
*/
public void valueChanged(ListSelectionEvent event) {
//Instantiating JList with a type of ? seems to solve the issue of
//eclipse complaining about an unchecked cast (originally to
//JList<String>, so I should be able to cast child values directly
//to string later on anyways.
JList<?> stateList = (JList<?>) event.getSource();
//Account for keyboard and mouse actions
if (!event.getValueIsAdjusting()) {
State chosenState; //Keep track of the picked state.
try {
//Try to get the currently selected state
chosenState = db.findState((String) stateList.getSelectedValue());
}
catch (NoSuchElementException exception) {
//Somehow the picked state was not found, notify the user.
reportError("The selected state is not available.");
return;
}
//Find the container for the gui window.
Container container = getContentPane();
//Remove the empty state view container by generating a new one.
container.remove(currentStateView);
//Generate the state view and add that to the container.
currentStateView = generateStateView(chosenState);
container.add(currentStateView, BorderLayout.CENTER);
//Redraw everything.
revalidate();
}
}

Looks like a severe case of lost-in-nesting :-)
While it nesting can be a means to achieve layout requirements, it can be hard to find out what exactly goes wrong if it doesn't give the intended output. Also, there are general warning signals that something is basically wrong with the nesting
deep levels of containers with wildly varying managers
containers with only a single child
In your case, all except the top-level borderlayout have a single "netto" (that is, not intended to hack around a layout issue) child, with levels:
BorderLayout (the state view)
GridLayout (the panel that's the scrollPane's view)
BoxLayout (the panel that contains a single pollBox)
FlowLayout (the panel that contains the textArea)
The experienced problems:
text areas are single-line without ever wrapping
text areas are centered
are both due to the characteristics of the LayoutManagers, either partly (first) or completely (second):
The part that's not due the manager is that a textArea doesn't wrap by default, you have to explicitly configure it to do so. The part that's due to the manager (== FlowLayout) is that it always lays out its children according to their prefSize, nothing else. That boils down to have a fixed child size, no matter how much space is available for the parent. So even if the textArea were configured to wrap, it would stick to that initial size, no matter how wide its parent can get.
The inner FlowLayout aligns its children ... centered. So whatever alignment you set on the level of the BoxLayout doesn't have any effect. As doesn't the glue: it only takes all the excess space if the other child/ren have a maximum size in that dimension (which a panel with FlowLayout does not have, simply because it's that simple LayoutManager doesn't have the notion of max, only managers of type LayoutManager2 have)
Time to take a step back and review the inital requirement:
multiple text areas listed vertically, with a width that does not
exceed the JScrollPane's, and as much height as they need
The zeroth order solution is to
configure the textArea as needed
throw out all layout nesting (inside the scrollPane)
In code:
JComponent overview = new JPanel(new BorderLayout());
overview.add(new JLabel("All polls for state XY"), BorderLayout.NORTH);
JComponent pollPanel = new JPanel();
pollPanel.setLayout(new BoxLayout(pollPanel, BoxLayout.PAGE_AXIS));
for (int i = 0; i < 10; i++) {
pollPanel.add(generatePollTextArea(i)); //Put the box into the pollPanel.
}
pollPanel.add(Box.createVerticalGlue());
JScrollPane pollViewer = new JScrollPane(pollPanel);
overview.add(pollViewer);
showInFrame(overview, "layout text only");
protected JComponent generatePollTextArea(int i) {
String agency = "agency ";
// simulates different size text
for (int j = 0; j < i; j++) {
agency += i;
}
String text = "Conducted by " + agency +
" on day " + "today" + " for " + 20 +
" days, " + 99 +
"% voted Democrat, and " + 0.2 +
"% voted Republican.";
JTextArea pollInfo = new JTextArea(text);
// give it some reasonable initial width
// in terms of content
pollInfo.setColumns(20);
pollInfo.setLineWrap(true);
pollInfo.setWrapStyleWord(true);
pollInfo.setEditable(false); //We don't want the user editing this.
return pollInfo;
}
"Das Wort zum Dienstag": nesting layouts isn't a means of avoiding to learn the characteristics of the LayoutManagers used on the individual levels of the nested layout.

Firstly, I don't think Box is suppose to be used with anything other then the BoxLayout (I could be wrong, but that's how I read it)
Personally, I'd either use either GridBagLayout or VerticalLayout from SwingLabs.
In order to facilitate the horizontal restrictions, you will also want to take a look at the Scrollable interface, in particular getScrollableTracksViewportWidth

Related

When typing into JTextArea, text in JLabel moves

I'm making a simple GUI and have a problem.
This is my code :
JFrame jFrame = new JFrame();
jFrame.setTitle("Simple Editor");
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setLocation(50,50);
jFrame.setResizable(true);
Box box = new Box(BoxLayout.Y_AXIS);
JTextArea jTextArea = new JTextArea();
jTextArea.setPreferredSize(new Dimension(470,500));
JLabel jLabel = new JLabel();
box.add(jTextArea);
box.add(jLabel);
jLabel.setText("Font type : " + Main.fontType + " font size : " + Main.size
+ " background color : " + Main.backgroundColor
+ " font color : " + Main.fontColor);
jFrame.setContentPane(box);
jFrame.pack();
jFrame.setVisible(true);
When I typing something in JTextArea, text in JLabel is moving. I can't figure out how to solve this. Maybe some component between them? Any advice and help is welcome.
This looks like an artifact of how the Box is calculating sizes and locations. Note that some components and layout managers do not use setPreferredSize, or only take it as a hint, or use it as only one part of a computation, or etc. so it cannot be depended upon as a reliable method to set the size of a component.
In this case, I would hypothesize what is going on is something like: BoxLayout generally uses minimum/maximum sizes, not preferred sizes, and the min/max of a JTextArea is computed based on its text content. As the text changes, the size is recalculated so the layout changes too.
In general if you have a text area, you should put it in a JScrollPane instead:
Box box = new Box(BoxLayout.Y_AXIS);
JTextArea jTextArea = new JTextArea();
JScrollPane jScrollPane = new JScrollPane(jTextArea);
jScrollPane.getViewport().setPreferredSize(new Dimension(470,500));
JLabel jLabel = new JLabel();
box.add(jScrollPane);
box.add(jLabel);
This way when the text content changes in the JTextArea it can simply do its thing, recalculating its size, and flow out the side of the scroll pane.
Also see How to Use Scroll Panes, How to Use Text Areas.
Per Andrew's comment, here are a couple ways to set the initial size of the scroll pane which are perhaps more reliable than setting the viewport's preferred size explicitly:
// specify rows & columns
JTextArea jTextArea = new JTextArea(20, 20);
// specify preferred scrollable viewport size
JTextArea jTextArea = new JTextArea() {
#Override
public Dimension getPreferredScrollableViewportSize() {
return new Dimension(470,500);
}
};
jTextArea.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e) {
jLabel.setText(jTextArea.getText());
}
});
where,
jTextArea - your name object of JTextArea class
jLabel - your name object of JLabel class
You add text in the textarea and text in the label is changing. I think, this code help you to decide your problem.

JtextPane with a touch interface

I'm developing a touch based windows 7 app using a tuio client-server setup and a touch library that extends multitouch 4 java.
One of the functionality I'm struggling with is to enable text highlighting when using touch.
I display a simple txt file using a JTextPane to display the text, the highlighting is done by through a drag action.
I get the clicked position where the drag event starts and then when it stops and tried to convert those coordinates to the text panel's space but I get different values from the ones that I should have, usually before the actual text.
The code I'm using to display the document is the following:
//Create the JDialog that is the container of it
window = new JDialog(parent);
window.setUndecorated(true);
//Create the JTextPane
text = new JTextPane();
text.setPage(newFile.toURI().toURL());
text.setEditable(false);
text.setHighlighter(null);
//ScrollPane that will be used to display the text
JScrollPane scroll = new JScrollPane(text);
scroll.setPreferredSize(new Dimension(500, 700));
window.getContentPane().add(scroll, BorderLayout.CENTER);
window.pack();
window.setVisible(true);
window.validate();
Where the JDialog parent is the main display component used in my app.
The drag is handled as follows:
#Override
public boolean processGestureEvent(GestureEvent ge) {
if((ge instanceof DragEvent) && this.component.isHighlight())
{
tapCount=0;
if(this.component.isHighlight())
{
//do highlighting
DragEvent drag = (DragEvent) ge;
switch (drag.getId()) {
case GestureEvent.GESTURE_STARTED:
Point start = drag.getFrom();
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
startPos = this.textDisplay.viewToModel(calcStart);
break;
case GestureEvent.GESTURE_ENDED:
Point end = drag.getTo();
Point calcEnd = new Point(end.x - compPosition.x, end.y - compPosition.y);
endPos = this.textDisplay.viewToModel(calcEnd);
System.out.println("I have this positions:" + startPos + "/" + endPos);
System.out.println("Should have " + this.textDisplay.getSelectionStart() + "/" + this.textDisplay.getSelectionEnd());
System.out.println("And the text is: " + this.textDisplay.getText().substring(startPos, endPos));
break;
case GestureEvent.GESTURE_CANCELED:
startPos = 0;
endPos = 0;
break;
}
}
return true;
}
In which the compPosition is the JDialog's position that holds the text pane. I'm simulating touch with the mouse so the correct text position for the highlighting I'm getting from the built-in highlighting functionality of the text pane with mouse.
Is the problem because of the JDialog and the JScroll pane that somehow skews the conversion?
The coordinate system for the point's I get from the touch are with the origin in the top left corner of the screen and the text pane's coordinate system origin is in the same top lef corner.
Any ideas on how can I solve the problem? Any suggestions are appreciated.
LE:
I was doing something wrong in that I was adding the gesture processor when I initialized the component and it's position was (0,0) and only afterwards I moved it where I wanted to.
I changed the position calculations as follows:
Point calcStart = new Point(start.x - this.component.getLocation().x, start.y -this.component.getLocation().y);
passing instead a reference to the actual component and getting the location when needed.
try instead of
Point calcStart = new Point(start.x - compPosition.x, start.y - compPosition.y);
to use
Point calcStart = new Point(start.x, start.y);
i wonder how it gonna end, so give us some value of what you get

Why does the last item added to a panel occupy the whole panel?

I'm adding a quantity of JTextField to a panel, and all of them are added but, the last one added takes the whole panel and seems all other text boxes added on the last one..... here is the code
public JPanel crearCartonFormulario() {
panel = new JPanel();
panel.setLayout(new BorderLayout());
JTextField[] textBoxes = new JTextField[25];
int cont = 0;
int posX = 10;
int posY = 0;
llenarArreglo();
while (cont <= 4) {
for (int i = 0; i <= 4; i++) {
if (cont == 2 && i == 2) {
textBoxes[i] = new JTextField("");
} else {
textBoxes[i] = new JTextField(String.valueOf(numeros[cont][i]));
}
textBoxes[i].setBounds(i + posX, 15 + posY, 40, 40);
textBoxes[i].setEditable(false);
panel.add(textBoxes[i]);
posX += 50;
}
posY += 50;
posX = 10;
cont++;
}
return panel;
}
This is returned at a panel where I keep multiple panels of this one, it works but in this one the last JTextField takes the whole panel space....
The new JFrame that contains the panels created by the method, adopt the last JTextField size and that text box doesn't take the bounds indicated by the method, but all the other text boxes still inside and correctly added.
panel.setLayout(new BorderLayout());
You are using a BorderLayout.
panel.add(textBoxes[i]);
When you use the add() method the default is to add the component to the CENTER of the BorderLayout. However, only a single component can be added to the center so the layout manager will only manage the size/location of the last component added. The rules of the BorderLayout is to make the component take up all the available space.
However, you have also used the setBounds() methods for the other text fields which is causing a problem. You should NOT attempt to use a layout manager and manage the bounds of the components yourself.
The solution is to just use a layout manager and let the layout manager do its job. Read the section from the Swing tutorial on Using Layout Managers for more information and use a more appropriate layout manager.
Update:
its a bingo table
Then maybe you shouldn't even be using JTextFields. Maybe a JTable would be a better component to use. The tutorial also has a section on How to Use Tables.
Your problem is here:
panel.setLayout(new BorderLayout());
You set the layout to BorderLayout and yet add components to the JPanel as if it were a GridLayout. Understand that when you add components to a BorderLayout-using container in a default way, the components get added in the BorderLayout.CENTER position which fills this position, covering anything added prevsiously.
Perhaps you wish to use a GridLayout instead? You will want to read the layout manager tutorial for more.
This is because you are using BorderLayout and BorderLaout Always requires a parameter like BorderLayout.CENTER, BorderLayout.WEST, BorderLayout.EAST, BorderLayout.NORTH and BorderLayout.SOUTH.
So basically BorderLayout only has 5 position where a component can go. And if you do not specify where when adding a component it defaults to BorderLayout.CENTER. And as there can only be one component at a time in the BorderLayout.CENTER position it only really adds the last one. So I'd suggest an other layout manager like GridLayout( if you want all the components to be equally sized).
I hope this helps :).
P.S. If you want me to give some explination on GridLayout just ask.

JDialog.pack() hides JLabel

I am trying to write a form in java, but after dynamically inserting JLabels to the current JDialog and doing a pack() the windows is resized to minimum. The JLabels are displayed, but I have to resize the window manually.
Here is the part where the JLabels are inserted:
public void displayQuizz(Test quiz){
int xLable = 44;
int yLable = 41;
int widthLable = 403;
int heightLable = 70;
int noOfQuestion = 1;
for(Question question : quiz.getQuestions()){
JLabel lblNewLabel = new JLabel(Integer.toString(noOfQuestion) + ". " + question.getStatement());
lblNewLabel.setBounds(xLable, yLable, widthLable, heightLable);
contentPanel.add(lblNewLabel);
contentPanel.revalidate();
contentPanel.repaint();
this.pack();
noOfQuestion++;
yLable += heightLable;
}
}
The pack() method sets the size of a Window (where JFrame and JDialog are subclasses from) to the preferred size.
The preferred size is determined by
The LayoutManager, which takes the arrangement of the components and
their preferred size into account
The component itself, if it does not have a layout manager
As you don't use a layout manager in your example (and set the bounds of the label manually), you also have to specify the preferred size yourself (see getPreferredSize(), the default is 0x0, that's the problem you encountered).
I'd encourage you to get used to always use layout managers (there's quite a lot of them, and you can easily write your own layout manager strategy if none suffices your needs).

Reduce gap between JPanels

I have a simple Class StatPanel that extends JPanel and has a few JTextFields, a JCheckBox, and a couple JLabels. The panel is going to be used as a series of panels used to display information and allow the user to edit the data in some of the JTextFields and I will probably have about 40 of them all stacked together on the side of the program in a JScrollPane JPanel. The layout I am using is a GridLayout(0,1).
I have them working but when the are in the scroll panel they are spaced too far apart vertically. I have tried changing the margins, changing the spacing in the GridLayout and changing the margins of the various items. Nothing I do seems to make them closer.
This is the constructor for the StatPanel class:
public StatPanel(String statName, String statAbility){
super();
this.skillName = statName;
this.setAlignmentY(CENTER_ALIGNMENT);
isClassSkill = new JCheckBox();
statLabel = new JLabel(skillName);
statTotalField = new JTextField(maxLength + 1);
statTotalField.setHorizontalAlignment(JTextField.CENTER);
statAbilityLabel = new JLabel("= " + statAbility + " ");
statAbilityModField = new JTextField(maxLength);
statAbilityModField.setHorizontalAlignment(JTextField.CENTER);
statSeperator1 = new JLabel(" + ");
statRanksField = new JTextField(maxLength);
statRanksField.setHorizontalAlignment(JTextField.CENTER);
statSeperator2 = new JLabel(" + ");
statMiscModField = new JTextField(maxLength);
statMiscModField.setHorizontalAlignment(JTextField.CENTER);
this.add(isClassSkill);
this.add(statLabel);
this.add(statTotalField);
this.add(statAbilityLabel);
this.add(statAbilityModField);
this.add(statSeperator1);
this.add(statRanksField);
this.add(statSeperator2);
this.add(statMiscModField);
}
}
When I use it in the program it looks like this:
As I am going to stack so many of them I want them pretty much one on top of the other but I can seem to remove the gap between them.
How is this done?
Thank you for your help.
Check out the FlowLayout API. By default a JPanel uses a FlowLayout with default horizontal/vertical gap of 5 pixels. You will want to change the vertical gap used by the layout manager. So at the top of your method you will want to add:
setLayout( new FlowLayout(...) );
Edit:
Once you change the gap you will also lose the gap at the top/bottom of the main panel so you might want to add an EmptyBorder to the panel.

Categories