I've been on this a couple of hours now, and not making any progress.
I have a very simple application which loads a large (2000x2500px) image from disk.
It loads into a JPanel, which is contained by a JScrollPanel, which in turn is contained by the outermost JPanel.
This code is in Scala, but should be easy enough for a Java developer to read.
When I move the scrollbar, the application prints out an indication that it is being repainted, but the image is left partially blank. When I resize the application pane, everything is drawn correctly.
Is this a bug or am I missing something really basic?
import java.awt._
import java.util.Date
import javax.imageio.ImageIO
import javax.swing._
object DisplayAnImage {
def main(args: Array[String]) = {
val rackStream = DisplayAnImage.getClass.getResourceAsStream("racks.jpg")
val bi = ImageIO.read(rackStream)
val outerMost = new JFrame("Image display")
outerMost.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
outerMost.setLayout(new BorderLayout())
val imagePane = new JPanel(true) {
val c = new Canvas() {
override def paint(g: Graphics): Unit = {
println(s"drawing the image ${new Date()}")
g.drawImage(bi, 0, 0, null)
}
}
c.setSize(bi.getWidth, bi.getHeight)
this.add(c)
}
val scroller = new JScrollPane(imagePane)
scroller.setAutoscrolls(true)
outerMost.add(scroller, BorderLayout.CENTER)
outerMost.pack()
outerMost.setSize(300, 300)
outerMost.setVisible(true)
}
}
With thanks to camackr - I modified the code to the following, it's simpler and works perfectly.
import java.awt._
import javax.imageio.ImageIO
import javax.swing._
object DisplayAnImage {
def main(args: Array[String]) = {
val rackStream = DisplayAnImage.getClass.getResourceAsStream("racks.jpg")
val bi = ImageIO.read(rackStream)
val outerMost = new JFrame("Image display")
outerMost.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
outerMost.setLayout(new BorderLayout())
val imagePane = new JPanel(true) {
val jl = new JLabel() {
override def paintComponent(g: Graphics): Unit = {
g.drawImage(bi, 0, 0, null)
}
this.setPreferredSize(new Dimension(bi.getWidth,bi.getHeight))
}
this.add(jl)
}
val scroller = new JScrollPane(imagePane)
scroller.setAutoscrolls(true)
outerMost.add(scroller, BorderLayout.CENTER)
outerMost.pack()
outerMost.setSize(300, 300)
outerMost.setVisible(true)
}
}
A scrollpane works based on the preferred size of the component added to the scroll pane. You are doing custom painting but not providing a preferred size for your component.
Why are you doing custom painting? Why would you use a Canvas? Why are you overriding paint(), custom painting is done by overriding paintComponent() when using Swing.
Anyway, just use a JLabel to display the image and add the label to the scrollpane. The label will automatically determine the preferred size of itself based on the size of the image.