Русский English Тэги View Sergey Zolotaryov's profile on LinkedIn Вход
Простой табличный LayoutManager для Swing
Постоянная ссылка 15-02-2008 anydoby java

В очередной раз убеждаюсь, что главная беда Java - заумные программеры. Отличный пример этому - концепция LayoutManagerов в свинге. Это ж кошмар, чтобы добиться пристойного расположения компонентов, нужно написать тонны кода для GridBagLayout. А ежели что-то надо добавить - все поехало и начинай с начала. Замечательный GridLayout в SWT - указал число колонок и оглы. Все остальное он сделает за программера. И самое главное - выглядит красиво и без сюрпризов.

LayoutManager с табличным уколоном, подобный SWT, когда-то был и для свинга. Назывался он, как нетрудно догадаться, TableLayout. Однако недавно, в поисках этого менеджера я потратил целый день, потому что какая-то заумная зараза написала похожий менеджер с таким же названием и даже засунула его в сановский туториал. Естественно, все ссылки в гугле ведут к нему. А пользоваться им ох как непросто.

Короче, откопал я старый добрый TableLayout - в нем, как в SWT, указал число колонок и все:


package com.anydoby.swing;


import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.LayoutManager;

/**
 * @author szo
 * @version 1.0, 2005-11-03
 */
public class TableLayout implements LayoutManager, java.io.Serializable {

	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 5154863521922195727L;

	/**
	 * This is the horizontal gap (in pixels) which specifies the space between columns. It can be
	 * changed at any time. This should be a non negative integer.
	 * 
	 * @serial
	 * @see #getHgap()
	 * @see #setHgap(int)
	 */
	int hgap;

	/**
	 * This is the vertical gap (in pixels) which specifies the space between rows. They can be
	 * changed at any time. This should be a non negative integer.
	 * 
	 * @serial
	 * @see #getVgap()
	 * @see #setVgap(int)
	 */
	int vgap;

	/**
	 * This is the number of columns specified for the table. The number of columns can be changed at
	 * any time. This should be a non negative integer, where '0' means 'any number' meaning that the
	 * number of Columns in that dimension depends on the other dimension.
	 * 
	 * @serial
	 * @see #getColumns()
	 * @see #setColumns(int)
	 */
	int cols;

	/**
	 * debug
	 */
	public boolean debug = false;


	/**
	 * Creates a table layout with a default of one column per component, in a single row.
	 */
	public TableLayout() {
		this(1, 0, 0);
	}


	/**
	 * Creates a table layout with the specified number of columns.
	 * 
	 * @param cols
	 *          the number of columns. This must be greater than zero.
	 */
	public TableLayout(int cols) {
		this(cols, 0, 0);
	}


	/**
	 * Creates a table layout with the specified number of columns.
	 * <p>
	 * In addition, the horizontal and vertical gaps are set to the specified values. Horizontal gaps
	 * are placed at the left and right edges, and between each of the columns. Vertical gaps are
	 * placed at the top and bottom edges, and between each of the rows.
	 * <p>
	 * 
	 * @param cols
	 *          the columns, with the value zero meaning any number of columns.
	 * @param hgap
	 *          the horizontal gap.
	 * @param vgap
	 *          the vertical gap.
	 */
	public TableLayout(int cols, int hgap, int vgap) {
		setColumns(cols);
		this.hgap = hgap;
		this.vgap = vgap;
	}


	/**
	 * Adds the specified component with the specified name to the layout.
	 * 
	 * @param name
	 *          the name of the component.
	 * @param comp
	 *          the component to be added.
	 */
	public void addLayoutComponent(String name, Component comp) {
		// Empty
	}


	/**
	 * Gets the number of columns in this layout.
	 * 
	 * @return the number of columns in this layout.
	 */
	public int getColumns() {
		return this.cols;
	}


	/**
	 * Gets the horizontal gap between components.
	 * 
	 * @return the horizontal gap between components.
	 */
	public int getHgap() {
		return this.hgap;
	}


	/**
	 * Gets the vertical gap between components.
	 * 
	 * @return the vertical gap between components.
	 */
	public int getVgap() {
		return this.vgap;
	}


	/**
	 * Lays out the specified container using this table layout.
	 * <p>
	 * This method reshapes the components in the specified target container in order to satisfy the
	 * constraints of the <code>TableLayout</code> object.
	 * 
	 * @param parent
	 *          the container to be laid out
	 */
	public void layoutContainer(Container parent) {
		synchronized (parent.getTreeLock()) {
			Insets insets = parent.getInsets();
			int ncomponents = parent.getComponentCount();
			int rows = ncomponents / this.cols;

			// Work out the column widths and heights
			int[] w = new int[this.cols];
			int[] h = new int[rows];
			for (int col = 0; col < this.cols; col++)
				w[col] = 0;
			for (int row = 0, i = 0; row < rows; row++) {
				h[row] = 0;
				for (int col = 0; col < this.cols; col++) {
					Component comp = parent.getComponent(i++);
					Dimension d = comp.getPreferredSize();
					if ( w[col] < d.width ) w[col] = d.width;
					if ( h[row] < d.height ) h[row] = d.height;
				}
			}

			// Now layout
			for (int row = 0, i = 0, y = insets.top; row < rows; row++) { // ov
				int rh = h[row];
				for (int col = 0, x = insets.left; col < this.cols; col++) { // ov
					Component comp = parent.getComponent(i++);
					int cw = w[col];
					if ( col == (this.cols - 1) ) {
						int ww = parent.getWidth() - x - insets.right;
						comp.setBounds(x, y, (ww > 0) ? ww - this.hgap : cw, rh);
					} // ov
					else {
						comp.setBounds(x, y, cw, rh);
						x += this.hgap + cw;
					}
				}
				y += this.vgap + rh;
			}
		}
	}


