如何在鼠标按下时更改JButton的颜色?

18

我希望可以根据鼠标事件(如鼠标进入、退出、按下等)获得自定义颜色。因此,我编写了以下代码来实现这一点。除了鼠标按下事件无法起作用外,其他方面都很好。

只有在使用UIManager.put("Button.select", Color.red);来覆盖颜色时才有效果。使用UIManager的问题是会对所有按钮进行更改。

有人能告诉我可能哪里出错了或者完成我想做的事情的最佳方法是什么吗?

我的代码:

final JButton btnSave = new JButton("Save");

btnSave.setForeground(new Color(0, 135, 200).brighter());
btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
btnSave.setBorder(null);
btnSave.setBackground(new Color(3, 59, 90));

btnSave.addMouseListener(new MouseListener() {
    @Override
    public void mouseReleased(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // Not working :(
        btnSave.setBackground(Color.pink);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }
});

编辑1: 所以,我用mKorbel建议的ChangeListenerButtonModel代替了MouseListener。 使用这段代码时,我仍然没有观察到在按钮上按下鼠标时的任何变化,除非我按下并拖到按钮外。 有什么想法吗?

btnSave.getModel().addChangeListener(new ChangeListener() {

    @Override
    public void stateChanged(ChangeEvent e) {
        ButtonModel model = (ButtonModel) e.getSource();

        if (model.isRollover()) {
            btnSave.setBackground(new Color(3, 59, 90).brighter());
        } else if (model.isPressed()) {
            btnSave.setBackground(Color.BLACK);
        } else {
            btnSave.setBackground(new Color(3, 59, 90));
        }
    }
});

好的,但是你为什么要在 JButton 上添加鼠标监听器? - Branislav Lazic
当鼠标进入、退出和按下时,改变按钮的颜色。因此,在鼠标悬停等情况下突出显示按钮。 - ktulinho
但是为什么要这么多花哨的颜色呢? - Amarnath
1
@ktulinho,你读了我的回答的第一段吗?这清楚地解释了为什么这不起作用。 - Guillaume Polet
请参考此答案 - trashgod
+1 @GuillaumePolet 哦,你说得对!我错过了,直接进入了代码。我正在使用你的建议,它像魔法一样工作!谢谢 :) - ktulinho
9个回答

31

问题的原因在于JButton默认填充其内容区域,当按钮被按下时,金属L&F会自动使用所选颜色来填充它。

最好的做法是扩展JButton以创建自己的按钮,禁用内容区域填充,并自己绘制按钮的背景。

这里是一个小的演示(不确定它是否适用于其他L&F,甚至可以确定它不适用):

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

public class TestButton {

    class MyButton extends JButton {

        private Color hoverBackgroundColor;
        private Color pressedBackgroundColor;

        public MyButton() {
            this(null);
        }

        public MyButton(String text) {
            super(text);
            super.setContentAreaFilled(false);
        }

        @Override
        protected void paintComponent(Graphics g) {
            if (getModel().isPressed()) {
                g.setColor(pressedBackgroundColor);
            } else if (getModel().isRollover()) {
                g.setColor(hoverBackgroundColor);
            } else {
                g.setColor(getBackground());
            }
            g.fillRect(0, 0, getWidth(), getHeight());
            super.paintComponent(g);
        }

        @Override
        public void setContentAreaFilled(boolean b) {
        }

        public Color getHoverBackgroundColor() {
            return hoverBackgroundColor;
        }

        public void setHoverBackgroundColor(Color hoverBackgroundColor) {
            this.hoverBackgroundColor = hoverBackgroundColor;
        }

        public Color getPressedBackgroundColor() {
            return pressedBackgroundColor;
        }

        public void setPressedBackgroundColor(Color pressedBackgroundColor) {
            this.pressedBackgroundColor = pressedBackgroundColor;
        }
    }

    protected void createAndShowGUI() {
        JFrame frame = new JFrame("Test button");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        final MyButton btnSave = new MyButton("Save");
        btnSave.setForeground(new Color(0, 135, 200).brighter());
        btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
        btnSave.setBorder(null);
        btnSave.setBackground(new Color(3, 59, 90));
        btnSave.setHoverBackgroundColor(new Color(3, 59, 90).brighter());
        btnSave.setPressedBackgroundColor(Color.PINK);
        frame.add(btnSave);
        frame.setSize(200, 200);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new TestButton().createAndShowGUI();
            }
        });

    }

}

5
+1 表示支持 new Color(3, 59, 90).brighter().brighter(),并非开玩笑 :-)。请注意,本翻译保留了原文的意思和语气,同时增加了可读性以使内容更容易理解。 - mKorbel

11

