获取 Swing 组件相对于屏幕的位置

Getting the position of a Swing Component relative to the screen

我有一个名为 SuggestionField 的 class,它将向用户显示一个项目列表以自动完成他的输入。他让输入显示在 JDialog(suggestionFrame) 中。我需要将 JDialogs 位置设置在其父级下方,但我无法获得相对于屏幕的父级 (JTextField) X 和 Y。

问题示例

import javax.swing.*;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.Arrays;

public class Runner {
    public static void main(String[] args) {

        JFrame frame = new JFrame();
        String[] items = new String[]{"Tiger", "Wolf", "Car", "Cat", "Space", "Sing", "Scene"};
        SuggestionField suggestionField = new SuggestionField(new ArrayList<>(Arrays.asList(items)), frame);

        frame.setDefaultCloseOperation(3);
        frame.setSize(100, 65);
        frame.setLocationRelativeTo(null);

        JPanel panel = new JPanel();

        panel.setLayout(new GridLayout(1,1));
        panel.add(suggestionField);

        frame.add(panel);

        frame.setVisible(true);

    }


    public static class SuggestionField extends JTextField implements DocumentListener, KeyListener {

        String[] values = new String[0];
        ArrayList<String> displayValues = new ArrayList<>(0);
        JDialog suggestionFrame;
        JPanel suggestionPanel = new JPanel();
        Color backGround = new Color(109, 104, 104, 133);
        Color selectBackGround = new Color(109, 104, 104, 133);
        Color textColor = new Color(5, 19, 88, 255);
        Color selectTextColor = new Color(115, 134, 238, 255);
        BoxLayout bl = new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS);
        int selectedEntry = 0;

        public SuggestionField(ArrayList<String> values, JFrame parentDisplay) {
            this.getDocument().addDocumentListener(this);
            this.addKeyListener(this);
            suggestionFrame = new JDialog(parentDisplay);
            suggestionFrame.add(suggestionPanel);
            suggestionFrame.setUndecorated(true);
            suggestionFrame.setAlwaysOnTop(true);
            suggestionFrame.setFocusable(false);
            suggestionPanel.setFocusable(false);
            bl.maximumLayoutSize(suggestionPanel);
            this.values = values.toArray(this.values).clone();
        }

        public boolean updateSuggestions() {
            boolean added = false;
            boolean add;
            displayValues.clear();
            for (int i = 0;i < values.length;i++) {
                add = true;
                for (int k = 0;k < this.getText().length() && k < values[i].length();k++) {
                    if (values[i].toUpperCase().charAt(k) != this.getText().toUpperCase().charAt(k)) {
                        add = false;
                        break;
                    }
                }
                if (add && !values[i].equalsIgnoreCase(this.getText()) && values[i].length() > this.getText().length()) {
                    added = true;
                    displayValues.add(values[i]);
                }
            }
            return added;
        }

        private void updateDisplay() {

            suggestionFrame.setSize(this.getWidth(), 16 * displayValues.size());
            suggestionPanel.removeAll();
            suggestionPanel.setLayout(new BoxLayout(suggestionPanel, BoxLayout.Y_AXIS));
            for (int i = 0;i < displayValues.size();i++) {
                JLabel a = new JLabel(displayValues.get(i));
                if (i == selectedEntry) {
                    a.setBackground(selectBackGround);
                    a.setForeground(selectTextColor);
                } else {
                    a.setBackground(backGround);
                    a.setForeground(textColor);
                }
                suggestionPanel.add(a);
            }
            suggestionPanel.revalidate();
            suggestionFrame.revalidate();
            suggestionFrame.setLocation(this.getX(), this.getY() + this.getHeight());
            suggestionFrame.setVisible(true);
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            if (updateSuggestions()) {
                updateDisplay();
            } else suggestionFrame.setVisible(false);
            selectedEntry = 0;
            this.requestFocus();
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            if (updateSuggestions()) {
                updateDisplay();
            } else suggestionFrame.setVisible(false);
            selectedEntry = 0;
            this.requestFocus();
        }

        @Override
        public void changedUpdate(DocumentEvent e) {

        }

        @Override
        public void keyTyped(KeyEvent e) {

        }

        @Override
        public void keyPressed(KeyEvent e) {
            int ID = e.getKeyCode();
            switch (ID) {

                case 40:
                    if (selectedEntry < displayValues.size() - 1) selectedEntry++;
                    updateDisplay();
                    break;

                case 38:
                    if (selectedEntry > 0) selectedEntry--;
                    updateDisplay();
                    break;

                case 10:
                    this.setText(displayValues.get(selectedEntry));
                    break;

                case 27:
                    suggestionFrame.setVisible(false);
                    break;

            }
            this.requestFocus();
            this.grabFocus();
        }

        @Override
        public void keyReleased(KeyEvent e) {

        }

    }
}

有什么方法可以在 updateDisplay()(第 97 行)方法中获得 suggestionFrame

JTextField 的绝对位置

您可以使用 SwingUtilities.convertPointToScreen 来执行此操作。

只需传递 (x, y+height) 和您的输入字段即可获得显示下拉列表的起点。

    JButton component = new JButton();
    Point pt = new Point(component.getLocation());
    pt.y += component.getHeight();
    SwingUtilities.convertPointToScreen(pt, component);

    // pt is now in screen coords... so you can use it to position the pop-up

完整的文档可以在这里找到:https://docs.oracle.com/javase/7/docs/api/javax/swing/SwingUtilities.html

I need to set the JDialogs position to be below its parent..

使用Window.setLocationRelativeTo(Component).

This will just place the JDialog ontop of the JTextFiled

实际上,它比那更微妙。例如,它会考虑对话框是否会离开屏幕,然后将其移回屏幕。如果基于 'location on screen'.

的位置,那是需要明确处理的事情