我想实现一个自定义 JScrollPane,它能够在其自己的 ViewPort/ViewPort 组件的顶部的客户区域中进行绘制。这样做的挑战是当用户滚动 ViewPort 时保持自定义绘制代码重新绘制,或者换句话说,ViewPort 组件被重新绘制。
我的目标是与 iOS (iPhone/iPad) 的半透明滚动条具有相同的效果,它呈现在可滚动列表的顶部(而不是经典的滚动条)。
所以这只是一个绘画问题,而不是一个关于滚动的问题:)
我已经有了一些工作要做,如下所示(下面的代码仅在 JScrollPane 上方呈现一条绿色测试线):
public class ScrollPaneTest
{
private JFrame frame;
private TestScrollPane scrollPane;
private JTextArea textArea;
public static void main( String[] args ) throws Exception
{
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
new ScrollpaneTest();
}
public ScrollPaneTest()
{
configureUi();
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
frame.setVisible( true );
}
} );
}
private void configureUi()
{
textArea = new JTextArea();
textArea.setWrapStyleWord( true );
textArea.setLineWrap( true );
textArea.setOpaque( false );
textArea.setText( "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor "
+ "invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo "
+ "duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor "
+ "sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor "
+ "invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et "
+ "justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum "
+ "dolor sit amet." );
scrollPane = new TestScrollPane();
scrollPane.setViewportView( textArea );
scrollPane.setLocation( 50, 50 );
scrollPane.setSize( 200, 200 );
frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setLayout( null );
frame.setSize( 340, 380 );
frame.setLocationRelativeTo( null );
frame.add( scrollPane );
}
}
public class TestScrollPane extends JScrollPane
{
public TestScrollPane()
{
setOpaque( true );
setBackground( Color.WHITE );
}
@Override
protected JViewport createViewport()
{
JViewport viewPort = super.createViewport();
viewPort.setOpaque( false );
return viewPort;
}
@Override
protected void paintChildren( Graphics g )
{
super.paintChildren( g );
Graphics2D g2d = (Graphics2D)g;
g2d.setColor( Color.GREEN );
g2d.drawLine( 0, 0, 100, 100 );
}
}
运行代码,看起来像这样:
这里的关键是将 ViewPort 和 ViewPort 组件设置为非透明:
// From: ScrollPaneTest.configureUi()
textArea.setOpaque( false );
和:
// From: TestScrollPane.paintChildren()
JViewport viewPort = super.createViewport();
viewPort.setOpaque( false );
(如果不将这些设置为非不透明,则 JScrollPane 将不会被重新绘制,并且 ViewPort 组件(JTextArea)将覆盖绿线)。
现在我相当简单的问题是:
有更好的方法吗?必须在两个组件上使用 setOpaque( false)
感觉不太对。
非常感谢您的帮助!
最佳答案
Having to use setOpaque( false) on both components just doesn't feel right.
嗯,也许更容易一点。您可以重写 JViewport 的 PaintComponent() 方法,然后您只需在文本区域上使用 setOpaque(false) ,而不是视口(viewport):)
import java.awt.*;
import javax.swing.*;
public class ScrollPaneTest5
{
private JFrame frame;
private JScrollPane scrollPane;
private JTextArea textArea;
public static void main( String[] args ) throws Exception
{
UIManager.setLookAndFeel( "com.sun.java.swing.plaf.windows.WindowsLookAndFeel" );
new ScrollPaneTest5();
}
public ScrollPaneTest5()
{
configureUi();
SwingUtilities.invokeLater( new Runnable() {
@Override public void run() {
frame.setVisible( true );
}
} );
}
private void configureUi()
{
textArea = new JTextArea();
textArea.setWrapStyleWord( true );
textArea.setLineWrap( true );
// textArea.setOpaque( false );
textArea.setText( "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor "
+ "invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo "
+ "duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor "
+ "sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor "
+ "invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et "
+ "justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum "
+ "dolor sit amet." );
scrollPane = new JScrollPane();
// scrollPane.setViewportView( textArea );
scrollPane.setLocation( 50, 50 );
scrollPane.setSize( 200, 200 );
frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setLayout( null );
frame.setSize( 340, 380 );
frame.setLocationRelativeTo( null );
frame.add( scrollPane );
JViewport viewport = new JViewport()
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor( Color.BLUE );
g.drawArc( 100, 100, 80, 80, 0, 360);
}
};
textArea.setOpaque( false );
viewport.setView(textArea);
scrollPane.setViewport( viewport );
}
}
否则,我认为您需要使用JLayer
类。请参阅 Swing 教程中关于 How to Decorate Components with the JLayer Class 的部分例如。
关于Java 6/Swing : Paint on top of JScrollPane's ViewPort/ViewPort-component,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24975898/