【问题标题】:JComboBox determine if Items are/aren't visible in drop-down listJComboBox 确定项目是否在下拉列表中可见/不可见
【发布时间】:2011-08-20 04:35:19
【问题描述】:

我尝试从 JComboBox 下拉列表中确定每个项目在 JViewPort 中是否可见

(我的星期五加班)

编辑:我不想为 System.out.print(...) 的重复事件实现 MouseListener

不可能将 JComboBox 与 JList 一起传递,由 JCombo#Model 使用 SwingUtilities http://download.oracle.com/javase/6/docs/api/javax/swing/SwingUtilities.html 声明,但这个 APi 不在我的范围内......

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class ItemVisibleRecCombo extends JFrame {

    private static final long serialVersionUID = 1L;
    private JComboBox fontsBox;

    public ItemVisibleRecCombo() {
        String[] numbers = {"one", "two", "three", "four", "five", "six", "seven"};
        fontsBox = new JComboBox(numbers);
        fontsBox.setSelectedItem(0);
        fontsBox.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent e) {
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    manItemInCombo();
                }
            }
        });
        fontsBox.setModel(new DefaultComboBoxModel(numbers));
        fontsBox.setMaximumRowCount(3);
        add(fontsBox, BorderLayout.CENTER);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setPreferredSize(new Dimension(400, 60));
        setLocation(200, 105);
        pack();
        setVisible(true);
    }

    private void manItemInCombo() {
        if (fontsBox.getItemCount() > 0) {
            final Object comp = fontsBox.getUI().getAccessibleChild(fontsBox, 0);
            if ((comp instanceof JPopupMenu)) {
                final JList list = new JList(fontsBox.getModel());
                final JPopupMenu popup = (JPopupMenu) comp;
                final JScrollPane scrollPane = (JScrollPane) popup.getComponent(0);
                final JViewport viewport = scrollPane.getViewport();
                final Rectangle rect = popup.getVisibleRect();
                Point pt = viewport.getViewPosition();
                for (int i = 0; i < list.getModel().getSize(); i++) {
                    pt = list.indexToLocation(i);
                    System.out.print(pt + " - ");
                    rect.setLocation(rect.x - pt.x, rect.y - pt.y);
                    System.out.println(new Rectangle(viewport.getExtentSize()).contains(rect));
                }
            }
        }
    }

    public static void main(String arg[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                ItemVisibleRecCombo ivrc = new ItemVisibleRecCombo();
            }
        });
    }
}

