⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 autoadaptcolumnwidthtable.java

📁 用于串口通讯测试的工具软件。完全使用java语言编写。
💻 JAVA
字号:
package com.zcsoft.swing;

import javax.swing.*;
import javax.swing.table.*;
import java.awt.event.*;
import java.util.*;
import java.awt.*;
import javax.swing.event.*;
import javax.swing.border.LineBorder;
import com.zcsoft.util.StringKit;

/**
 *
 * 设定最佳滚动区域大小{@link #setVisibleRowCount(int)}
 * <br>
 * 双击列的右边缘后自动调整列宽使其可显示所有内容
 * <br>
 * @version 2.3
 */
public class AutoAdaptColumnWidthTable extends JTable
{
	/** 表格默认显示行数 */
	private int visibleRowCount = 5;
	/** 在表格数据数据发生变化时,列宽重新调整 */
	private boolean columnWidthAdaptedOnChange = true;

	/** 参照javax.swing.JTable */
	public AutoAdaptColumnWidthTable(TableModel tm)
	{
		super(tm, null, null);
		initTable();
	}

	/**
	 * 初始化排序类
	 * 初始化内容包括:调整列宽、设置为排序、设置为非自调整列宽模式和不允许列自由排列模式
	 */
	private void initTable()
	{
		this.getTableHeader().setReorderingAllowed(false);
		this.setAutoResizeMode(this.AUTO_RESIZE_OFF);
		this.getTableHeader().addMouseListener(new HeaderClickHandler());
	}

	/**
	 * 用指定列的各个单元的最佳宽度尺寸设定为列宽.
	 * 如果列宽的最小尺寸为0,则不进行调整。
	 * 计算时,从第一行开始到最后一行
	 * @param col 要设定宽度的列索引
	 * @param from 起始行索引
	 * @param to 终止行索引(不包含)
	 */
	public void adaptColWidthOnCell(int col)
	{
		adaptColWidthOnCell(col, 0, this.getRowCount());
	}

	/**
	 * 用指定列的各个单元的最佳宽度尺寸设定为列宽.
	 * 如果列宽的最小尺寸为0,则不进行调整。
	 * 计算时,从第from行开始到第to行
	 * @param col 要设定宽度的列索引
	 */
	protected void adaptColWidthOnCell(int col, int from, int to)
	{
		TableColumn tc = this.getColumnModel().getColumn(col);
		if (tc.getMinWidth() == 0)
		{
			return;
		}
		TableCellRenderer tcr = tc.getCellRenderer();//列col的绘制器
		tcr = tcr!=null?tcr:this.getDefaultRenderer(this.getColumnClass(col));
		//列的最佳宽度
		int max = this.getTableHeader().getDefaultRenderer()
			.getTableCellRendererComponent(this, this.getColumnName(col), false, false, 0, col)
			.getPreferredSize().width;

		javax.swing.border.Border cb = UIManager.getBorder("TableHeader.cellBorder");
		if (cb != null)
		{
			max += cb.getBorderInsets(this).left + cb.getBorderInsets(this).right;
		}
		//System.out.print(max);
		int temp;//列col各行单元最佳宽度
		int margin = this.getColumnModel().getColumnMargin();
		int cellMax = max;//列col各行单元最佳宽度最大值
		Object value;
		Component rc = null;//单元绘制组件
		for(int i = from; i< to; i++)
		{
			value = this.getValueAt(i,col);
			if (value == null)
			{
				continue;
			}
			if (rc != null)
			{
				rc.invalidate();
			}
			rc = tcr.getTableCellRendererComponent(this, value, false, false, i, col);

			temp = rc.getPreferredSize().width + margin;//绘制时需要margin
			if (temp > cellMax)
			{
				//System.out.println(col+" temp="+temp);
				cellMax = temp;
			}
		}
		if (cellMax > max)//所有最大宽度由行单元决定,此时需要加上列边距
		{
			max = cellMax;
		}
		tc.setPreferredWidth(max);
		if(rc != null)
		{
			rc.invalidate();
		}
	}

	/**
	 * 更改表格的最佳尺寸和表格列宽度相关,而不是固定的某个值。
	 * @return 表格最佳尺寸(宽度未表格头最佳宽度和(屏幕尺寸宽度-100)两者的最小值;
	 *       高度未5行数据行所占用的高度)
	 * @since 2.1
	 */
	public Dimension getPreferredScrollableViewportSize()
	{
		return new Dimension(Math.min(this.getTableHeader().getPreferredSize().width,
													(int)(this.getToolkit().getScreenSize().width*0.6)),
							this.getRowHeight() * this.getVisibleRowCount());
	}

	/**
	 * 获取最佳显示行数
	 */
	public int getVisibleRowCount()
	{
		return visibleRowCount;
	}

	/**
	 * 设定最佳显示行数
	 * @param visibleRowCount 大于零的显示行数
	 */
	public void setVisibleRowCount(int visibleRowCount)
	{
		if (visibleRowCount > 0 && this.visibleRowCount != visibleRowCount)
		{
			this.visibleRowCount = visibleRowCount;
			//this.resizeAndRepaint();
		}
	}