	/**
	 * Determines the minimum size of the container argument using this table layout.
	 * <p>
	 * The minimum width of a table layout is the sum of the minimum widths of the columns plus the
	 * intercolumn gaps.
	 * <p>
	 * The minimum height of a table layout is the sum of the minimum heights of the rows plus the
	 * intercomponent gaps.
	 * 
	 * @param parent
	 *          the component to be laid out
	 * @return the minimum dimensions needed to lay out the subcomponents of the specified container.
	 */
	public Dimension minimumLayoutSize(Container parent) {
		return preferredLayoutSize(parent);
	}


	/**
	 * Determines the preferred size of the container argument using this table layout.
	 * <p>
	 * The preferred width of a table layout is the sum of the preferred widths of the columns plus
	 * the intercolumn gaps.
	 * <p>
	 * The preferred height of a table layout is the sum of the preferred heights of the rows plus the
	 * intercomponent gaps.
	 * 
	 * @param parent
	 *          the container in which to do the layout.
	 * @return the preferred dimensions to lay out the subcomponents of the specified container.
	 */
	public Dimension preferredLayoutSize(Container parent) {
		synchronized (parent.getTreeLock()) {
			Insets insets = parent.getInsets();
			int ncomponents = parent.getComponentCount();
			int rows = ncomponents / this.cols;

			int[] w = new int[this.cols];
			for (int col = 0; col < this.cols; col++)
				w[col] = 0;
			int h = 0;
			for (int row = 0, i = 0; row < rows; row++) {
				int hh = 0;
				for (int col = 0; col < this.cols; col++) {
					if ( i < ncomponents ) {
						Component comp = parent.getComponent(i++);
						Dimension d = comp.getPreferredSize();
						if ( w[col] < d.width ) w[col] = d.width;
						if ( hh < d.height ) hh = d.height;
					}
				}
				h += hh;
			}

			int hh = insets.top + insets.bottom + (rows - 1) * this.vgap + h;
			int ww = insets.left + insets.right + (this.cols - 1) * this.hgap;
			for (int col = 0; col < this.cols; col++)
				ww += w[col];

			return new Dimension(ww, hh);
		}
	}


	/**
	 * Removes the specified component from the layout.
	 * 
	 * @param comp
	 *          the component to be removed.
	 */
	public void removeLayoutComponent(Component comp) {
		// Empty
	}


	/**
	 * Sets the number of columns in this layout to the specified value.
	 * 
	 * @param cols
	 *          the number of columns in this layout.
	 * @exception IllegalArgumentException
	 *              if the cols value is less than 2.
	 */
	public void setColumns(int cols) {
		if ( cols < 1 ) {
			throw new IllegalArgumentException("'cols' must be > 0"); //$NON-NLS-1$
		}
		this.cols = cols;
	}


	/**
	 * Sets the horizontal gap between components to the specified value.
	 * 
	 * @param hgap
	 *          the horizontal gap between components.
	 */
	public void setHgap(int hgap) {
		this.hgap = hgap;
	}


	/**
	 * Sets the vertical gap between components to the specified value.
	 * 
	 * @param vgap
	 *          the vertical gap between components.
	 */
	public void setVgap(int vgap) {
		this.vgap = vgap;
	}


	/**
	 * Returns the string representation of this table layout's values.
	 * 
	 * @return a string representation of this table layout.
	 */
	public String toString() {
		return getClass().getName() + "[hgap=" + this.hgap + ",vgap=" + this.vgap + //$NON-NLS-1$ //$NON-NLS-2$
				",cols=" + this.cols + "]"; //$NON-NLS-1$//$NON-NLS-2$
	}
}

Добавить комментарий

xelix
21-02-2008

вообще-то GridLayout есть и в Swing'e. пример

import java.awt.*; import java.applet.Applet; public class ButtonGrid extends Applet { public void init() { setLayout(new GridLayout(3,2)); add(new Button("1")); add(new Button("2")); add(new Button("3")); add(new Button("4")); add(new Button("5")); add(new Button("6")); } }

статья - http://java.sun.com/docs/books/tutorial/uiswing/layout/grid.html

anydoby
25-02-2008

Вообще-то, если бы этот LayoutManager можно было бы использовать, я бы эту статью не писал :) Ни один заказчик не примет формочку, в которой все элементы одинакового размера (а именно это и делает GridLayout). Его можно было бы назвать UglyLayout или как-нибудь еще - написан явно для "галочки" - вот мол, у нас тоже есть гриды. Но с SWTшным GridLayout ему не тягаться. И потом, часто я не знаю наперед, сколько у меня будет строк в форме, как этим менеджером можно добиться того, что мне нужно?

Предыдущая статья Как правильно достать имя русского файла в Linux Следующая статья Вся правда о Java на PocketPC