简体   繁体   English

更改JButton颜色并同时移动JTextArea插入符号

[英]Change JButton color and move JTextArea Caret Simultaneously

I'm writing a program where there is a keyboard and a JTextArea. 我正在编写一个有键盘和JTextArea的程序。 Once a button is pressed, the color of the button should change as well as the caret position. 按下按钮后,按钮的颜色以及插入标记的位置都应更改。 I used Key Binding. 我使用了键绑定。 Now if I first change the color then move the caret, only the caret moves but color doesn't change and vise versa. 现在,如果我先更改颜色然后移动插入符号,则只有插入符号移动,但颜色不会更改,反之亦然。 Here is a sample of my code: (I have many panels to organize the rest of the buttons...) 这是我的代码示例:(我有很多面板来组织其余的按钮...)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;

import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.ButtonGroup;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;

public class KeyboardTest extends JFrame {

public static final JTextArea textToType = new JTextArea("jjj jjj jjj jjj kkk kkk kkk kkk jjj kkk jjj kkk jjj kkk jkj jkj jkj jkj kjk kjk kjk \r\nkjk jjj jjj jjj kkk kkk kkk jk jk jk kj kj kj jj kk jk kj kj jk jj jk kk kj j j j j k \r\nk k k j k k j j k k j jkj jjk kjj kkj jkk kkk jjj kjk");
public static final int caretPosition = 0;

public static void main(String[] args) {
    new KeyboardTest();
}

public KeyboardTest() {
    EventQueue.invokeLater(new Runnable() {
        @Override
        public void run() {
            try {
        UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
            } catch (Exception ex) {
                try {
                    UIManager.setLookAndFeel(UIManager
                            .getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException
                        | IllegalAccessException
                        | UnsupportedLookAndFeelException e) {
                    e.printStackTrace();
                }
            }
            JFrame frame = new JFrame("Testing");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setLayout(new BorderLayout());
            frame.add(new TestPane());
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            frame.setBounds(100, 100, 650, 390);
        }
    });

}

public class TestPane extends JPanel {

    public TestPane() {
        JPanel contentPane = new JPanel();
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        add(contentPane);
        GridBagLayout gbl_contentPane = new GridBagLayout();
        gbl_contentPane.columnWidths = new int[] { 614, 0 };
        gbl_contentPane.rowHeights = new int[] { 31, 14, 51, 181, 25, 0 };
        gbl_contentPane.columnWeights = new double[] { 0.0,
                Double.MIN_VALUE };
        gbl_contentPane.rowWeights = new double[] { 0.0, 0.0, 0.0, 0.0,
                0.0, Double.MIN_VALUE };
        contentPane.setLayout(gbl_contentPane);

        JLabel title = new JLabel(
                "Our first touch typing lesson introduces 2 home row keys for the right hand: j k");
        GridBagConstraints gbc_title = new GridBagConstraints();
        gbc_title.fill = GridBagConstraints.BOTH;
        gbc_title.insets = new Insets(0, 0, 5, 0);
        gbc_title.gridx = 0;
        gbc_title.gridy = 1;
        contentPane.add(title, gbc_title);

        textToType.setEditable(false);
        textToType.setFont(new Font("Lucida Console", Font.PLAIN, 12));
        textToType.setBackground(new Color(255, 250, 205));
        textToType
                .setText("jjj jjj jjj jjj kkk kkk kkk kkk jjj kkk jjj kkk jjj kkk jkj jkj jkj jkj kjk kjk kjk \r\nkjk jjj jjj jjj kkk kkk kkk jk jk jk kj kj kj jj kk jk kj kj jk jj jk kk kj j j j j k \r\nk k k j k k j j k k j jkj jjk kjj kkj jkk kkk jjj kjk");
        textToType.getCaret().setVisible(true);
        textToType.setCaretPosition(caretPosition);
        textToType.setFocusable(false);
        GridBagConstraints gbc_textToType = new GridBagConstraints();
        gbc_textToType.fill = GridBagConstraints.BOTH;
        gbc_textToType.insets = new Insets(0, 0, 5, 0);
        gbc_textToType.gridx = 0;
        gbc_textToType.gridy = 2;
        contentPane.add(textToType, gbc_textToType);

        JPanel keyboardPanel = new JPanel();
        GridBagConstraints gbc_keyboardPanel = new GridBagConstraints();
        gbc_keyboardPanel.fill = GridBagConstraints.BOTH;
        gbc_keyboardPanel.insets = new Insets(0, 0, 5, 0);
        gbc_keyboardPanel.gridx = 0;
        gbc_keyboardPanel.gridy = 3;
        contentPane.add(keyboardPanel, gbc_keyboardPanel);
        keyboardPanel.setLayout(new GridLayout(5, 0, 0, 0));

        JPanel kPanel1 = new JPanel();
        keyboardPanel.add(kPanel1);
        kPanel1.setLayout(null);

        JPanel buttonspanel1 = new JPanel();
        buttonspanel1.setBounds(0, 0, 523, 36);
        kPanel1.add(buttonspanel1);
        buttonspanel1.setLayout(new GridLayout(0, 13, 0, 5));

        JButton one = new JButton("1");
        one.setFocusable(false);
        buttonspanel1.add(one);

        addKeyBindingGreen(one, "1", KeyEvent.VK_1);

        addKeyBinding(one, "1", KeyEvent.VK_1);
}
//Key Binding to change the button to green...

        protected void addKeyBindingGreen(JButton btn, String name,
            int virtualKey) {
        ActionMap am = getActionMap();
        InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);

        im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name
                + ".pressed");
        im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name
                + ".released");