从来没有使用过MouseListener来处理JButton。但是你能告诉我为什么吗? - Amarnath
1
+1,确实使用ButtonModel是更好的选择。@Che,总是依赖于更高级别的模型抽象,比如ButtonModel,而不是依赖于低级别的API,比如MouseListener,这样做总是更好的。 - Guillaume Polet
@Che,你的问题的答案在(@GuillaumePolet)的评论中,即“更高级别的模型抽象”与“低级别”的区别。 - mKorbel
@mKorbel 我已经看到了。那么你的意思是尽可能使用所有高级抽象类 - Amarnath
显示剩余3条评论

6
public class MyCustomButton extends JButton {
    private Color pressedColor = Color.GREEN;
    private Color rolloverColor = Color.RED;
    private Color normalColor = Color.BLUE;

    public MyCustomButton (String text) {
        super(text);
        setBorderPainted(false);
        setFocusPainted(false);

        setContentAreaFilled(false);
        setOpaque(true);

        setBackground(normalColor);
        setForeground(Color.WHITE);
        setFont(new Font("Tahoma", Font.BOLD, 12));
        setText(text);

        addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent evt) {
                if (getModel().isPressed()) {
                    setBackground(pressedColor);
                } else if (getModel().isRollover()) {
                    setBackground(rolloverColor);
                } else {
                    setBackground(normalColor);
                }
            }
        });
    }
}

1
尝试使用您已经拥有的内容:
yourButton.setBorder(null);
yourButton.setContentAreaFilled(false);

1
在我的情况下,我只想要当用户按下按钮时进行简单的背景和颜色切换。
改编自Guillaume Polet的解决方案:
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.ButtonModel;
import javax.swing.JButton;

@SuppressWarnings("serial")
class Button extends JButton {

    private Color pressedForeground = Color.BLACK;
    private Color pressedBackground = Color.WHITE;

    Button() {
        this(null);
    }

    Button(String text) {
        super(text);
        super.setContentAreaFilled(false);
        setForeground(Color.WHITE);
        setBackground(Color.BLACK);
        setFocusPainted(false);
        setBorderPainted(false);
        setFont(new Font("arial", Font.PLAIN, 16));
    }

    @Override
    public void paint(Graphics g) {
        Color oldFg = getForeground();
        Color newFg = oldFg;
        ButtonModel mod = getModel();

        if (mod.isPressed()) {
            newFg = pressedForeground;
            g.setColor(pressedBackground);
        } else {
            g.setColor(getBackground());
        }

        g.fillRect(0, 0, getWidth(), getHeight());
        setForeground(newFg);
        super.paintComponent(g);
        setForeground(oldFg);
    }
}

0

这对我有效!兄弟,试试这个...

final JButton btnSave = new JButton("Save");

btnSave.setForeground(new Color(0, 135, 200).brighter());
btnSave.setHorizontalTextPosition(SwingConstants.CENTER);
btnSave.setBorder(null);

UIManager.put("Button.select", new Color(3, 59, 90)); //<--- Added ---
//btnSave.setBackground(new Color(3, 59, 90));

btnSave.addMouseListener(new MouseListener() {
    @Override
    public void mouseReleased(MouseEvent e) {
        UIManager.put("Button.select", new Color(3, 59, 90)); //<--- Added ---
        //btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mousePressed(MouseEvent e) {
        // it's working :)
        UIManager.put("Button.select", Color.pink); //<--- Added ---
        //btnSave.setBackground(Color.pink);
    }

    @Override
    public void mouseExited(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90));
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        btnSave.setBackground(new Color(3, 59, 90).brighter());
    }
});    

0

初始化一个按钮

JButton button = new JButton();

接下来,在鼠标按下时执行以下操作:

button.setContentAreaFilled(false);
button.setOpaque(true);
button.setBackground(/*Insert Color Here*/);

当鼠标释放时,执行以下操作:

button.setContentAreaFilled(true);
button.setBackground(/*Reset Color Here*/);

0

可能没什么问题,但可以试着用大写字母的Color.PINK代替小写字母吗?这样做有什么变化吗?

此外,mousepressed和mouseclicked不会互相覆盖吗?当你按下鼠标时应该触发mousepressed,而当你释放鼠标时应该触发clicked。


可能没有什么区别,但可以尝试使用大写字母的Color.PINK吗?这没有任何区别。它们都代表完全相同的颜色。 - Guillaume Polet
Color.PINK 指的是内部的 Color.pink。它们是相同的。 - Amarnath
即便如此,鼠标点击事件不是会在鼠标按下事件之后立即发生吗?==> mouseClicked(MouseEvent) 在用户单击所监听的组件后立即调用。 ==> mousePressed(MouseEvent) 当光标位于所监听的组件上并且用户按下鼠标按钮时立即调用。 - Random IT Guy
不,事件按以下顺序发生:鼠标按下,可能是鼠标拖动,鼠标释放,然后可能是鼠标点击(只要按钮没有释放,就不会触发点击事件)。 - Guillaume Polet

0

不要设置颜色,因为那似乎不起作用,你可以尝试将背景设置为可拉伸的图像文件,并将其设置为背景。这样能行吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接