2013-12-04 2 views
2

Я использую следующий код, чтобы добавить изображение в JPanel внутри JScrollPane. Я хочу, чтобы панель прокрутки прокручивалась донизу, но, похоже, она не распознает, что изображение было добавлено до некоторого времени позже, хотя я вызываю это внутри оболочки SwingUtilities.JPanel высота не обновляется сразу

private void addImage(final String image){ 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      imagePanel.addImage(image); 
      final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar(); 
      vScroll.setValue(vScroll.getMaximum()); 
     } 
    }); 
} 

Код addImage из imagePanel выглядит следующим образом:

public void addImage(String image){ 
     System.out.println("Pre-height: " + getHeight()); 
     add(createImageLabel(image)); 
     System.out.println("After-height: " + getHeight()); 
     revalidate(); 
     System.out.println("Revalidate-height: " + getHeight()); 
    } 

Похоже, что это приводит к следующему, например:

Pre-height: 690 
After-height: 690 
Revalidate-height: 690 

Если я сплю в цикле после того, как звоните, для высоты, по-видимому, требуется около 200 мс.

Код, который у меня здесь, всегда имеет 1 изображение сзади при прокрутке. Каков наилучший способ прокрутки на дно таким образом, что я действительно знаю, где находится дно? Я немного знаком с соглашениями Swing, но я думал, что, поскольку все происходит в вызове SwingUtilities, он будет знать, что он делает.

Полный Исполняемые Пример кода, который демонстрирует проблему:

package com.foo.bar 

import java.awt.Dimension; 
import java.awt.Image; 
import java.io.IOException; 
import java.net.URL; 

import javax.imageio.ImageIO; 
import javax.swing.BoxLayout; 
import javax.swing.ImageIcon; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollBar; 
import javax.swing.JScrollPane; 
import javax.swing.SwingUtilities; 

import net.miginfocom.swing.MigLayout; 

public class ScrollImageExampleFrame extends JFrame{ 

    private ScrollImageExamplePanel siePanel; 

    public ScrollImageExampleFrame(){ 
     setSize(1024, 768); 
     siePanel = new ScrollImageExamplePanel(); 
     getContentPane().add(siePanel); 
    } 

    class ScrollImageExamplePanel extends JPanel { 
     ImageViewExamplePanel imagePanel = new ImageViewExamplePanel(); 
     JScrollPane imageScrollPane = new JScrollPane(imagePanel, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 

     public ScrollImageExamplePanel(){ 
      setLayout(new MigLayout("nogrid")); 
      imageScrollPane.getVerticalScrollBar().setPreferredSize(new Dimension(30, 0)); 
      add(imageScrollPane, "east, width 300!, height 100%"); 
     } 

     private void addImageOuter(final String image){ 
      SwingUtilities.invokeLater(new Runnable() { 
       @Override 
       public void run() { 
        imagePanel.addImageInner(image); 
        imagePanel.revalidate(); 
        final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar(); 
        vScroll.setValue(vScroll.getMaximum()); 
       } 
      }); 
     } 
    } 

    class ImageViewExamplePanel extends JPanel { 
     public ImageViewExamplePanel() { 
      setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 
     } 

     public void addImageInner(String image){ 
      URL imageUrl = ImageViewExamplePanel.class.getResource("/com/foo/bar/" + image); 
      ImageIcon ii = resizeImage(imageUrl, 360, 270); 
      JLabel label = new JLabel(); 
      label.setIcon(ii); 
      add(label); 
      revalidate(); 
     } 

     private ImageIcon resizeImage(URL url, int x, int y){ 
      Image image = null; 
      try{ 
       image = ImageIO.read(url); 
       Image newImage = image.getScaledInstance(x, y, Image.SCALE_SMOOTH); 
       return new ImageIcon(newImage); 
      } catch (IOException ioe){ 
       ioe.printStackTrace(); 
       return null; 
      } 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     ScrollImageExampleFrame frame = new ScrollImageExampleFrame(); 
     frame.setVisible(true); 

     frame.siePanel.addImageOuter("foo.png"); 
     Thread.sleep(1000); 
     frame.siePanel.addImageOuter("foo.png"); 
     Thread.sleep(1000); 
     frame.siePanel.addImageOuter("foo.png"); 
     Thread.sleep(1000); 
     frame.siePanel.addImageOuter("foo.png");   
    } 

} 
+2

для лучшей помощи скорее опубликовать SSCCE, как демонстрация вы можете поставить там пустые JPanel – mKorbel

+0

тьфу. МОЙ SSCCE работает так, как я ожидал. Я не хочу вставлять весь проект здесь, но я не могу определить, почему мой проект видит задержку между добавлением изображения и обновлением высоты. Все еще пытается создать SSCCE. –

+1

Почему вы создаете новый JLabel. Просто используйте 'label.setIcon (новый ImageIcon());'. Избегайте добавления/удаления компонентов в видимом графическом интерфейсе, когда существует более простая альтернатива. – camickr

ответ

1

Используйте следующий трюк для этой цели:

private void addImage(final String image){ 
    imagePanel.addImage(image); 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar(); 
      vScroll.setValue(vScroll.getMaximum()); 
     } 
    }); 
} 

В этом случае все компоненты будут добавлены, подтверждены и окрашены, а затем в invokeLater вы можете изменить po раздел JScrollBar. Это помогает вам.

+0

Кажется, что это определенно должно работать, но я все еще вижу неправильную высоту в то время, когда выполняется vScroll.getMaxmimum(). –

+1

Вызов 'revalidate()' метода 'JFrame', в котором вы показываете свой' JScrollPane', он пересчитывает размер панели. – alex2410

+0

Спасибо, alex2410, это оказалось моей проблемой! –

-1

Может попробовать это ...

imagePanel.updateUI(); 
+0

К сожалению, это не работает для меня. –

+0

-1, нет необходимости использовать updateUI(). Этот метод используется Swing при изменении LAF. – camickr

+0

Больше комментария, чем ответа в любом случае. –

0

@ alex2410 определил решение. Вызвав revalidate() на содержащий JFrame, мои размеры компонентов были правильно рассчитаны, когда они мне были нужны. Я был в состоянии исправить мой пример кода, заменив метод addImageOuter следующим образом:

private void addImageOuter(final String image){ 
     final JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(this); 
     SwingUtilities.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       imagePanel.addImageInner(image); 
       frame.revalidate(); 
       final JScrollBar vScroll = imageScrollPane.getVerticalScrollBar(); 
       vScroll.setValue(vScroll.getMaximum()); 
      } 
     }); 
    } 
Смежные вопросы