2012-02-09 11 views
6

Ho un pannello in cui posiziono diversi mini-pannelli, affiancati, con dimensioni e colori diversi, e dovrebbero occupare l'intero pannello genitore (orizzontalmente).JPanel non si allungano completamente per occupare lo spazio disponibile

Per questo utilizzo BorderLayout (per il pannello padre) e BoxLayout per un pannello secondario in cui posiziono tutti i mini-pannelli (vedere il codice seguente). Funziona e si comporta correttamente con il ridimensionamento e tutto. Tuttavia, poiché il numero di mini-pannelli diventa più grande, si verifica uno strano comportamento: lo spazio vuoto appare alla fine del pannello genitore.

enter image description here

penso che ho trovato che questo è un bug streching nei gestori di layout, perché per strech i pannelli, il gestore di layout tenta di aggiungere un singolo pixel per ogni mini-pannello. Tuttavia, quando il numero di mini-pannelli è ampio, l'aggiunta di un singolo pixel a ognuno comporterà l'aggiunta di molti pixel e oltre la dimensione del genitore. Pertanto, il gestore di layout non aggiunge alcun pixel a nessun mini-pannello, risultando nello spazio vuoto.

Ecco il mio SSCCE: (provare a eseguire, e streching la finestra, per capire il problema)

package com.myPackage; 

import java.awt.*; 
import java.util.Vector; 
import javax.swing.BorderFactory; 
import javax.swing.BoxLayout; 
import javax.swing.JFrame; 
import javax.swing.JPanel; 

public class ColoredPanels extends JPanel 
{ 
    /* Content information. */ 
    private Vector<Integer> partitions; 
    private Vector<Color> colors; 

    /* Panel where the content panels will go. */ 
    private JPanel contentHolder; 

    private final int defaultHeight = 20; 

    public ColoredPanels(Vector<Integer> partitions, Vector<Color> colors) 
    { 
     assert partitions != null; 
     assert !partitions.isEmpty(); 
     assert colors != null; 
     assert !colors.isEmpty(); 
     assert colors.size() == partitions.size(); 

     this.partitions = partitions; 
     this.colors = colors; 

     /* Set layout manager. */ 
     setLayout(new BorderLayout()); 

     /* Create the content holder. */ 
     contentHolder = new JPanel(); 
     contentHolder.setLayout(new BoxLayout(contentHolder, BoxLayout.X_AXIS)); 
     this.add(contentHolder, BorderLayout.NORTH); 

     /* Fill content holder with colored panels. */ 
     createPanels(); 
    } 

    private void createPanels() 
    { 
     assert partitions != null; 
     assert !partitions.isEmpty(); 
     assert colors != null; 
     assert !colors.isEmpty(); 
     assert colors.size() == partitions.size(); 

     for (int i = 0; i < partitions.size(); i++) 
     { 
      JPanel newPanel = new JPanel(); 
      newPanel.setBackground(colors.get(i)); 
      newPanel.setPreferredSize(new Dimension(partitions.get(i), defaultHeight)); 
      newPanel.setMinimumSize(new Dimension(1, defaultHeight)); 
      contentHolder.add(newPanel); 
     } 
    } 

    public static void main(String[] in) 
    { 
     Vector<Integer> sizes = new Vector<Integer>(); 
     Vector<Color> cols = new Vector<Color>(); 

     /* Make 100 random sizes, and use two colors. */ 
     for (int i = 0; i < 100; i++) 
     { 
      int size = (int)Math.round(1 + Math.random() * 10); 
      sizes.add(size); 
      cols.add((i%2 == 0)? Color.red : Color.green); 
     } 

     ColoredPanels panels = new ColoredPanels(sizes, cols); 
     panels.setBorder(BorderFactory.createLineBorder(Color.yellow, 1)); 

     JFrame newFrame = new JFrame(); 
     newFrame.getContentPane().add(panels); 
     newFrame.pack(); 
     newFrame.setVisible(true); 
    } 
} 

Come posso evitare questo comportamento? Voglio che i miei pannelli occupino l'intero contenitore.

EDIT: I mini-pannelli sono destinati ad avere (una volta che questo è stato risolto) ascoltatori del mouse. Pertanto, le soluzioni di verniciatura sono sfortunatamente evitabili.

+3

molto bello SSCCE +1 – mKorbel

+0

