JTable实现行拖拽

简介

JTable是swing中的表格插件,官方提供的DnD (drag and drop)指的是将数据从不同组件之间进行拖拽,如将JTable中某行的数据拖到JTextView。为了实现一个JTable中不同行之间进行拖拽,需要自己实现。

实现原理

  1. 自己新建一个类继承自BasicTableUI,重写createMouseInputListenerpaint方法。
public class DragDropRowTableUI extends BasicTableUI {
	@Override
	protected MouseInputListener createMouseInputListener() {
		return new MouseInputHandler() {
			public void mousePressed(MouseEvent e) {
				// 记录鼠标起始位置
			}

			public void mouseDragged(MouseEvent e) {
				// 计算鼠标移动的位置
				// 如果超过了上一行的中间位置,则向上挪动一行
				// 如果超过了下一行的中间位置,则向下挪动一行
			}
		};
	}

	@Override
	public void paint(Graphics g, JComponent c) {
		super.paint(g, c);
		// 使用copyArea方法将选中的行移动到鼠标的位置(鼠标位置为行中间高度)
		// 使用fillRect方法将空白区域填充背景色(比如将最后一行往下拖的时候,中间会空出一段区域)
	}
  1. 创建JTable时,指定UI为自定义的UI类
table = new JBTable(tableModel);
table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
table.setUI(new DragDropRowTableUI(true));

实现代码

具体实现细节中,还需要考虑一些因素,例如需要退出单元格的编辑模式,否则会导致编辑状态的组件覆盖了目标行的数据。

@RequiredArgsConstructor
public class DragDropRowTableUI extends BasicTableUI {
	private boolean draggingRow = false;
	private int startDragPoint;
	private int dyOffset;
	private int oldRowIndex = -1;
	private int newRowIndex = -1;
	
	@Setter
	private DragDropConfig dragDropConfig;

	@Override
	protected MouseInputListener createMouseInputListener() {
		return new DragDropRowMouseInputHandler();
	}

	@Override
	public void paint(Graphics g, JComponent c) {
		super.paint(g, c);
		if (draggingRow) {
			g.setColor(table.getParent().getBackground());
			Rectangle cellRect = table.getCellRect(table.getSelectedRow(), 0, false);
			g.copyArea(cellRect.x, cellRect.y, table.getWidth(), table.getRowHeight(), cellRect.x, dyOffset);
			if (dyOffset < 0) {
				g.fillRect(cellRect.x, cellRect.y + (table.getRowHeight() + dyOffset), table.getWidth(),
						dyOffset * -1);
			} else {
				g.fillRect(cellRect.x, cellRect.y, table.getWidth(), dyOffset);
			}
		}
	}

	class DragDropRowMouseInputHandler extends MouseInputHandler {
		public void mousePressed(MouseEvent e) {
			// 鼠标按下获得起点位置
			super.mousePressed(e);
			startDragPoint = (int) e.getPoint().getY();
			oldRowIndex = table.getSelectedRow();
		}

