问题
I recently have had the need to put several Components, a JTextPane among them, inside of a single JScrollPane's viewport view. (See details here, if interested).
I put all the Components (two JPanels and the JTextPane) inside of another JPanel, this one having a BorderLayout LayoutManager, and set that JPanel as the ScrollPane's viewport view.
What I immediately noticed was:
The JTextPane doesn't resize its width according to the width of the JScrollPane anymore, even when the JScrollPane is set to HORIZONTAL_SCROLLBAR_NEVER. Instead, it just maximizes its width to fit the entire text inside into one line (apart from "\n" line breaks).
Here are two pictures of how it looks:
And this is how it should look like instead:
Now, I found this question here which answers the first part of the question. The JPanel needs to be subclassed and the subclass needs to implement Scrollable.
However, another problem arises with this. The JTextPane inside of the JPanel doesn't honor the JPanel's BorderLayout.CENTER anymore. If the text is doesn't fill the remaining space in the window, the JTextPane will not stretch itself anymore.
What I am looking for is a solution for this.
I would like to have a reliable way to make the JTextPane honor the BorderLayout of the JPanel again and stretch itself accordingly. I'm not looking for some workaround, like making the background white so that the JTextPane appears to be filling the frame when it in fact isn't.
Below, I have posted some code where you can try out the different configurations.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
import java.awt.GridLayout;
import java.awt.Rectangle;
import javax.swing.JLabel;
import javax.swing.JTextPane;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.Scrollable;
public class MinimalExample extends JFrame {
private JPanel contentPane;
private JPanel outerPanel;
private JPanel firstHeadlinePanel;
private JLabel lblSomeJlabel;
private JPanel secondHeadlinePanel;
private JLabel lblAnotherJlabel;
private JPanel headlinesPanel;
private JTextPane textPane;
private JScrollPane scrollPane;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
MinimalExample frame = new MinimalExample();
frame.setVisible(true);
}
catch (Exception e) {
e.printStackTrace();
}
}
});
}
public MinimalExample() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setBounds(100, 100, 230, 350);
contentPane = new JPanel();
contentPane.setBorder(null);
setContentPane(contentPane);
contentPane.setLayout(new GridLayout(1, 0, 0, 0));
outerPanel = new ScrollablePanel();
//outerPanel = new JPanel();
outerPanel.setLayout(new BorderLayout(0, 0));
headlinesPanel = new JPanel();
headlinesPanel.setBorder(new EmptyBorder(5, 0, 0, 0));
outerPanel.add(headlinesPanel, BorderLayout.NORTH);
headlinesPanel.setLayout(new BorderLayout(0, 0));
firstHeadlinePanel = new JPanel();
firstHeadlinePanel.setBorder(new EmptyBorder(0, 0, 5, 0));
headlinesPanel.add(firstHeadlinePanel, BorderLayout.NORTH);
firstHeadlinePanel.setLayout(new BorderLayout(0, 0));
lblSomeJlabel = new JLabel("Some JLabel");
firstHeadlinePanel.add(lblSomeJlabel, BorderLayout.CENTER);
secondHeadlinePanel = new JPanel();
headlinesPanel.add(secondHeadlinePanel, BorderLayout.SOUTH);
secondHeadlinePanel.setLayout(new BorderLayout(0, 0));
lblAnotherJlabel = new JLabel("Another JLabel");
lblAnotherJlabel.setBorder(new EmptyBorder(0, 0, 5, 0));
secondHeadlinePanel.add(lblAnotherJlabel, BorderLayout.NORTH);
textPane = new JTextPane();
textPane.setText(addSomeShortText());
//textPane.setText(addSomeLongText());
outerPanel.add(textPane);
scrollPane = new JScrollPane();
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
scrollPane.setViewportView(outerPanel);
contentPane.add(scrollPane);
}
private String addSomeShortText() {
return ("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur facilisis "
+ "dictum ullamcorper. Nulla sed diam sit amet ante pellentesque ultricies in non "
+ "est.");
}
private String addSomeLongText() {
return (" Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur facilisis "
+ "dictum ullamcorper. Nulla sed diam sit amet ante pellentesque ultricies in non "
+ "est. Suspendisse sit amet leo purus. Aenean lacinia ornare quam, a porttitor "
+ "tortor vulputate quis. Quisque suscipit porttitor nisl, mollis suscipit ligula "
+ "laoreet vitae. Nulla quis faucibus dui. Donec vestibulum quam sit amet "
+ "facilisis scelerisque. Pellentesque eu lacus eget nibh elementum venenatis in "
+ "congue magna. Donec et vestibulum magna, ut dapibus sapien.\n"
+ "Donec nec enim magna. Curabitur vitae est est. Etiam non tellus nec ipsum "
+ "dapibus ultricies eget ac lacus. Aliquam eu rhoncus leo. Suspendisse sodales "
+ "nibh eros, vel placerat velit bibendum ut. Vivamus feugiat purus at viverra "
+ "volutpat. Donec tincidunt quam quam, ut mattis enim rutrum sit amet. Integer "
+ "volutpat sit amet metus vitae elementum. Aenean ullamcorper iaculis vulputate. "
+ "Etiam venenatis mollis arcu quis interdum. Quisque et pharetra justo, et "
+ "ullamcorper augue. Phasellus id pellentesque massa. Proin suscipit, mauris et "
+ "congue commodo, mi eros fermentum ante, vel ultricies nisl neque a sem. Mauris "
+ "tristique quis erat vel pulvinar. ");
}
/* Original source of this is here:
* https://stackoverflow.com/questions/15783014/jtextarea-on-jpanel-inside-jscrollpane-does-not-resize-properly */
private static class ScrollablePanel extends JPanel implements Scrollable {
@Override
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
@Override
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
int direction) {
return 16;
}
@Override
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation,
int direction) {
return 16;
}
@Override
public boolean getScrollableTracksViewportWidth() {
return true;
}
@Override
public boolean getScrollableTracksViewportHeight() {
return false;
}
}
}
Thanks in advance for any help you can give me!
回答1:
Okay, so basically, you need to change the way getScrollableTracksViewportHeight
works, based on the JViewport
's and your component's height.
You basically want the component to track the viewport's height (adjusting itself to match) while the viewport's height is greater then the component's preferred height. When the component's preferred height is greater then the viewport's, you want to stop tracking and allow the component to "overfill" the scroll pane, something like...
@Override
public boolean getScrollableTracksViewportHeight() {
boolean track = true;
Container parent = getParent();
if (parent instanceof JViewport) {
JViewport viewport = (JViewport) parent;
if (viewport.getHeight() < getPreferredSize().height) {
track = false;
}
}
return track;
}
This is "basically" how things like JTable
and JList
work
来源:https://stackoverflow.com/questions/32514168/jtextcomponent-inside-jpanel-inside-jscrollpane