Java Swing: JList with ListCellRenderer selected item different height

前端 未结 7 1595
花落未央
花落未央 2020-12-17 05:27

I\'m making a custom ListCellRenderer. I know that you can have different dimensions for each individual cell. But now I want to have a different dimension for the selected

相关标签:
7条回答
  • 2020-12-17 05:36

    I just implemented this feature. The problem is, that the cell renderer is asked twice for rendering a cell. In the first round all list entries are rendered without selection, then the selected cells are rendered again using selection. So if you provide a preferred size in the first round, it is cached and also used for the second round.

    The trick is to ignore the isSelected boolean parameter in the getListCellRendererComponent and to figure out the selection state by checking if list.getSelectedIndices() contains the given index.

    But, I still have the problem, that after the list is made visible, the height of the rendered components are sometimes to large/small. After resizing the list by mouse everything is fine again. I played around with validate/revalidate, repaint, reset of cached heights, but nothing worked. Swing is sometimes a bit strange...

    0 讨论(0)
  • 2020-12-17 05:37

    The JList is probably "caching" your cell renderer. Try to attach a ListSelectionListener, and set the renderer again when selection is changed.

    ...
    addListSelectionListener(new ListSelectionListener() {  
      public void valueChanged(ListSelectionEvent event) { 
        if(event.getValueIsAdjusting() == false) {
          list.setCellRenderer(new MyRenderer());
        }
      }
    )
    ...
    
    0 讨论(0)
  • 2020-12-17 05:43

    I've been tearing my hair out about this stupid JList row height problem. I have a cell renderer which sets a variable row height for every row - problem is that JList keeps a cache of the heights.

    Using the other answers, I think I've struck on the holy grail. Here it is:

    Use a simplified version of the BasicListUI as created by Jaap:

    public class BetterListUI extends BasicListUI {
        public void triggerUpdate() {
            updateLayoutState();
        }
    }
    

    Then when you create a JList - extend it like this :

    betterListUI = new BetterListUI();
    myJList = new JList() {
        @Override
        public void repaint(long tm, int x, int y, int width, int height) {
            betterListUI.triggerUpdate();
            super.repaint(tm, x, y, width, height);
        }
    };
    myJList.setUI(betterListUI);
    

    You may need to put a guard around the triggerUpdate during creation depending on your circumstances.

    0 讨论(0)
  • 2020-12-17 05:47

    Basically, there are two aspects of the problem, both located in the ui delegate

    • it fails to configure the renderer to its real state when measuring, that is ignores the selection (and focus) completely
    • it is notoriously stubborn against being forced to re-calculate the cached cell sizes: it has no public api to do so and only does voluntarily on model changes.

    The remedy to fix the first is indeed the renderer: implement to ignore the given selected flag and query the list for the real selection, as outlined by @Andy. In code, using the OP's components

    ListCellRenderer renderer = new ListCellRenderer() {
        Yeah yeah = new Yeah(false);
        Oh oh = new Oh();
    
        @Override
        public Component getListCellRendererComponent(JList list,
                Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            // ignore the given selection index, query the list instead
            if (list != null) {
                isSelected = list.isSelectedIndex(index);
            }
            if (isSelected || ((Integer) value) == 42) {
                yeah.isSelected = isSelected;
                return yeah;
    
            }
            return oh;
        }
    };
    list.setCellRenderer(renderer);
    

    To fix the second, a custom ui delegate (as suggested in others answers as well) is a possible solution. Though some work in the general case, if supporting multiple LAFs is needed.

    A less intrusive but slightly dirty method to force the ui into voluntarily update its cache is to send a fake ListDataEvent on selectionChange:

    ListSelectionListener l = new ListSelectionListener() {
        ListDataEvent fake = new ListDataEvent(list, ListDataEvent.CONTENTS_CHANGED, -1, -1);
        @Override
        public void valueChanged(ListSelectionEvent e) {
            JList list = (JList) e.getSource();
            ListDataListener[] listeners = ((AbstractListModel) list.getModel())
                    .getListDataListeners();
            for (ListDataListener l : listeners) {
                if (l.getClass().getName().contains("ListUI")) {
                    l.contentsChanged(fake);
                    break;
                }
            }
        }
    };
    list.addListSelectionListener(l);
    

    BTW, JXList of the SwingX project has a custom ui delegate - mainly for supporting sorting/filtering - with public api to re-calculate the cache, then the above ListSelectionListener would be simplified (and clean :-) to

        ListSelectionListener l = new ListSelectionListener() {
            @Override
            public void valueChanged(ListSelectionEvent e) {
                ((JXList) e.getSource()).invalidateCellSizeCache();
            }
        };
        list.addListSelectionListener(l);
    
    0 讨论(0)
  • 2020-12-17 05:49

    this is a simple solution:

    public class VariableHeightListUI extends BasicListUI {
    
      @Override
      public void paint(Graphics g, JComponent c) {
        updateLayoutState();
        super.paint(g, c); 
      }
    }
    

    of course you need write your own implementation of ListCellRenderer, and according to different selection state of list element, you can set different prefer height of returned Component.

    Only one issue need to go on is : when you select an element of List FIRST time, not draw correctly. but after then, all work well.

    hope this can help you.

    0 讨论(0)
  • 2020-12-17 05:52

    The JList has no ability to change size of cell depending on selection or whatever. The list use "cached" sizes. If there is new cellRenderer provided this sizes are recounted and applied within all cells in list. I think the reason is performance for list with a lot of entries. The possible solution is to write own ListUI implementation which is able to use different sizes for selected and unselected cells. This brings also possibility to adjust size of cells around selection by logarithm or other interpolation. I hope you have a big reason why to do this. It is a lot of work!

    0 讨论(0)
提交回复
热议问题