2012-04-26 13 views
8

Mam mały problem podczas korzystania z JScrollPane w mojej aplikacji Java.Zmiana rozmiaru JScrollPane zawierającego JPanel przy wyświetlaniu pasków przewijania

Mam JScrollPane zawierający JPanel. Ta JPanel jest dynamicznie aktualizowana za pomocą przycisków (uporządkowanych pionowo), które mogą mieć dowolną szerokość. Model JPanel automatycznie dostosowuje swoją szerokość do największego komponentu JButton w środku.

Teraz, gdy pojawi się pionowy pasek przewijania, zabiera trochę miejsca po prawej stronie mojego JPanel, co powoduje, że największe przyciski nie są wyświetlane w całości. Nie chcę używać poziomego paska przewijania, oprócz wyświetlania całego przycisku.

Czy istnieje sposób zmiany rozmiaru mojej JPanel po wyświetleniu paska przewijania, więc ładnie się wyświetla obok przycisków? Czy istnieje inny sposób wyświetlania paska przewijania obok mojego JPanel?

Z góry dziękuję!

EDYTOWANIE: Oto demonstracja mojego problemu. Kiedy zmieniasz rozmiar okna na mniejszą wysokość, mała część przycisków po prawej stronie zostaje zasłonięta.

import java.awt.BorderLayout; 
import java.awt.EventQueue; 

import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import java.awt.GridLayout; 

/** 
* @author Dylan Kiss 
*/ 