interessante ... cosa vuoi succedere? La soluzione più semplice (probabilmente non la migliore :-) sarebbe quella di dare tutto lo spazio di arrotondamento all'ultimo ... – kleopatra

+0

Voglio che i mini pannelli riempiano completamente lo spazio disponibile. Dare il rimanente all'ultimo non è un'opzione, dal momento che i rapporti delle dimensioni delle partizioni devono essere rispettati. – Anoyz

risposta

3

I problemi di layout vengono risolti da ... LayoutManagers :-) Se non si desidera ciò che si desidera, implementare il comportamento desiderato.

Quindi, se il core BoxLayout ignora semplicemente i pixel a causa di errori di arrotondamento, crea sottoclasse e falla distribuire quei pixel secondo necessità. Un esempio rapido molto grezzo:

public static class XBoxLayout extends BoxLayout { 

    enum Strategy { 
     NONE, 
     STRETCH_LAST, 
     DISTRUBUTE 
    } 

    private Strategy strategy; 

    public XBoxLayout(Container target, int axis, Strategy strategy) { 
     super(target, axis); 
     this.strategy = strategy; 
    } 


    @Override 
    public void layoutContainer(Container target) { 
     super.layoutContainer(target); 
     if (Strategy.NONE == strategy) return; 
     Insets targetInsets = target.getInsets(); 
     int targetSize = target.getWidth() - targetInsets.left - targetInsets.right; 
     int childSum = 0; 
     for (Component child : target.getComponents()) { 
      childSum += child.getWidth(); 
     } 
     if (targetSize > childSum) { 
      int excess = targetSize - childSum; 
      distribute(target, excess); 
     } 
    } 


    private void distribute(Container target, int excess) { 
     System.out.println("childCount/rounding excess " + target.getComponentCount() + "/" + excess); 
     if (Strategy.STRETCH_LAST == strategy) { 
      Component lastChild = target.getComponent(target 
        .getComponentCount() - 1); 
      lastChild.setSize(lastChild.getWidth() + excess, 
        lastChild.getHeight()); 
     } else { 
      int firstToDistribute = target.getComponentCount() - excess; 
      int summedOffset = 0; 
      for(int index = firstToDistribute; index < target.getComponentCount(); index++) { 
       Component child = target.getComponent(index); 
       Rectangle bounds = child.getBounds(); 
       bounds.x += summedOffset++; 
       bounds.width += 1; 
       child.setBounds(bounds); 
      } 
     } 
    } 

} 
+0

Grazie per il vostro aiuto. Sembra una soluzione valida e stavo cercando qualcosa che coinvolgesse i gestori di layout. Il codice non sembra rispetta le proporzioni delle dimensioni dei componenti (in relazione tra loro), anche con la strategia DISTRIBUTION, ma ho capito il punto e cercherò di implementare qualcosa in quelle linee. – Anoyz

+0

In secondo luogo, quando si ha bisogno di un comportamento personalizzato, fare il proprio gestore di layout non è così doloroso e si può ottimizzare per il proprio caso d'uso specifico. –

+0

@Anoyz che distribuisce i singoli pixel è .. una questione di strategia :-) Implementa tutto ciò che ti serve, alla fine della giornata non sarà esattamente proporzionale alla reale/pref/qualsiasi dimensione – kleopatra

2

due opzioni mi viene in mente:

  1. avere una componente personalizzato che ha un metodo paintComponent(...) che dipinge tutte le regioni, e quando la finestra viene ridimensionata sarebbe ridipingere le regioni, mentre il ridimensionamento di loro in base alla nuove informazioni sulla larghezza.

  2. Dipingi le regioni colorate una volta su un'immagine, quindi dipingila e quando ridimensiona la finestra ridimensiona l'immagine per adattarla.

+0

Grazie per l'aiuto. Intendo aggiungere gli ascoltatori del mouse ai mini-pannelli. Se uso la strategia di pittura, come potrei farlo? – Anoyz

+0

Sarebbe necessario aggiungere il listener al componente sottostante (ad es.un pannello), quindi ridimensionare le coordinate indietro rispetto alle dimensioni ridimensionate del pannello. –

+0

Non c'è una soluzione più semplice con i layout? – Anoyz

1

Una volta mi sono imbattuto in questo problema quando si utilizza GridLayout per una scacchiera. La mia soluzione era estendere GridLayout e calcolare il layout con precisione in virgola mobile.

+0

Potrebbe fornire ulteriori informazioni per quella soluzione? – Anoyz

Problemi correlati