        am.put(name + ".pressed", new KeyActionGreen(btn, true));
        am.put(name + ".released", new KeyActionGreen(btn, false));
    }

 //Key binding to move the caret

        protected void addKeyBinding(JButton btn, String name,
            int virtualKey) {
        ActionMap am = getActionMap();
        InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);

        im.put(KeyStroke.getKeyStroke(virtualKey, 0, false), name
                + ".pressed");
        im.put(KeyStroke.getKeyStroke(virtualKey, 0, true), name
                + ".released");

        am.put(name + ".pressed", new KeyAction(btn, true, caretPosition));
        am.put(name + ".released", new KeyAction(btn, false, caretPosition ));
    }
}
public class KeyAction extends AbstractAction {

    private JButton btn;
    private boolean cur;
    private int caretPosition;

    public KeyAction(JButton btn, boolean cur, int caretPosition) {
        this.btn = btn;
        this.cur = cur;
        this.caretPosition = caretPosition;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (cur) {
            btn.getModel().setPressed(true);
            textToType.setCaretPosition(caretPosition++);
        } else {
            btn.getModel().setPressed(false);
        }
    }

}
public class KeyActionGreen extends AbstractAction {

    private JButton btn;
    private boolean highlight;

    public KeyActionGreen(JButton btn, boolean highlight) {
        this.btn = btn;
        this.highlight = highlight;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (highlight) {
            btn.getModel().setPressed(true);
            btn.setBackground(Color.GREEN);
            btn.setOpaque(true);
        } else {
            btn.getModel().setPressed(false);
            btn.setBackground(null);
            btn.setOpaque(false);
        }
    }

    }
}

You are adding an Action to the name + ".pressed" and name + ".released" key of the ActionMap , both in your addKeyBinding and addKeyBindingGreen methods. 您正在通过addKeyBindingaddKeyBindingGreen方法将Action添加到ActionMapname + ".pressed"name + ".released"键。

AcationMap is a Map . AcationMap是一个Map The behavior of a Map is that each key can contain only one value. Map的行为是每个键只能包含一个值。 If you attempt to put two values to the same key, the first value you put will get pushed out by the second value, in your case, the value being whichever Action you add second. 如果您尝试将两个值放入同一个键,则您输入的第一个值将由第二个值推出,在这种情况下,该值即为您再加上第二个Action的值。

Only choice is to combine the code you want into one Action 唯一的选择是将所需的代码合并为一个Action

Updated 更新

There are three core issues, 有三个核心问题,

Issue #1 第1期

You passing the value of caretPosition (from KeyboardTest ) to the KeyAction , the problem with this, is the KeyAction will only ever update the class instance of caretPosition relative to itself, that is, if you have two instance of KeyAction , they will have two separate values and will be updated independently. 您将caretPosition的值(从KeyboardTest )传递给KeyAction ,这是一个问题,就是KeyAction只会相对caretPosition自身更新caretPosition的类实例,也就是说,如果您有两个KeyAction实例,则它们将有两个单独的值,并将独立更新。

This is because parameters in Java are passed by value, not by reference... 这是因为Java中的参数是通过值而不是通过引用传递的...

