Windows鼠标框选效果

本文介绍如何在Java Swing中实现JTable的框选功能。通过自定义MyTableUI类并重写MouseInputListener,配合AbstractTable及AbstractScollPane类处理鼠标事件,最终实现在表格上的矩形区域选择。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

常见情况下,一个JTable是放到一个JScrollPane,这样数据量很大的时候,可以通过滚动条来控制。因此要实现框选效果,需要考虑到这两个主要组件的paint问题。

框选操作,可以分解为 鼠标左键点击,拖拽,释放三个动作,因此需要实现MouseInputListener。考虑到此框选操作为特定look&feel拥有,故以BasicTableUI为基类实现


public class MyTableUI extends BasicTableUI {

protected MouseInputListener createMouseInputListener() {

return new MyMouseInputHandler();

}
class MyMouseInputHandler extends MouseInputHandler {

private boolean ignoreDrag = false;

public void mousePressed(MouseEvent e) {
if (e.getClickCount() == 2) {
table.clearSelection();
}
Point origin = e.getPoint();
int row = table.rowAtPoint(origin);
int column = table.columnAtPoint(origin);
if (row != -1 && column != -1) {
if(table instanceof AbstractTable){
((AbstractTable)table).mousePressed(e.getPoint());
}
if (table.isCellSelected(row, column)) {
ignoreDrag = true;
} else {
super.mousePressed(e);
}
}
}

public void mouseClicked(MouseEvent e) {
super.mouseClicked(e);
}

public void mouseDragged(MouseEvent e) {
if (!ignoreDrag) {
super.mouseDragged(e);
if(table instanceof AbstractTable){
((AbstractTable)table).mouseDragged((Point)e.getPoint().clone());
}
}
}

public void mouseExited(MouseEvent e) {
if(table instanceof AbstractTable){
((AbstractTable)table).mouseExit();
}
}

public void mouseReleased(MouseEvent e) {
if (ignoreDrag) {
super.mousePressed(e);
ignoreDrag = false;

}
if(table instanceof AbstractTable){
((AbstractTable)table).mouseReleased();
}
super.mouseReleased(e);
}

}

然后在其中的AbstractTable类中实现相应的鼠标事件处理。
public class AbstractTable extends JTable {

private static final long serialVersionUID = 1L;

private volatile boolean canSelected = true;

private AbstractScollPane srcollPane;

AbstractTable() {
super();
setUI(new MyTableUI());
}

/*
* (non-Javadoc)
* @see javax.swing.JTable#changeSelection(int, int, boolean, boolean)
*/
public void changeSelection(int rowIndex,int columnIndex,boolean toggle, boolean extend) {
if(columnIndex != 1){
return;
}else{
super.changeSelection(rowIndex, columnIndex, toggle, extend);
}
}

/**
* @param p
*/
public void mousePressed(Point p) {
if(srcollPane == null){
srcollPane = (AbstractScollPane)SwingUtilities.getAncestorOfClass(AbstractScollPane.class, this);
}
Point pp = SwingUtilities.convertPoint(this, p, srcollPane);
srcollPane.setStartPoint(pp);
//clearSelection();
}

public void mouseReleased() {
canSelected = true;
try {
srcollPane.makeRectSelected();
srcollPane.setStartPoint(null);
srcollPane.setEndPoint(null);
srcollPane.repaint();
} catch (Exception e1) {
e1.printStackTrace();
} finally {
canSelected = true;
}
}

/**
* @param p
*/
public void mouseDragged(Point p) {
Point pp = SwingUtilities.convertPoint(this, p, srcollPane);
canSelected = false;
srcollPane.setEndPoint(pp);
srcollPane.repaint();
}

/**
*
*/
public void mouseExit() {
//startPoint = null;
//endPoint = null;
//canSelected = true;
}

/*
* (non-Javadoc)
* @see javax.swing.JTable#isCellSelected(int, int)
*/
public boolean isCellSelected(int row, int column) {
if (column != 1) {
return false;
} else {
if(canSelected){
return super.isCellSelected(row, column);
}else{
return false;
}
}
}

public void paintComponent(Graphics g) {
super.paintComponent(g);
}
}

最后由AbstractScollPane 来实现真正的框选效果。
public class AbstractScollPane extends JScrollPane {

private static final long serialVersionUID = 1L;
public String type;

private transient Point startPoint;
private transient Point endPoint;

public AbstractScollPane(Component comp){
super(comp);
this.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent e){
startPoint =e.getPoint();
if(getViewport().getView() instanceof AbstractTable) {
AbstractTable table = (AbstractTable)getViewport().getView();
table.clearSelection();
}
}
public void mouseReleased(MouseEvent e){
makeRectSelected();
startPoint = null;
endPoint = null;
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent e){
endPoint =e.getPoint();
repaint();
}
});
}
public String getType(){
return type;
}
/**
* @param type the type to set
*/
public void setType(String type) {
this.type = type;
}

public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
if (startPoint != null && endPoint != null) {
g2.setColor(Color.red);
int x = Math.min(startPoint.x, endPoint.x);
int y = Math.min(startPoint.y, endPoint.y);
int width = Math.abs(startPoint.x - endPoint.x);
int height = Math.abs(startPoint.y - endPoint.y);
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,0.2f));
g2.drawRect(x, y, width, height);
g2.setColor(Color.yellow);
g2.fillRect(x, y, width, height);
}
}

public void paintComponent(Graphics g) {
super.paintComponent(g);
}

public void makeRectSelected() {
if(startPoint != null && endPoint != null) {
if(getViewport().getView() instanceof AbstractTable) {
AbstractTable table = (AbstractTable)getViewport().getView();
Point tableLocation = table.getLocation();
Point tablePointInScroll = SwingUtilities.convertPoint(table, tableLocation, this);
Rectangle mousedRect = new Rectangle(Math.min(startPoint.x, endPoint.x),Math.min(startPoint.y, endPoint.y),Math.abs(startPoint.x - endPoint.x),Math.abs(startPoint.y - endPoint.y));
Rectangle interSection = SwingUtilities.computeIntersection(tablePointInScroll.x, tablePointInScroll.y, getSize().width, table.getHeight(), mousedRect);
if(interSection.equals(new Rectangle(0,0,0,0))){
return;
}else {
Point start = SwingUtilities.convertPoint(this, new Point(interSection.x,interSection.y), table);
Point end = SwingUtilities.convertPoint(this, new Point(interSection.x + interSection.width,interSection.y+interSection.height), table);
int srow = table.rowAtPoint(start);
int scolumn = table.columnAtPoint(start);
int erow = table.rowAtPoint(end);
if (erow == -1)
erow = table.getRowCount() - 1;
if (srow == -1)
srow = 0;
if ((scolumn == 0 || scolumn == 1) && Math.abs(erow - srow) >= 0) {
table.setRowSelectionInterval(srow, erow);
table.setColumnSelectionInterval(0, 1);
}else {
table.clearSelection();
}
}
}
}
}

/**
* @param endPoint the endPoint to set
*/
public void setEndPoint(Point endPoint) {
this.endPoint = endPoint;
}

/**
* @param startPoint the startPoint to set
*/
public void setStartPoint(Point startPoint) {
this.startPoint = startPoint;
}
}

总的来说,如果框选初始事件由最里层的AbstractTable捕获,则外传到包装它的AbstractScollPane由它来做具体的paint操作;如果是在AbstractScollPane上直接发生框选事件,就直接paint.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值