Search code examples
javajscrollpane

JScrollpane on my custom made component is not updating


I am trying to update the scrollbar on the scrollpane automatically when going up and down in my custom made popupmenu. But it doesn´t work. Have a look at my fully runnable example below where I have added my custom JPopupMenu based component and a JComboBox to compare with. As you can see, in the JComboBox the scrollpane updates its position so the currently selected item always is visible. But that does not work with my component and I don´t know how to set that.

My SSCCE:

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.ScrollPane;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Vector;

import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JList;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;


public class ScrollFocus extends JFrame {

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new ScrollFocus();

            }
        });
    }

    public ScrollFocus() {
        this.setLayout(new BorderLayout());
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Vector<String> values = new Vector<>();
        values.add("a");
        values.add("b");
        values.add("c");
        values.add("d");
        values.add("e");
        values.add("f");
        values.add("g");
        values.add("h");
        values.add("i");
        values.add("j");
        JComboBox<String> comboBox = new JComboBox<>(values);
        JScrollPane scrollPane = new JScrollPane(comboBox);
        this.add(scrollPane, BorderLayout.NORTH);

        CustomTextField customTextField = new CustomTextField();
        this.add(customTextField, BorderLayout.CENTER);
        pack();
        setVisible(true);
    }

    class CustomTextField extends JTextField implements FocusListener {

        private CustomPopup customPopup = new CustomPopup();

        public CustomTextField() {
            this.addFocusListener(this);
            this.addKeyListener(new KeyAdapter() {
                @Override
                public void keyPressed(KeyEvent e) {
                    System.out.println("printing...");
                    setPopupSize();
                    setPosition();
                    customPopup.setVisible(true);

                    if(e.getKeyCode() == KeyEvent.VK_UP) {
                        if(customPopup.index < customPopup.getListSize())
                            customPopup.setSelectedIndex(customPopup.index--);
                    }
                    else if(e.getKeyCode() == KeyEvent.VK_DOWN){
                        if(customPopup.index >= 0)
                            customPopup.setSelectedIndex(customPopup.index++);
                    }
                }
            });
        }

        public void setPopupSize() {
            customPopup.setPopupSize(new Dimension(this.getWidth(), 110));
        }

        public void setPosition() {
            customPopup.setLocation(this.getLocation().x, this.getLocation().y);
        }

        @Override
        public void focusGained(FocusEvent e) {

        }

        @Override
        public void focusLost(FocusEvent e) {
            customPopup.setVisible(false);
        }


        class CustomPopup extends JPopupMenu {
            String[] values = new String[]{"Value1", "Value2", "Value3", "Value4", "Value5", "Value6", "Value7",
                    "Value8","Value9", "Value10", "Value11", "Value12", "Value13", "Value14", "Value15", "Value16",};
            JList<String> list = new JList<>(values);
            JScrollPane scrollPane = new JScrollPane(list);
            public int index = 0;

            public CustomPopup() {
                this.setLayout(new GridLayout(0,1));
                this.add(scrollPane);
                pack();
            }

            public void setSelectedIndex(int index) {
                list.setSelectedIndex(index);
            }

            public int getListSize() {
                return values.length;
            }
        }
    }
}

Solution

  • in the JComboBox the scrollpane updates its position so the currently selected item always is visible. But that does not work with my component and I don´t know how to set that.

    list.setSelectedIndex(index);
    list.ensureIndexIsVisible(index); // added
    

    Also, do not use a KeyListener. Swing was designed to be used with Key Bindings. Read thw section from the Swing tutorial on How to Use Key Bindings for more information.