【问题讨论】:

    标签: java swing popup jcombobox viewport


    【解决方案1】:

    谢谢,但你的假设不正确,这里是输出,我仍然无法测试 ViewPort 是否正确包含 Item,没办法,我必须回到我原来的声明,因为正确显示第一个可见 Item呸呸呸

    编辑感谢@Anthony Accioly 的正确建议Changing the listener from ItemListener to ActionListener

    ItemListener 输出错误

    1stIndex = 0, LastIndex = 2, SelectedItem1 = two, Value = one, two, three//ok
    1stIndex = 0, LastIndex = 2, SelectedItem1 = three, Value = one, two, three//ok
    1stIndex = 0, LastIndex = 2, SelectedItem1 = four, Value = one, two, three//wrong
    1stIndex = 1, LastIndex = 3, SelectedItem1 = five, Value = two, three, four//wrong
    1stIndex = 2, LastIndex = 4, SelectedItem1 = six, Value = three, four, five//wrong
    1stIndex = 3, LastIndex = 5, SelectedItem1 = seven, Value = four, five, six//wrong
    1stIndex = 4, LastIndex = 6, SelectedItem1 = six, Value = five, six, seven//ok
    1stIndex = 4, LastIndex = 6, SelectedItem1 = five, Value = five, six, seven//ok
    1stIndex = 4, LastIndex = 6, SelectedItem1 = four, Value = five, six, seven//wrong
    1stIndex = 3, LastIndex = 5, SelectedItem1 = three, Value = four, five, six//wrong
    1stIndex = 2, LastIndex = 4, SelectedItem1 = two, Value = three, four, five//wrong
    1stIndex = 1, LastIndex = 3, SelectedItem1 = one, Value = two, three, four//wrong
    

    以及来自ActionListener 的预期输出

    1stIndex = 0, LastIndex = 2, SelectedItem1 = two, Value = one, two, three
    1stIndex = 0, LastIndex = 2, SelectedItem1 = three, Value = one, two, three
    1stIndex = 1, LastIndex = 3, SelectedItem1 = four, Value = two, three, four
    1stIndex = 2, LastIndex = 4, SelectedItem1 = five, Value = three, four, five
    1stIndex = 3, LastIndex = 5, SelectedItem1 = six, Value = four, five, six
    1stIndex = 4, LastIndex = 6, SelectedItem1 = seven, Value = five, six, seven
    1stIndex = 4, LastIndex = 6, SelectedItem1 = six, Value = five, six, seven
    1stIndex = 4, LastIndex = 6, SelectedItem1 = five, Value = five, six, seven
    1stIndex = 3, LastIndex = 5, SelectedItem1 = four, Value = four, five, six
    1stIndex = 2, LastIndex = 4, SelectedItem1 = three, Value = three, four, five
    1stIndex = 1, LastIndex = 3, SelectedItem1 = two, Value = two, three, four
    1stIndex = 0, LastIndex = 2, SelectedItem1 = one, Value = one, two, three
    

    修改后的代码

    import java.awt.*;
    import java.awt.event.*;
    import javax.accessibility.Accessible;
    import javax.swing.*;
    
    public class ItemVisibleRecCombo extends JFrame {
    
        private static final long serialVersionUID = 1L;
        private JComboBox fontsBox;
    
        public ItemVisibleRecCombo() {
            String[] numbers = {"one", "two", "three", "four", "five", "six", "seven"};
            fontsBox = new JComboBox(numbers);
            fontsBox.setSelectedItem(0);
            /*fontsBox.addItemListener(new ItemListener() {
    
            @Override
            public void itemStateChanged(ItemEvent e) {
            if (e.getStateChange() == ItemEvent.SELECTED) {
            manItemInCombo();
            }
            }
            });*/
            fontsBox.addActionListener(new ActionListener() {
    
                @Override
                public void actionPerformed(ActionEvent e) {
                    manItemInCombo();
                }
            });
            fontsBox.setModel(new DefaultComboBoxModel(numbers));
            fontsBox.setMaximumRowCount(3);
            add(fontsBox, BorderLayout.CENTER);
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setPreferredSize(new Dimension(400, 60));
            setLocation(200, 105);
            pack();
            setVisible(true);
        }
    
        private void manItemInCombo() {
            if (fontsBox.getItemCount() > 0) {
                final Accessible a = fontsBox.getUI().getAccessibleChild(fontsBox, 0);
                if (a instanceof javax.swing.plaf.basic.ComboPopup) {
                    final JList list = ((javax.swing.plaf.basic.ComboPopup) a).getList();
                    final Rectangle rect = list.getVisibleRect();
                    final int first = list.locationToIndex(rect.getLocation());
                    final int last = first + fontsBox.getMaximumRowCount() - 1;
                    String selectedItem = fontsBox.getSelectedItem().toString();
                    System.out.println("1stIndex = " + first + ", LastIndex = "
                            + last + ", SelectedItem1 = " + selectedItem
                            + ", Value = " + fontsBox.getItemAt(first).toString()
                            + ", " + fontsBox.getItemAt(first + 1).toString()
                            + ", " + fontsBox.getItemAt(first + 2).toString());
                }
            }
        }
    
        public static void main(String arg[]) {
            java.awt.EventQueue.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    ItemVisibleRecCombo ivrc = new ItemVisibleRecCombo();
                }
            });
        }
    }
    

    【讨论】:

    • 对我来说只是一个评论(Windows + Java SE 6),单击项目似乎给出了正确的结果,使用箭头我得到了错误的结果。 @mKorbel,你能确认一下吗?
    • 是的,这是正确的,:-) 但是直接选择某些项目并不让我感兴趣,因为 ItemListener 只是对 MouseListener 的模拟,然后如果您通过 Mouse + ScroolBar 滚动,则不会选择任何项目:-)
    • 好的。但是在什么样的事件中,您究竟想捕捉可见的行?鼠标进入?鼠标退出?鼠标移动了?
    • @Anthony Accioly,现在我不知道可能直接((JViewport) ((JScrollPane) ((BasicComboPopup) field.get(box.getUI())).getComponents()[0]).getComponents()[0]).getComponents()[0].addMouseListener(listener()); 可能是普通的香草听众fontsBox.addMouseListener blablabla MouseAdapter 但在我的 TZ 中睡过了午夜
    【解决方案2】:

    将侦听器从 ItemListener 更改为 ActionListener 似乎给出了预期的结果,无论是箭头键还是点击:

    fontsBox.addActionListener(new ActionListener() {
    
        @Override
        public void actionPerformed(ActionEvent e) {
             manItemInCombo();
        }
    });
    

    【讨论】:

      【解决方案3】:

      基本上,您正在寻找 list.locationToIndex(如果我理解正确的话),类似于

          Accessible a = fontsBox.getUI().getAccessibleChild(fontsBox, 0);
          if (a instanceof javax.swing.plaf.basic.ComboPopup) {
              JList list = ((javax.swing.plaf.basic.ComboPopup)a).getList();
              Rectangle rect = list.getVisibleRect();
              int first = list.locationToIndex(rect.getLocation());
              // similar for last, at the lower edge of the visible rect, left as exercise <g>
              // Edit: as of @Boro's comment, last is easier calculated with maxRowCount
              int last = first + fontsBox.getMaximumRowCount() - 1;
              ....
      

      顺便说一句,另一个未传递到列表中的属性:本来可以预期

         list.getVisibleRowCount() == combo.getMaximumRowCount()   
      

      回答这个问题:第一个/最后一个之间的所有项目(包括在内)都是可见的,第一个上方和最后一个下方的所有项目都不可见;-)

      【讨论】:

      • 是的,仅此线程 stackoverflow.com/questions/5896282/… 是唯一的,因为我想为派生的 JViewPort 中可见的项目使用/应用渲染器,我会检查你的...
      • +1 更好更干净的做事方式。 @kleopatra 虽然要获得最后一个,但最好只计算它而不是向列表询问您需要为其创建新对象的位置。 int rowCount = fontsBox.getMaximumRowCount(); int last = first + (rowCount-1);
      • @Boro - 不是第一个:只需移动可见矩形,无需创建(没关系,假设微优化;-) +1 用于使用 maxRowCount,将编辑:- )
      • 你是完全正确的我忘记了可见的矩形是一个副本,你可以用它做你想做的一切。谢谢你。
      【解决方案4】:

      如果要获取组合框中可见的元素,我在这里有一个算法可供您使用

                      Point pt = viewport.getViewPosition();
                      int rowCount = fontsBox.getMaximumRowCount();
                      int rowsize = viewport.getSize().height / rowCount;
                      System.out.println("viewport.getHeight()="+ viewport.getHeight()
                              +"; viewport.getViewSize().getHeight()="+ viewport.getViewSize().getHeight()
                              +"; rowsize=" + rowsize+"; pt="+pt);                
                      int firstVisibleElementIndex = pt.y/rowsize;
                      int lastVisibleElementIndex = firstVisibleElementIndex + (rowCount-1);
                      System.out.println("firstVisibleElementIndex="+ firstVisibleElementIndex
                              +"; lastVisibleElementIndex="+lastVisibleElementIndex);
      

      检查一下,它会返回第一个和最后一个可见元素,然后由你决定对它们做什么。

      编辑:这只是在给定示例之上构建的一个快速(且令人讨厌的)解决方案。如需更好的解决方案,请参阅@kleopatra 的解决方案。

      【讨论】:

      • 不,你不需要视口,列表本身会告诉你所有必要的
      • @kleopatra 我刚刚扩展了@mKorbel 提供的示例,我并不是说这是最好/唯一的方法:)
      • 不要重复错误/过分复杂的事情,它们容易留在记忆中;-)
      • @kleopatra 你是完全正确的。 :) 实施我的解决方案我不考虑哪种是“更清洁”的方法,我只是重用了代码。
      猜你喜欢
      • 1970-01-01
      • 2011-10-08
      • 1970-01-01
      • 2015-03-03
      • 2011-06-24
      • 1970-01-01
      • 1970-01-01
      • 2012-06-30
      • 2014-08-19
      相关资源
      最近更新 更多