You could pass an object which wrapped the int value, making sure you pass the same instance to all the buttons. 您可以传递包装int值的对象,确保将相同的实例传递给所有按钮。 This way the actions would be modifying the same value or you could simply make the action's reference the class instance field instead... 这样,动作将修改相同的值,或者您可以简单地使动作的引用代替类实例字段...

public int caretPosition = 0;

//...

public class KeyAction extends AbstractAction {

    private JButton btn;
    private boolean cur;

    public KeyAction(JButton btn, boolean cur) {
        this.btn = btn;
        this.cur = cur;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (cur) {
            btn.getModel().setPressed(true);
            textToType.setCaretPosition(caretPosition++);
        } else {
            btn.getModel().setPressed(false);
        }
    }

}

Issue #2 第2期

You are doing a post increment of the caretPosition instead of a pre increment... 您正在执行caretPosition的后递增而不是预递增...

textToType.setCaretPosition(caretPosition++);

What this means is, the value of caretPosition is only been changed AFTER the setCaretPosition returns, meaning that the first time the action is triggered, it's value is 0, still... 这意味着,仅在setCaretPosition返回之后才更改caretPosition的值,这意味着第一次触发动作时,其值为0,仍然...

You should change this to something more like... 您应该将其更改为更像...

textToType.setCaretPosition(++caretPosition);

Issue #3 问题#3

As, has already been pointed out, you can only attach a single Action to a KeyStroke , via the ActionMap / InputMap bindings. 如前所述,您只能通过ActionMap / InputMap绑定将单个Action附加到KeyStroke

This leaves you with at least three basic options... 这给您至少三个基本选择...

One... 一...

You could resign yourself to the fact that you can only specify a single Action and have to decide which one is the best choice... 您可能会屈服于这样的事实:您只能指定一个Action而必须决定哪个是最佳选择...

In this case you "could" attach another Action or ActionListener to the individual buttons which could, for example, carry out the other Action ...but you would need to ensure that the key binding Action triggered the button, using something like JButton#doClick , for example... 在这种情况下,您可以“将”另一个ActionActionListener附加到各个按钮上,例如,可以执行另一个Action ...,但是您需要确保使用JButton#doClick类的键绑定Action触发了该按钮。 JButton#doClick ,例如...

Two... 二...

Devise a "chainable" Action , which was capable of taking a list of Action s and when triggered, would trigger these actions... 设计一个“可链接的” Action ,它能够获取Action的列表,并且在触发时将触发这些动作...

public class ChainableAction extends AbstractAction {

    private List<Action> actions;

    public ChainableAction(Action... actions) {
        this.actions = new ArrayList<>(Arrays.asList(actions));
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        for (Action action : actions) {
            action.actionPerformed(e);
        }
    }

}

The above example does have one issue, it has not "configuration" properties, making it worthless to be used with things like JMenuItem . 上面的示例确实有一个问题,它没有“ configuration”属性,因此无法与JMenuItem东西一起使用。 You can provide configuration information (such as icons and text) via the constructor though. 不过,您可以通过构造函数提供配置信息(例如图标和文本)。

Three... 三...

Set up an inheritance chain, where the color Action and text Action were capable of extending from each other, in form or other, then you would simply use the final child Action for the bindings... 设置一个继承链,其中颜色Action和文本Action能够以形式或其他形式彼此扩展,那么您只需将最终的子Action用于绑定即可...

public class KeyAction extends ColorActionGreen {

    private JButton btn;
    private boolean cur;
    private int caretPosition;

    public KeyAction(JButton btn, boolean cur, int caretPosition, boolean highlight) {
        super(btn, highlight);
        this.btn = btn;
        this.cur = cur;
        this.caretPosition = caretPosition;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        super.actionPerformed(e);
        if (cur) {
            btn.getModel().setPressed(true);
            textToType.setCaretPosition(caretPosition++);
        } else {
            btn.getModel().setPressed(false);
        }
    }

}

public class ColorActionGreen extends AbstractAction {

    private JButton btn;
    private boolean highlight;

    public ColorActionGreen(JButton btn, boolean highlight) {
        this.btn = btn;
        this.highlight = highlight;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        if (highlight) {
            btn.getModel().setPressed(true);
            btn.setBackground(Color.GREEN);
            btn.setOpaque(true);
        } else {
            btn.getModel().setPressed(false);
            btn.setBackground(null);
            btn.setOpaque(false);
        }
    }

}

Then you would be able to use the KeyAction for all the bindings and even the JButton s themselves... 然后,您将能够对所有绑定甚至JButton本身使用KeyAction

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM