¿Cuál es la relación entre ContentPane y JPanel?

Encontré un ejemplo en el que los botones se agregan a los paneles (instancias de JPanel ) luego los paneles se agregan a los contenedores (instancias generadas por getContentPane() ) y luego los contenedores son, por la construcción, incluidos en el JFrame (las ventanas).

Intenté dos cosas:

  1. Me deshice de los contenedores. En más detalles, agregué botones a un panel (instancia de JPanel ) y luego agregué el panel a las ventanas (instancia de JFrame ). Funcionó bien

  2. Me deshice de los paneles. En más detalles, agregué botones directamente al contenedor y luego agregué el contenedor a la ventana (instancia de JFrame ).

Entonces, no entiendo dos cosas.

  1. ¿Por qué tenemos dos mecanismos en competencia para hacer las mismas cosas?

  2. ¿Cuál es la razón para usar contenedores en combinación con los paneles ( JPanel )? (Por ejemplo, para qué incluimos botones en JPanels y luego incluimos JPanels en los Contenedores). ¿Podemos incluir JPanel en JPanel ? ¿Podemos incluir un contenedor en un contenedor?

ADICIONAL:

Quizás la esencia de mi pregunta se puede poner en una línea de código:

 frame.getContentPane().add(panel); 

¿Para qué ponemos getContentPane() en medio? Intenté solo frame.add(panel); y funciona bien

AGREGADO 2:

Me gustaría agregar un código para ser más claro sobre lo que quiero decir. En este ejemplo, uso solo JPane:

 import java.awt.*; import javax.swing.*; public class HelloWorldSwing { public static void main(String[] args) { JFrame frame = new JFrame("HelloWorldSwing"); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(new JButton("W"), BorderLayout.NORTH); panel.add(new JButton("E"), BorderLayout.SOUTH); frame.add(panel); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } 

Y en este ejemplo, uso solo el Panel de contenido:

 import java.awt.*; import javax.swing.*; public class HelloWorldSwing { public static void main(String[] args) { JFrame frame = new JFrame("HelloWorldSwing"); Container pane = frame.getContentPane(); pane.setLayout(new BorderLayout()); pane.add(new JButton("W"), BorderLayout.NORTH); pane.add(new JButton("E"), BorderLayout.SOUTH); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.pack(); frame.setVisible(true); } } 

¡Ambos funcionan bien! Solo quiero saber si entre estas dos formas de hacer las cosas, una es mejor (más segura).

No se trata de dos mecanismos que compiten: un JPanel es un Container (basta con mirar la jerarquía de clases en la parte superior de los Javavacs de JPanel ). JFrame.getContentPane() simplemente devuelve un Container para colocar los Component que desea mostrar en el JFrame . Internamente, está usando un JPanel (de manera predeterminada, puede cambiar esto llamando a setContentPane() ) En cuanto a por qué devuelve un Container lugar de un JPanel , es porque debe progtwigr una interfaz, no una implementación , en ese nivel, todo Lo que necesita preocuparse es que puede agregar Component a algo, y aunque Container es una clase en lugar de una interfaz, proporciona la interfaz necesaria para hacer exactamente eso.

En cuanto a por qué tanto JFrame.add() como JFrame.getContentPane().add() hacen lo mismo: JFrame.add() se reemplaza para llamar a JFrame.getContentPane().add() . Este no era siempre el caso: antes de JDK 1.5, siempre tenía que especificar JFrame.getContentPane().add() explícitamente y JFrame.add() lanzaba RuntimeException si lo llamaba, pero debido a muchas quejas, esto se cambió en JDK 1.5 para hacer lo que esperas.

Buena pregunta. Me resultó útil comprender que “Swing ofrece tres clases de contenedores de primer nivel generalmente útiles: JFrame , JDialog y JApplet . Como conveniencia, el método add y sus variantes, remove y setLayout se han reemplazado para reenviar al contentPane según sea necesario. “- Uso de contenedores de nivel superior

Creo que la razón es porque Swing se creó a partir de AWT, y Container es un objeto AWT de nivel superior. En realidad, no es la mejor opción de diseño, ya que generalmente no desea mezclar objetos AWT (peso pesado) con Swing (peso ligero).

Creo que la mejor manera de manejarlo es lanzar siempre el contenidoPanel a un JPanel.

 JPanel contentPanel = (JPanel)aFrame.getContentPane(); 

Todo está escrito en la última versión de API doc que JFrame.add () (nowerdays) es suficiente.

Puede comparar versiones anteriores de Java aquí .

Interesante: jframe.setBackground(color) no funciona para mí, pero funciona jframe.getContentPane().setBackground(color) .

La historia y la mecánica de esto también se discuten con cierto detalle en este artículo de punto de vista . Nota en particular:

getContentPane() devuelve un objeto Container . ¡Este no es realmente un objeto Container simple, pero en realidad es un JPanel ! Este es un Container como consecuencia de la jerarquía. Entonces, si obtenemos el panel de contenido predefinido, resulta que en realidad es un JPanel , pero realmente no podemos aprovechar la funcionalidad que fue agregada por JComponent .

y

Definieron métodos add() en JFrame que simplemente llaman a los métodos add() correspondientes para el panel de contenido. Parece extraño agregar esta característica ahora, especialmente debido a que muchos diseños usan múltiples paneles nesteds, por lo que aún debe sentirse cómodo al agregarlos directamente a un JPanel . Y no todo lo que desea hacer con el panel de contenido se puede hacer a través de llamadas a JFrame .