📄 autoadaptcolumnwidthtable.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 + -