我正在尝试编写一个JPane,它有一个JScrollPane,我想实时添加Panels.我的意思是,我有一些组件,一个是一个Button,它将另一个JPane添加到JScrollPane中的主要组件(secundario).问题是当它添加一些覆盖超过窗口大小的面板时,我放置的滚动条不会移动,所以我不能下去看下面的面板(它应该是一个垂直滚动条).我会在这里留下代码:
旧代码:
private JPanel panelModulos() { JPanel raiz = new JPanel(); raiz.setLayout(new GridLayout(1, 1)); raiz.setPreferredSize(new Dimension(490, 790)); // Lo que guarda el contenedor secundario = new JPanel(); secundario.setPreferredSize(maximumSize); // Deficicion del formato JLabel etModuloDir = etiqueta("Directorio"); JLabel etModuloNombre = etiqueta("Nombre"); JButton botAnModulo = boton("Añadir", "addModulo", 'A', icon("Iconos/addMod_opt.png")); JButton botElModulo = boton("Eliminar", "delModulo", 'E', icon("Iconos/delMod_opt.png")); JTextField txtNomMod = texto("", "Nombre", true, 25); JTextField txtDirMod = texto("", "Directorio", true, 25); txtDirMod.setMaximumSize(maximumSize); txtNomMod.setMaximumSize(maximumSize); // Contenedor de botones y etiquetas JPanel tercero = new JPanel(); tercero.add(etModuloDir); tercero.add(txtDirMod); tercero.add(etModuloNombre); tercero.add(txtNomMod); tercero.add(botAnModulo); tercero.add(botElModulo); secundario.add(tercero); principal = new JScrollPane(secundario); principal.setPreferredSize(new Dimension(490, 790)); principal.setVerticalScrollBar(principal.createVerticalScrollBar()); principal.setWheelScrollingEnabled(true); principal.getVerticalScrollBar().setEnabled(true); principal.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); raiz.add(principal); return raiz; }
更新:好的,所以我减少了我的代码,以便你可以在任何地方运行程序.它在问题所在的窗口中显示问题,其余窗口正常工作.控制该窗口的函数名为panelModulos(); 我想要做的是将这些组件添加到Panel中,当我按下"Añadir"按钮时,它会创建另一个具有相同组件的JPanel,并在下面(在colums中)创建旧面板; 问题是现在的大小和VERTICAL滚动条,当我添加一些占用超过窗口大小的面板时,它不起作用.
码:
package problem; import java.awt.BorderLayout; import java.awt.Checkbox; import java.awt.CheckboxGroup; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.TextArea; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.sql.Connection; import java.util.ArrayList; import java.util.EventObject; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.filechooser.FileNameExtensionFilter; /** * Clase mainApp ** Interfaz gráfica. * * @author Berta García Sanz * @author Héctor Mamolar Albitre * * @version 1.0 * */ public class probMain implements ActionListener, ItemListener { /* * Botones de la aplicación */ private JButton btnBBDDRep, btnEliminarAntena, btnEliminarPA, btnAnnadirPAf, btnAnnadirPA, btnAddRed, btnEliminarRed, btnEliminarPlanta, btnAnnadirEdif, btnEliminarEdif, rutaPA1, rutaPA2, cargarDatos, iniciarLocalizacion, terminarLocalizacion, conectar, iniciar, obtenerDatos, logs; /* * Campos de la aplicación */ private JTextField txtUrlBBDDRep, txtDriverBBDDRep, txtUsuarioBBDDRep, finicial, ffinal, tuser, tclase, turl; private JTextField txtDireccionI = texto("", " Dirección del edificio ", true, 20); private JTextField txtTelefonoI = texto("", " Teléfono del edificio ", true, 20); private JTextField txtX = texto("", " Posicion X de la antena ", true, 3); private JTextField txtY = texto("", " Posicion Y de la antena ", true, 3); private JTextField txtZ = texto("", " Posicion Z de la antena ", true, 3); private JTextField txtDCC = texto("\\\\192.168.0.0\\publico", " Direccion de la carpeta compartida ", true, 17); private JTextField txtAlcance = texto("", " AlcanceAntena ", true, 5); private JTextField txtModelo = texto("", " ModeloAntena ", true, 5); private JTextField txtAncho = texto("", " Ancho de planta del edificio ", true, 3); private JTextField txtAlto = texto("", " Alto de planta del edificio ", true, 3); private JTextField txtLargo = texto("", " Largo de planta del edificio ", true, 3); private JTextField txtEPA2 = texto("", " Edificio del PA ", true, 10); private JTextField txtPPA2 = texto("", " PPlanta del PA ", true, 4); private JTextField txtEPA = texto("", " Edificio del PA ", true, 10); private JTextField txtPPA = texto("", " Planta del PA ", true, 4); private JTextField txtRed = texto("", " Red del PA ", true, 6); /* * Áreas de texto de la aplicación */ private TextArea localizaciontexto = new TextArea(), areatexto = new TextArea(); private JCheckBox jchx; private JComboBox
eList, pList, antList, antList2, antList3, edificiosList, edificiosList1, edificiosList2, edificiosList3, plantasList, plantasList2, plantasList3, PAList, PAList2, PAList3, PA1List; private CheckboxGroup cbg1, cbg2, cbg3; private Checkbox modEd, modPl, modPA, modAnt, jchxFiltro1, jchxFiltro3, jchxFiltro4; private JPasswordField tpass, tpassClaveBBDDRep; private JMenu men; private JFrame cR, cE, cPA, cD; private String edif = null; private String planta = null; private String fIni = null; private String fFin = null; static String password = ""; private Connection conexion, conexionRep, conexionMejora; private String[] Edificios = { "" }; private String[] Plantas = { "" }; private String[] PuntosAcceso1 = { "" }; private String[] Antenas = { "" }; private File fichero1, fichero2; private FileNameExtensionFilter filtroFicherotxt = new FileNameExtensionFilter("Archivos .txt", "txt"); private String rutPA1, rutPA2; // TODO añadido yo private JTextField txtFuera = texto("", "Fuera (0,1)", true, 2); private JTextField txtRssiMin = texto("", "RSSI mínimo", true, 3); private JTextField txtRssiMax = texto("", "RSSI máximo", true, 3); private JTextField mejorabbddText; private ArrayList listFichDatos; private ArrayList listFichDisp; private JTextField macDispConcreto; private ArrayList listDispConcretos; private Thread capturas; private Thread localizaciones; private JComboBox boxPuntosAcceso; private ArrayList listPAActivar; private ArrayList rutasModulos; private ArrayList parametros; // Permite ir añadiendo elementos a la interfaz tras inicializarlo private JPanel secundario; private Dimension maximumSize; private ArrayList panelesModulos; private JScrollPane principal; public static void main(String args[]) { probMain apli = new probMain(508, 808); } // ------------- // CONSTRUCTOR // ------------- /** * Constructor de la clase * * @param alt * - Altura de la ventana del interfaz de la aplicación * @param anch * - Anchura de la ventana del interfaz de la aplicación */ public probMain(int alt, int anch) { listFichDatos = new ArrayList (); listFichDisp = new ArrayList (); listDispConcretos = new ArrayList (); listPAActivar = new ArrayList (); panelesModulos = new ArrayList (); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); JFrame f = new JFrame("Universidad de Burgos - Sistema de Localización GUI 2.0"); f.setIconImage(new ImageIcon("Iconos/ubu.png").getImage()); f.setSize(anch, alt); f.setJMenuBar(menu()); f.setContentPane(tabpanel()); f.setVisible(true); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } catch (ClassNotFoundException e) { JOptionPane.showMessageDialog(new JFrame(), e + "\n Clase No encontrada desempaquetando objeto", "Excepción java.lang", JOptionPane.WARNING_MESSAGE, null); } catch (InstantiationException e) { JOptionPane.showMessageDialog(new JFrame(), e + "\n Excepcion instanciando clase de Aspecto", "Excepción java.lang", JOptionPane.WARNING_MESSAGE, null); } catch (IllegalAccessException e) { JOptionPane.showMessageDialog(new JFrame(), e + "\n Acceso ilegal a clase de Aspecto", "Excepción java.lang", JOptionPane.WARNING_MESSAGE, null); } catch (UnsupportedLookAndFeelException e) { JOptionPane.showMessageDialog(new JFrame(), e + "\n Clase de Aspecto no soportada", "Excepción javax.swing", JOptionPane.WARNING_MESSAGE, null); } } // --------------------- // UTILERIA de INTERFAZ // --------------------- /** * Creación de los componentes principales del menú. * * @return JMenuBar Barra de menú de la aplicación */ private JMenuBar menu() { JMenuBar mBar = new JMenuBar(); mBar.add(menuConf('Z', "Configuración")); mBar.add(menuTitulo('A', "Ayuda")); return (mBar); } /** * Metodo que crea un submenú del menú principal. * * @param mnemonic * Nemotécnico del submenú * @param nombre * Nombre del submenú * @return JMenu Menú de ayuda */ private JMenu menuTitulo(char mnemonic, String nombre) { JMenu men = new JMenu(nombre); men.setMnemonic(mnemonic); men.add(objetoMenu("Acerca de...", 'c', "acerca")); return (men); } /** * Metodo que crea un submenú con tres componentes de configuración del menú * principal. * * @param mnemonic * Nemotécnico del submenú * @param nombre * Nombre del submenú * @return JMenu Menú de configuración */ private JMenu menuConf(char mnemonic, String nombre) { men = new JMenu(nombre); men.setMnemonic(mnemonic); men.setEnabled(false); men.add(objetoMenu("Configuración Edificios", 'e', "confEdif")); men.add(objetoMenu("Configuración Puntos de Acceso y Red", 't', "confPA")); men.add(objetoMenu("Configuración Representación", 'r', "confRep")); return (men); } /** * Metodo que crea un objeto del menú. * * @param nombre * nombre del objeto a crear * @param mnemonic * mnemonic * @param accion * accion que realiza * @return JMenuItem objeto con el objeto implententado * */ private JMenuItem objetoMenu(String nombre, char mnemonic, String accion) { JMenuItem mT = new JMenuItem(nombre, mnemonic); mT.setActionCommand(accion); mT.addActionListener(this); return (mT); } /** * Metodo que las pestañas de la ventana principal. * * @return JTabbedPane Contenedor de pestañas de la interfaz */ private JTabbedPane tabpanel() { JTabbedPane jtp = new JTabbedPane(); jtp.addTab("correct", panelConexion()); jtp.addTab("correct", panelEscaneo()); jtp.addTab("correct", panelObtenerDatos()); jtp.addTab("PROBLEM", panelModulos()); jtp.addTab("correct", panelLocalizarDisp()); return (jtp); } // PANEL CONEXION BBDD /** * Método que establece el formato deseado de la pestaña de configuración de * la base de datos. * * @return JPanel Panel de conexión a la BBDD de localización */ private JPanel panelConexion() { JPanel raiz = new JPanel(new GridLayout(1, 1)); return (raiz); } /** * Método que establece el formato deseado de la pestaña de escaneo de * redes. * * @return JPanel Panel de escaneo de redes */ private JPanel panelEscaneo() { JPanel raiz = new JPanel(new GridLayout(2, 1)); return (raiz); } /** * Método que establece el formato deseado de la pestaña de filtrado de * datos. * * @return JPanel Panel de filtrado de datos */ private JPanel panelObtenerDatos() { JPanel raiz = new JPanel(); return (raiz); } /** * Método que establece el formato deseado de la pestaña que añade módulos a * la aplicación. * */ private JPanel panelModulos() { JPanel raiz = new JPanel(); raiz.setLayout(new GridLayout(1, 1)); // Deficicion del formato JLabel etModuloDir = etiqueta("Directorio"); JLabel etModuloNombre = etiqueta("Nombre"); JButton botAnModulo = boton("Añadir", "addModulo", 'A', icon("Iconos/addMod_opt.png")); JButton botElModulo = boton("Eliminar", "delModulo", 'E', icon("Iconos/delMod_opt.png")); JTextField txtNomMod = texto("", "Nombre", true, 25); JTextField txtDirMod = texto("", "Directorio", true, 25); // Lo que guarda el contenedor secundario = new JPanel(); maximumSize = new Dimension(12, 12); etModuloDir.setMaximumSize(maximumSize); etModuloNombre.setMaximumSize(maximumSize); botAnModulo.setMaximumSize(maximumSize); botElModulo.setMaximumSize(maximumSize); txtNomMod.setMaximumSize(maximumSize); txtDirMod.setMaximumSize(maximumSize); GridLayout gridLay = new GridLayout(0, 6); BorderLayout borLay = new BorderLayout(); // Contenedor de botones y etiquetas JPanel tercero = new JPanel(); tercero.setMaximumSize(maximumSize); tercero.setLayout(gridLay); tercero.add(etModuloDir, BorderLayout.PAGE_START); tercero.add(txtDirMod, BorderLayout.PAGE_START); tercero.add(etModuloNombre, BorderLayout.PAGE_START); tercero.add(txtNomMod, BorderLayout.PAGE_START); tercero.add(botAnModulo, BorderLayout.PAGE_START); tercero.add(botElModulo, BorderLayout.PAGE_START); secundario.setLayout(borLay); secundario.add(tercero); principal = new JScrollPane(secundario, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); principal.setVerticalScrollBar(principal.createVerticalScrollBar()); principal.setWheelScrollingEnabled(true); principal.getVerticalScrollBar().setEnabled(true); // principal.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); raiz.add(principal); return raiz; } /** * Método que establece el formato deseado de la pestaña de localización de * dispositivos. * * @return JPanel Panel de localización */ private JPanel panelLocalizarDisp() { JPanel raiz = new JPanel(new FlowLayout(FlowLayout.LEFT)); return (raiz); } /** * Método que establece las etiquetas de la interfaz. * * @param texto * Texto de la etiqueta * @return JLabel Etiqueta de la interfaz */ private JLabel etiqueta(String texto) { return (new JLabel(texto)); } /** * Método que crea campo de texto. * * @param texto * Texto del campo * @param tooltip * cadena con el tooltip * @param editable * El campo es editable o no * @param columnas * Tamaño del campo * @return JTextField Panel de escaneo de redes */ private JTextField texto(String texto, String tooltip, boolean editable, int columnas) { JTextField tf = new JTextField(texto, columnas); if (editable) tf.setEditable(true); else tf.setEditable(false); tf.setToolTipText(tooltip + texto); return (tf); } /** * Método que crea los botones de la interfaz. * * @param nombre * Nombre del botón * @param accion * Acción del botón * @param memo * Nemotécnico del botón * @param icon * Icono asociado al botón * @return JPanel Panel de escaneo de redes */ private JButton boton(String nombre, String accion, char memo, Icon icon) { JButton b = new JButton(nombre, icon); b.setActionCommand(accion); b.setMnemonic(memo); b.addActionListener(this); return (b); } /** * Método que obtiene el icono. * * @param ruta * Ruta en la que se encuentra ubicado el icono * @return JPanel Panel de escaneo de redes */ private ImageIcon icon(String ruta) { return (new ImageIcon(ruta)); } /** * Método que devuelve el panel de los que hay en la pestaña de módulos al * que se corresponde el objeto botón que se recibe como parámetro. * * @param bot * botón a buscar entre los paneles de los módulos de mejora. * @return panel correspondiente al botón. */ private Component getPanel(JButton bot) { for (JPanel panel : panelesModulos) { for (int i = 0; i < panel.getComponentCount(); ++i) { if (panel.getComponent(i).equals(bot)) { return panel; } } } return null; } /** * Añade un panel con sus correspondientes componentes a la pestaña de * módulos para que se introduzca otro. */ private void añadeFilaModulos() { JLabel etModuloDir = etiqueta("Directorio"); JLabel etModuloNombre = etiqueta("Nombre"); JButton botAnModulo = boton("Añadir", "addModulo", 'A', icon("Iconos/addMod_opt.png")); JButton botElModulo = boton("Eliminar", "delModulo", 'E', icon("Iconos/delMod_opt.png")); JTextField txtNomMod = texto("", "Nombre", true, 25); JTextField txtDirMod = texto("", "Directorio", true, 25); txtDirMod.setMaximumSize(maximumSize); txtNomMod.setMaximumSize(maximumSize); JPanel tercero = new JPanel(); tercero.add(etModuloDir); tercero.add(txtDirMod); tercero.add(etModuloNombre); tercero.add(txtNomMod); tercero.add(botAnModulo); tercero.add(botElModulo); panelesModulos.add(tercero); secundario.add(tercero); secundario.revalidate(); secundario.repaint(); principal.revalidate(); principal.repaint(); } /** * Borra un panel y sus componentes y además los datos del módulo * correspondiente. * * @param e * evento ocurrido al pulsar el botón de eliminar un módulo. */ private void borraFilaModulos(EventObject e) { // Elimina el panel correspondiente secundario.remove(getPanel((JButton) e.getSource())); secundario.revalidate(); secundario.repaint(); principal.revalidate(); principal.repaint(); } // --------- // OYENTES // --------- // Oyente de Botones public void actionPerformed(ActionEvent e) { // Añade los datos del módulo a la base de datos y añade una nueva fila // para poner otro módulo. if (e.getActionCommand().equals("addModulo")) { añadeFilaModulos(); } if (e.getActionCommand().equals("delModulo")) { borraFilaModulos(e); } } /** * Procedimiento para llenar los datos de una planta de un edificio dado * como parametro segun los datos de la base de datos. * * @param idEdificio * edificio donde se encuentran las plantas */ protected void rellenarPlantas(String idEdificio) { } /** * Procedimiento para llenar los datos de una planta de un edificio dado * como parametro segun los datos de la base de datos. * * @param idEdificio * edificio donde se encuentran las plantas */ protected void rellenarPlantas2(String idEdificio) { } /** * Procedimiento para llenar los datos de una planta de un edificio dado * como parametro segun los datos de la base de datos. * * @param idEdificio * edificio donde se encuentran las plantas */ protected void rellenarPlantas3(String idEdificio) { } /** * Procedimiento para llenar los datos de una planta de un edificio dado * como parametro segun los datos de la base de datos. * * @param idEdificio * edificio donde se encuentran las plantas */ protected void rellenarPlantas4(String idEdificio) { } /** * Procedimiento para rellenar los datos de un punto de acceso según la base * de datos. * * @param idEdificio * edificio donde se encuentra el punto de acceso. * @param idPlanta * planta en la que se encuentra el punto de acceso. */ protected void rellenarPAC1(String idEdificio, String idPlanta) { } /** * Procedimiento para rellenar los datos de un punto de acceso según la base * de datos. * * @param idEdificio * edificio donde se encuentra el punto de acceso. * @param idPlanta * planta en la que se encuentra el punto de acceso. */ protected void rellenarPAC2(String idEdificio, String idPlanta) { } /** * Procedimiento para rellenar los datos de un punto de acceso según la base * de datos. * * @param idEdificio * edificio donde se encuentra el punto de acceso. * @param idPlanta * planta en la que se encuentra el punto de acceso. */ protected void rellenarPAC3(String idEdificio, String idPlanta) { } /** * Procedimiento que rellena los datos de una antena conectada a un punto de * acceso pasado como parámetro. * * @param idPA * identificador del punto de acceso. */ protected void rellenarAnt1(String idPA) { } /** * Procedimiento que rellena los datos de una antena conectada a un punto de * acceso pasado como parámetro. * * @param idPA * identificador del punto de acceso. */ protected void rellenarAnt2(String idPA) { } /** * Procedimiento que rellena los datos de una antena conectada a un punto de * acceso pasado como parámetro. * */ protected void rellenarAnt3() { } /** * Procedimiento que rellena los datos de la red según la antena a la que * esté conectada que se pasa como parámetro. * * @param idAntena * identificador de la antena. */ protected void rellenarRed(String idAntena) { } /** * Rellena los datos del punto de acceso 1. * * @param idEdificio * identificador del edificio * @param idPlanta * identificador de la planta */ protected void rellenarPA1(String idEdificio, String idPlanta) { } public void itemStateChanged(ItemEvent i) { if (i.getItemSelectable() == jchx) { if (i.getStateChange() == ItemEvent.SELECTED) jchx.setSelected(true); else jchx.setSelected(false); } } /** * Procedimiento que guarda la información obtenida en un fichero de texto * denominado HISTORIAL.txt. */ public void guardarInfo() { }
}
以下是图片:https://www.dropbox.com/sh/082egfz7zk4l7kq/AABmX-EdDdHdlFbGbjsX6pZia?dl=0
谢谢您的帮助.
这会搞砸你:
secundario.setPreferredSize(maximumSize);
通过设置组件的首选大小,可以防止它根据它所包含的组件实际计算出它的首选大小,这可能会阻止secundario组件扩展.首先摆脱这条线.
如果这不能完全解决您的问题,那么您将要向我们展示您的最小,完整和可验证的示例.
你说:
如果我删除这一行它可以工作但水平,我的意思是,面板连续添加一行,并出现水平滚动条.我想垂直使用垂直滚动条,抱歉我没有提到它.
这是影响您的问题的关键信息,解决方案是使用布局管理器或布局管理器来帮助您解决此问题.如果添加到secudario的所有组件的大小都相同,那么考虑给它一个GridLayout.我所做的就是使用JPanel将GridLayout添加到另一个使用BorderLayout的JPanel并将其添加到BorderLayout.PAGE_START位置,然后将第二个JPanel添加到JScrollPane的视口中.这可以防止使用JPanel的GridLayout中的网格展开.
解决问题的一种方法是创建一个JPanel,它包含一行的数据,并使用一个合理的布局管理器,并且具有允许外部类从中提取任何信息的方法.例如这个类:
// holds a row of data @SuppressWarnings("serial") class RowPanel extends JPanel { private static final int COLS = 20; private JTextField txtDirMod = new JTextField(COLS); private JTextField txtNomMod = new JTextField(COLS); private ProbMain2 probMain2; private int minorStrut = 3; private int majorStrut = 6; // TODO: add tool tips, ... public RowPanel(ProbMain2 probMain2) { this.probMain2 = probMain2; setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); setBorder(BorderFactory.createEmptyBorder(minorStrut, minorStrut, minorStrut, minorStrut)); add(new JLabel("Directorio")); add(Box.createHorizontalStrut(minorStrut)); add(txtDirMod); add(Box.createHorizontalStrut(majorStrut)); add(new JLabel("Nombre")); add(Box.createHorizontalStrut(minorStrut)); add(txtNomMod); add(Box.createHorizontalStrut(majorStrut)); add(new JButton(new AnadirAction())); add(Box.createHorizontalStrut(minorStrut)); add(new JButton(new EliminarAction())); } // to extract data held in the JPanel public String getDir() { return txtDirMod.getText(); } public String getNom() { return txtNomMod.getText(); } private class AnadirAction extends AbstractAction { public AnadirAction() { super("Anadir"); } @Override public void actionPerformed(ActionEvent e) { // add new RowPanel to the main GUI probMain2.addRowPanel(); } } private class EliminarAction extends AbstractAction { public EliminarAction() { super("Eliminar"); } @Override public void actionPerformed(ActionEvent e) { // tell main GUI to remove *this* RowPanel object probMain2.removeRowPanel(RowPanel.this); } } }
有两个JTextFields和两个JButtons.JButtons具有调用主类中的方法的Actions,它们告诉主类probMain2添加另一个RowPanel,或者删除这个相同的RowPanel对象.
在主类ProbMain2中,我使用JPanel将RowPanel添加到GridLayout,这里称为rowHolderPanel,我将JPanel放入另一个使用BorderLayout的JPanel,到BorderLayout.PAGE_START位置,就像我最初建议的那样.然后我重新验证并重新绘制rowHolderPanel.我还将RowPanel添加到ArrayList中,因此我可以更轻松地提取它所拥有的数据.
这是我完整的Minimal,Complete和Verifiable示例程序.请注意,它比你的小得多,因为它试图消除与问题无关的所有代码(除了一些额外的代码来美化结果):
import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.List; import javax.swing.*; @SuppressWarnings("serial") public class ProbMain2 extends JPanel { private static final int PREF_W = 800; private static final int PREF_H = 500; // holds the rows, has grid layout with variable number of rows and one column: private JPanel rowHolderPanel = new JPanel(new GridLayout(0, 1)); private ListrowPanelList = new ArrayList<>(); public ProbMain2() { // panel below holds the rowHOlderPanel at the PAGE_START position JPanel borderLayoutPanel = new JPanel(new BorderLayout()); borderLayoutPanel.add(rowHolderPanel, BorderLayout.PAGE_START); JScrollPane scrollPane = new JScrollPane(borderLayoutPanel); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); addRowPanel(); setLayout(new BorderLayout()); add(scrollPane); } public void addRowPanel() { RowPanel rowPanel = new RowPanel(this); rowPanelList.add(rowPanel); rowHolderPanel.add(rowPanel); rowHolderPanel.revalidate(); rowHolderPanel.repaint(); } public void removeRowPanel(RowPanel row) { rowPanelList.remove(row); rowHolderPanel.remove(row); rowHolderPanel.revalidate(); rowHolderPanel.repaint(); } @Override public Dimension getPreferredSize() { if (isPreferredSizeSet()) { return super.getPreferredSize(); } return new Dimension(PREF_W, PREF_H); } private static void createAndShowGui() { ProbMain2 mainPanel = new ProbMain2(); JFrame frame = new JFrame("ProbMain2"); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } // holds a row of data @SuppressWarnings("serial") class RowPanel extends JPanel { private static final int COLS = 20; private JTextField txtDirMod = new JTextField(COLS); private JTextField txtNomMod = new JTextField(COLS); private ProbMain2 probMain2; private int minorStrut = 3; private int majorStrut = 6; // TODO: add tool tips, ... public RowPanel(ProbMain2 probMain2) { this.probMain2 = probMain2; setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); setBorder(BorderFactory.createEmptyBorder(minorStrut, minorStrut, minorStrut, minorStrut)); add(new JLabel("Directorio")); add(Box.createHorizontalStrut(minorStrut)); add(txtDirMod); add(Box.createHorizontalStrut(majorStrut)); add(new JLabel("Nombre")); add(Box.createHorizontalStrut(minorStrut)); add(txtNomMod); add(Box.createHorizontalStrut(majorStrut)); add(new JButton(new AnadirAction())); add(Box.createHorizontalStrut(minorStrut)); add(new JButton(new EliminarAction())); } // to extract data held in the JPanel public String getDir() { return txtDirMod.getText(); } public String getNom() { return txtNomMod.getText(); } private class AnadirAction extends AbstractAction { public AnadirAction() { super("Anadir"); } @Override public void actionPerformed(ActionEvent e) { // add new RowPanel to the main GUI probMain2.addRowPanel(); } } private class EliminarAction extends AbstractAction { public EliminarAction() { super("Eliminar"); } @Override public void actionPerformed(ActionEvent e) { // tell main GUI to remove *this* RowPanel object probMain2.removeRowPanel(RowPanel.this); } } }
代码有两个非内部类,但只有第一个是public,并且有main方法,所以两个非内部类都可以存在于一个文件中.这使得更好的mcve,因为现在人们可以更容易地将这些代码复制并粘贴到他们的IDE中并进行测试.
一个更好的解决方案是摆脱持有者JPanel和RowPanel而不是使用JTable,但我今晚没有时间或精力来做这件事.