		public void mouseDragged(MouseEvent e) {
			// 拖拽选中的行,稍微难点就在鼠标拖拽这一事件里面
			int fromRow = table.getSelectedRow();
			if (fromRow >= 0) {
				draggingRow = true;
			}
			int rowHeight = table.getRowHeight();
			// 获取选中行中间的Y坐标
			int middleOfSelectedRow = (rowHeight * fromRow) + (rowHeight / 2);
			int toRow = -1;
			int yMousePoint = (int) e.getPoint().getY();
			if (yMousePoint < (middleOfSelectedRow - rowHeight)) {
				// Move row up
				toRow = fromRow - 1;
			} else if (yMousePoint > (middleOfSelectedRow + rowHeight)) {
				// Move row down
				toRow = fromRow + 1;
			}
			// 数据调换
			boolean canDrag = true;
			if (dragDropConfig != null && toRow >= 0 && toRow < table.getRowCount()) {
				canDrag = dragDropConfig.canDrap(table.getModel(), fromRow, toRow);
			}
			if (canDrag) {
				if (toRow >= 0 && toRow < table.getRowCount()) {
					cancelEditing();
					TableModel model = table.getModel();
					for (int i = 0; i < model.getColumnCount(); i++) {
						Object fromValue = model.getValueAt(fromRow, i);
						Object toValue = model.getValueAt(toRow, i);
						model.setValueAt(toValue, fromRow, i);
						model.setValueAt(fromValue, toRow, i);
					}
					if (model instanceof RowNumberTableModel) {
						((RowNumberTableModel)model).swapRowData(fromRow, toRow);
					}
					table.setRowSelectionInterval(toRow, toRow);
					newRowIndex = toRow;
					startDragPoint = yMousePoint;
				}
			} else {
				startDragPoint = yMousePoint;
			}
			dyOffset = (startDragPoint - yMousePoint) * -1;
			table.repaint();
		}

		public void mouseReleased(MouseEvent e) {
			super.mouseReleased(e);
			draggingRow = false;
			if (newRowIndex >= 0 && newRowIndex < table.getRowCount() && newRowIndex != oldRowIndex) {
				table.setRowSelectionInterval(newRowIndex, newRowIndex);
			}
			// 对表格的重新刷新。。。
			table.repaint();
			newRowIndex = -1;
			oldRowIndex = -1;
		}

		private void cancelEditing() {
			int row = table.getEditingRow();
			int column = table.getEditingColumn();
			if (row != -1 && column != -1) {
				TableCellEditor cellEditor = table.getCellEditor(row, column);
				if (cellEditor != null) {
					cellEditor.cancelCellEditing();
				}
			}
		}
	}
	
	@FunctionalInterface
	public interface DragDropConfig {

		boolean canDrap(TableModel model, int fromRow, int toRow);
	}
}

本示例代码增加了一些特性,如:

  • 增加某些行不能拖拽的配置功能。
  • 如果TableModel为RowNumberTableModel(自定义类,实现了行序号、行额外数据功能),则在发生数据调换的时候,通知TableModel。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现JTable的多表头,可以使用JTableHeader的setPreferredSize()方法来设置表头的高度,并使用TableColumnModel的getColumn()方法来获取表头中每列的组件。 以下是一个示例代码: ``` import javax.swing.*; import javax.swing.table.*; import java.awt.*; public class MultiRowHeaderTable extends JFrame { public MultiRowHeaderTable() { super("Multi-Row Headers in JTable"); DefaultTableModel model = new DefaultTableModel(5, 5); JTable table = new JTable(model); table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); // Create a multi-row header JTableHeader header = table.getTableHeader(); header.setPreferredSize(new Dimension(100, 50)); JPanel panel = new JPanel(new BorderLayout()); panel.add(header, BorderLayout.NORTH); panel.add(table, BorderLayout.CENTER); // Set the cell renderer for each column of the header TableColumnModel columnModel = table.getColumnModel(); for (int i = 0; i < columnModel.getColumnCount(); i++) { TableColumn column = columnModel.getColumn(i); column.setHeaderRenderer(new MultiRowHeaderRenderer()); } getContentPane().add(panel); pack(); setVisible(true); } public static void main(String[] args) { new MultiRowHeaderTable(); } } class MultiRowHeaderRenderer extends JLabel implements TableCellRenderer { public MultiRowHeaderRenderer() { setOpaque(true); setHorizontalAlignment(CENTER); setVerticalAlignment(TOP); } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { setText(value.toString()); return this; } } ``` 在这个示例中,我们创建了一个多表头,其中每列的组件都是JLabel。我们使用TableColumnModel的getColumn()方法来获取每列的组件,并将其设置为一个MultiRowHeaderRenderer对象。MultiRowHeaderRenderer类实现了TableCellRenderer接口,用于呈现表头中每个单元格的值。 希望这能帮助到你!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值