	/**
	 * 排序和按列单元调整列宽实现
	 * 如果表格正在编辑,则不允许排序
	 */
	class HeaderClickHandler extends MouseAdapter
	{
		public void mouseClicked(MouseEvent e)
		{
			if (SwingUtilities.isLeftMouseButton(e) && !AutoAdaptColumnWidthTable.this.isEditing())
			{
				int col=AutoAdaptColumnWidthTable.this.getColumnModel().getColumnIndexAtX(e.getX());
				int c = getResizingColumn(col, e.getPoint());
				if(e.getClickCount() == 2 && c>=0)
				{
					AutoAdaptColumnWidthTable.this.adaptColWidthOnCell(c);
				}
			}
		}

		/**
		 * <p>判断鼠标是否在列标题的边框上</p>
		 * <p>当鼠标处于两列的中间时默认调整前一列的宽度</p>
		 * @param column 列号
		 * @param p 鼠标坐标
		 * @return -1表示在边框内,否则返回要调整的列编号
		 */
		private int getResizingColumn(int column, Point p)
		{
			if(column>-1)
			{
				Rectangle r = AutoAdaptColumnWidthTable.this.getTableHeader().getHeaderRect(column);
				r.grow(-3, 0);
				int midPoint = r.x + r.width/2;
				//当鼠标处于两列的中间时默认调整前一列的宽度
				return r.contains(p)?-1:p.x < midPoint?(column-1):column;
			}
			return column;
		}
	}

	/**
	 * DEBUG 当表格没有任何数据行时,一旦table获得焦点,且按tab键时,
	 * 会出现rowIndex>=0或者columnIndex>=0的情况,这样会触发行选择事件,
	 * 从而导致通过选中行来处理问题的代码的运行错误
	 * Updates the selection models of the table, depending on the state of the
	 * two flags: <code>toggle</code> and <code>extend</code>. All changes
	 * to the selection that are the result of keyboard or mouse events received
	 * by the UI are channeled through this method so that the behavior may be
	 * overridden by a subclass.
	 * <p>
	 * This implementation uses the following conventions:
	 * <ul>
	 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>false</em>.
	 *      Clear the previous selection and ensure the new cell is selected.
	 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>true</em>.
	 *      Extend the previous selection to include the specified cell.
	 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>false</em>.
	 *      If the specified cell is selected, deselect it. If it is not selected, select it.
	 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>true</em>.
	 *      Leave the selection state as it is, but move the anchor index to the specified location.
	 * </ul>
	 * @param  rowIndex   affects the selection at <code>row</code>
	 * @param  columnIndex  affects the selection at <code>column</code>
	 * @param  toggle  see description above
	 * @param  extend  if true, extend the current selection
	 *
	 */
	public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)
	{
		if (rowIndex >= this.getRowCount())
		{
			rowIndex = -1;
		}
		if (columnIndex >= this.getColumnCount())
		{
			columnIndex = -1;
		}
		super.changeSelection(rowIndex, columnIndex, toggle, extend);
	}

	/**
	 * 在添加数据行时,额外增加数据列宽度调整自动处理
	 * 纠正bug。当原选择行全部被删除时,并不会调用removeSelectionInterval()方法去触发
	 * ListSelectionEvent事件
	 */
	public void tableChanged(TableModelEvent e)
	{
		if (e.getType() == TableModelEvent.DELETE)
		{
			//由于表格数据模型事件的响应顺序是Last added first service机制
			//所以不能依赖别的事件侦听实现去完成removeSelectionInterval的调用
			//从而根据调用后表格的行选择状态进行后续操作的判断条件
			this.getSelectionModel().removeSelectionInterval(e.getFirstRow(), e.getLastRow());
		}
		super.tableChanged(e);
		if (this.isColumnWidthAdaptedOnChange()
			&& e.getType() == TableModelEvent.INSERT
			 && this.getAutoResizeMode() == AUTO_RESIZE_OFF)
		{
			//目前列的宽度全部重新计算,以后可以优化
			int start = 0;//e.getFirstRow();
			int end = getRowCount();//e.getLastRow()+1;
			/*if (start < 0)
				start = 0;
			if (end <= 0)
				end = getRowCount();*/
			try
			{
				for(int i=0, columnCnt=this.getColumnCount();i<columnCnt;i++)
				{
					adaptColWidthOnCell(i, start, end);
				}
			}
			catch(Exception ex)//可能因异步问题导致异常
			{
				ex.printStackTrace();
			}
		}
	}

	/**
	 * 设定在表格数据行增加时,各列列宽是否自动重新调整
	 * @param columnWidthAdaptedOnChange 是否重新调整
	 * @see #adaptColWidthOnCell(int, int, int)
	 */
	public void setColumnWidthAdaptedOnChange(boolean columnWidthAdaptedOnChange)
	{
		this.columnWidthAdaptedOnChange = columnWidthAdaptedOnChange;
	}

	/**
	 * 判断表格数据行增加时,各列列宽是否自动重新调整
	 * @return 是否自动重新调整
	 * @see #setColumnWidthAdaptedOnChange
	 */
	public boolean isColumnWidthAdaptedOnChange()
	{
		return columnWidthAdaptedOnChange;
	}

}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -