2012-02-09 9 views
6

Tengo un panel donde coloco varios mini paneles, uno al lado del otro, con diferentes tamaños y colores, y deben ocupar todo el panel principal (horizontalmente).JPanels no se extienden por completo para ocupar el espacio disponible

Para esto utilizo BorderLayout (para el panel padre), y BoxLayout para un subpanel donde coloco todos los mini paneles (vea el código a continuación). Funciona y se comporta correctamente al cambiar el tamaño y todo. Sin embargo, a medida que aumenta el número de mini paneles, se produce un comportamiento extraño: aparece espacio vacío al final del panel principal.

enter image description here

Creo que he encontrado que esto es un error estiramiento en los controladores de distribución, porque a fin de estirar los paneles, el controlador de distribución intenta añadir un solo píxel de cada mini-panel. Sin embargo, cuando el número de mini paneles es grande, agregar un solo píxel a cada uno dará como resultado la adición de muchos píxeles y más allá del tamaño del elemento principal. Por lo tanto, el administrador de diseño termina sin agregar píxeles a ningún mini panel, lo que resulta en el espacio vacío.

Aquí es mi SSCCE: (intente ejecutar, y streching la ventana, para entender el 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); 
    } 
} 

¿Cómo puedo evitar este comportamiento? Quiero que mis paneles ocupen todo el contenedor.

EDIT: Los mini paneles tienen la intención de tener (una vez que esto se resuelva) los oyentes del mouse. Por lo tanto, las soluciones de pintura son desafortunadamente evitables.

+3

muy bonito SSCCE +1 – mKorbel

+0

interesante ... ¿qué quieres que suceda? El camino más fácil (probablemente no el mejor :-) sería dar todo el espacio de redondeo al último ... – kleopatra

+0

Quiero que los mini paneles llenen por completo el espacio disponible. Dar lo último a lo último no es una opción, ya que se deben respetar las proporciones de tamaño de la partición. – Anoyz

Respuesta

3

Los problemas de diseño se resuelven mediante ... Administradores de diseño :-) Si uno no hace lo que quiere que haga, implemente el comportamiento que desee.

Por lo tanto, si el BoxLayout central simplemente ignora los píxeles debido a errores de redondeo, subclase y haga que distribuya esos píxeles según sea necesario. Un ejemplo rápido sin formato:

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

Gracias por su ayuda. Parece una solución viable, y estaba buscando algo que involucre a los administradores de diseño. El código no parece respetar las proporciones del tamaño del componente (en relación entre sí), incluso con la estrategia de DISTRIBUCIÓN, pero entiendo el punto, e intentaré implementar algo en esas líneas. – Anoyz

+0

Voy a mencionar esto: cuando necesita un comportamiento personalizado, hacer su propio administrador de disposición no es tan doloroso y puede optimizarlo para su caso de uso específico. –

+0

@Anoyz distribuir píxeles individuales es ... una cuestión de estrategia :-) Implemente lo que necesite, al final del día no será exactamente proporcional al tamaño real/pref/lo que sea – kleopatra

2

Dos opciones que se me ocurren:

  1. tienen un componente personalizado que tiene un método paintComponent(...) que pinta todas las regiones, y cuando se cambia el tamaño de la ventana que se vuelva a pintar las regiones, mientras que la escala de acuerdo a la nueva información de ancho

  2. Pintar las regiones de color una vez en una imagen, y luego pintar eso, y cuando se cambia el tamaño de la ventana redimensionar la imagen para que quepa.

+0

Gracias por la ayuda. Tengo la intención de agregar oyentes de ratón a los mini paneles. Si utilizo la estrategia de pintura, ¿cómo podría hacer eso? – Anoyz

+0

Sería necesario agregar el oyente al componente subyacente (p.un panel), luego escale las coordenadas desde el tamaño de escala del panel. –

+0

¿No hay una solución más fácil con los diseños? – Anoyz

1

Una vez me encontré con este problema al usar GridLayout para un tablero de ajedrez. Mi solución fue extender GridLayout y calcular el diseño con precisión de coma flotante.

+0

¿Podría proporcionar información adicional para esa solución? – Anoyz

Cuestiones relacionadas