public class Demo { 
    public static void main(String[] args) { 
     EventQueue.invokeLater(new Runnable() { 
      @Override 
      public void run() { 
       try { 
        JFrame myFrame = new JFrame("Demo"); 
        JPanel sideBar = new JPanel(); 
        JPanel centerPanel = new JPanel(); 
        centerPanel.add(new JLabel("This is the center panel.")); 

        JPanel buttonContainer = new JPanel(); 
        JButton myButton = null; 

        for (int i = 0; i < 20; i++) { 
         buttonContainer.setLayout(new GridLayout(20, 1, 0, 0)); 
         myButton = new JButton("This is my button nr. " + i); 
         buttonContainer.add(myButton); 
        } 

        sideBar.setLayout(new BorderLayout(0, 0)); 

        JScrollPane scrollPane = new JScrollPane(buttonContainer); 

        sideBar.add(scrollPane); 

        myFrame.getContentPane().add(sideBar, BorderLayout.WEST); 
        myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER); 

        myFrame.setLocationByPlatform(true); 
        myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        myFrame.pack(); 
        myFrame.setVisible(true); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 
} 
+1

można udostępnić SSCCE? – StanislavL

+0

Jest to część dużego projektu. Postaram się przedstawić krótką wersję demonstracyjną problemu. – dylan202

+1

@StanislavL dla nowych użytkowników, przypuszczam, że lepiej jest powiązać to, co [SSCCE] (http://sscce.org/) jest! :) – COD3BOY

Odpowiedz

6

Oto prosty, nie dość, rozwiązanie:

scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 

EDIT: myślałem, że nie może wykonać pracę w twoim przypadku. Oto lepsze rozwiązanie choć ma sporo boilerplate:

private class ButtonContainerHost extends JPanel implements Scrollable { 
    private static final long serialVersionUID = 1L; 

    private final JPanel buttonContainer; 

    public ButtonContainerHost(JPanel buttonContainer) { 
     super(new BorderLayout()); 
     this.buttonContainer = buttonContainer; 
     add(buttonContainer); 
    } 

    @Override 
    public Dimension getPreferredScrollableViewportSize() { 
     Dimension preferredSize = buttonContainer.getPreferredSize(); 
     if (getParent() instanceof JViewport) { 
      preferredSize.width += ((JScrollPane) getParent().getParent()).getVerticalScrollBar() 
        .getPreferredSize().width; 
     } 
     return preferredSize; 
    } 

    @Override 
    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { 
     return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width * 9/10, 1) 
       : Math.max(visibleRect.height * 9/10, 1); 
    } 

    @Override 
    public boolean getScrollableTracksViewportHeight() { 
     if (getParent() instanceof JViewport) { 
      JViewport viewport = (JViewport) getParent(); 
      return getPreferredSize().height < viewport.getHeight(); 
     } 
     return false; 
    } 

    @Override 
    public boolean getScrollableTracksViewportWidth() { 
     return true; 
    } 

    @Override 
    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { 
     return orientation == SwingConstants.HORIZONTAL ? Math.max(visibleRect.width/10, 1) 
       : Math.max(visibleRect.height/10, 1); 
    } 
} 

implementuje Przewijany aby uzyskać pełną kontrolę nad przewijaniem, nie fantazyjny sztuczkę ze śledzenia wysokości rzutni w celu zapewnienia przyciski rozwijać, gdy przestrzeń jest dostępna i dodaje szerokość pionowego paska przewijania do preferowanej szerokości przez cały czas. Może się rozwinąć, gdy pionowy pasek przewijania jest widoczny, ale i tak wygląda źle. Używaj go tak:

scrollPane = new JScrollPane(new ButtonContainerHost(buttonContainer)); 

Wydaje mi się to rozwiązanie jest konieczne z powodu ewentualnego błędu w javax.swing.ScrollPaneLayout:

if (canScroll && (viewSize.height > extentSize.height)) { 
    prefWidth += vsb.getPreferredSize().width; 
} 

Tutaj extentSize nadawana jest preferowany rozmiar viewport i viewSize są ustawione na viewport.getViewSize(). Nie wydaje się to prawidłowe, AFAIK, rozmiar widoku wewnątrz rzutni powinien zawsze być równy preferowanemu rozmiarowi. Wydaje mi się, że rozmiar widoku powinien być porównywany z rzeczywistym rozmiarem rzutni, a nie jej preferowanym rozmiarem.

+0

Wolę pokazywać pasek przewijania tylko wtedy, gdy jest to konieczne:/ – dylan202

+0

Dziękuję za to piękne rozwiązanie. Działa idealnie, jak chcę. – dylan202

0

Można zmienić rozmiar JPanel wywołując setPreferredSize, gdy JPanel wymaga zmiany rozmiaru.

buttonContainer.setPreferredSize(Dimension d); 
2

proste obejście, aby spełniać oczekiwania dotyczące

Is there a way to resize my JPanel when a scrollbar appears, so it 
appears nicely next to my buttons? 

jest wykorzystanie EmptyBorder, to pozwala osiągnąć to, co czujesz, jak powinno się zdarzyć, tak jak pokazano na rysunku poniżej:

SCROLLBAR SETTING

po prostu dodaje ten wiersz napisany poniżej po tej linii JPanel buttonContainer = new JPanel();

DODATEK LINE

buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 

Oto kod z tej linii dodanej:

import java.awt.BorderLayout; 
import java.awt.EventQueue; 

import javax.swing.BorderFactory; 
import javax.swing.JButton; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.JScrollPane; 
import java.awt.GridLayout; 

/** 
* @author Dylan Kiss 
*/ 

public class Demo 
{ 
    public static void main(String[] args) 
    { 
     EventQueue.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       try 
       { 
        JFrame myFrame = new JFrame("Demo"); 
        JPanel sideBar = new JPanel(); 
        JPanel centerPanel = new JPanel(); 
        centerPanel.add(new JLabel("This is the center panel.")); 

        JPanel buttonContainer = new JPanel(); 
        buttonContainer.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20)); 
        JButton myButton = null; 

        for (int i = 0; i < 20; i++) 
        { 
         buttonContainer.setLayout(new GridLayout(20, 1, 0, 0)); 
         myButton = new JButton("This is my button nr. " + i); 
         buttonContainer.add(myButton); 
        } 

        sideBar.setLayout(new BorderLayout(0, 0)); 

        JScrollPane scrollPane = new JScrollPane(buttonContainer);  

        sideBar.add(scrollPane); 

        myFrame.getContentPane().add(sideBar, BorderLayout.WEST); 
        myFrame.getContentPane().add(centerPanel, BorderLayout.CENTER); 

        myFrame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
        myFrame.pack(); 
        myFrame.setLocationByPlatform(true); 
        myFrame.setVisible(true); 
       } 
       catch (Exception e) 
       { 
        e.printStackTrace(); 
       } 
      } 
     }); 
    } 
} 
Powiązane problemy