Frage

Ich mache einen benutzerdefinierten ListCellRenderer. Ich weiß, dass Sie für jede einzelne Zelle unterschiedliche Dimensionen haben können. Aber jetzt möchte ich eine andere Dimension für die ausgewählte Zelle haben. Irgendwie schneidet die JList die Dimension für jede einzelne Zelle, wenn sie zum ersten Mal Grenzen für jede Zelle berechnen muss. Dies ist mein Code:

public class Test {

    static class Oh extends JPanel {

        public Oh() {
            setPreferredSize(new Dimension(100, 20));
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            g.setColor(Color.WHITE);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    static class Yeah extends JPanel {
        private boolean isSelected;

        public Yeah(boolean isSelected) {
            setPreferredSize(new Dimension(100, 100));
            this.isSelected = isSelected;
        }

        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            //setSize(100, 100); // doesn't change the bounds of the component
            //setBounds(0, 0, 100, 100); // this doesn't do any good either.
            if (isSelected) g.setColor(Color.GREEN);
            else g.setColor(Color.BLACK);
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        f.setSize(800, 500);
        Vector<Integer> ints = new Vector<Integer>();
        for (int i = 0; i < 100; i++) {
            ints.add(i);
        }
        JList list = new JList(ints);
        list.setCellRenderer(new ListCellRenderer() {
            public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
                if (isSelected || ((Integer) value) == 42) return new Yeah(isSelected);
                else return new Oh();
            }
        });
        //list.setPrototypeCellValue(null);
        //list.setFixedCellHeight(-1);
        f.add(new JScrollPane(list));
        f.setVisible(true);
    }
}

In den Kommentaren können Sie sehen, was ich bereits ausprobiert habe.

Ich habe bereits ziemlich lange gesucht und viele nutzlose Artikel gefunden. Einige berühren den ListCellRenderer/Dynamic Heighing -Ding, aber sie funktionieren nur, weil die Höhe für die einzelnen Zellen gleich bleibt. Meine Höhen ändern sich, also wie mache ich das?

War es hilfreich?

Lösung 5

Dank Rastislav Komara konnte ich dies ganz leicht lösen:

Ich habe eine innere Klasse erstellt, die BasicListui erweitert und öffentliche Methoden erstellt hat, die auf ListSelectionListener.ValuEchanged aufgerufen wird:

private class MyRenderer implements ListCellRenderer {
    public int listSelectedIndex = -1;

    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
            boolean cellHasFocus) {
        if (index == listSelectedIndex)
            return new Yeah(isSelected);
        else
            return new Oh();
    }
}
MyRenderer lcr = new MyRenderer();
private class MyListUI extends BasicListUI {

    public void triggerUpdate() {
        lcr.listSelectedIndex = list.getSelectedIndex();
        updateLayoutState();
        list.revalidate();
    }
}

Die UpdatElayoutState -Methode wird normalerweise ausgelöst, wenn sich die JList -Höhe ändert. Das einzige "verrückte" Ding, das ich hier mache, ist, dass mein Renderer wissen muss, was der ausgewählte Index ist. Dies liegt daran, dass die UpdateLayoutState -Methode den ausgewählten Index in den Höhenberechnungen nicht verwendet. Irgendwie funktioniert list.getSelectedIndex () in GetListCellRendererComponent nicht gut.

Bearbeiten:
Überprüfen Sie auch den Anser von Nevster und Kleopatra, sie sehen intelligenter aus, probieren Sie sie zuerst aus ...

Andere Tipps

Grundsätzlich gibt es zwei Aspekte des Problems, beide im UI -Delegierten

  • Es konfiguriert den Renderer nicht zu seinem real Zustand beim Messen ignoriert die Auswahl (und den Fokus) vollständig
  • Es ist notorisch hartnäckig dagegen, die zwischengespeicherten Zellgrößen neu zu berechnen: Es hat keine öffentliche API, um dies zu tun, und nur freiwillig für Modelländerungen.

Das Mittel zur Behebung des ersten ist in der Tat der Renderer: Implementieren Sie das angegebene ausgewählte Flag und die Liste für die reale Auswahl, wie von @Andy beschrieben. In Code verwenden die Komponenten des OP

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);

Um die zweite zu beheben, ist ein benutzerdefinierter UI -Delegierter (wie auch in anderen Antworten vorgeschlagen) eine mögliche Lösung. Obwohl einige Arbeiten im allgemeinen Fall arbeiten, ist die Unterstützung mehrerer LAFS erforderlich.

Eine weniger aufdringliche, aber leicht schmutzige Methode, um die Benutzeroberfläche dazu zu zwingen, seinen Cache freiwillig zu aktualisieren, besteht darin, eine gefälschte ListDataEvent auf SelectionChange zu senden:

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);

Übrigens, jxlist der Swingx Das Projekt verfügt über einen benutzerdefinierten UI -Delegierten - hauptsächlich für die Unterstützung von Sortier-/Filterung - mit öffentlicher API, um den Cache neu zu verkalken, und dann würde der oben genannte Listent surectionListener vereinfacht (und sauber :-) zu

    ListSelectionListener l = new ListSelectionListener() {
        @Override
        public void valueChanged(ListSelectionEvent e) {
            ((JXList) e.getSource()).invalidateCellSizeCache();
        }
    };
    list.addListSelectionListener(l);

Ich habe gerade diese Funktion implementiert. Das Problem ist, dass der Zellrenderer zweimal gebeten wird, eine Zelle zu rendern. In der ersten Runde werden alle Listeneinträge ohne Auswahl gerendert, dann werden die ausgewählten Zellen erneut unter Verwendung der Auswahl gerendert. Wenn Sie also in der ersten Runde eine bevorzugte Größe angeben, wird sie zwischengespeichert und auch für die zweite Runde verwendet.

Der Trick besteht darin, die zu ignorieren isSelected boolescher Parameter in der getListCellRendererComponent und um den Auswahlzustand herauszufinden, indem Sie prüfen, ob list.getSelectedIndices() Enthält den angegebenen Index.

Aber ich habe immer noch das Problem, dass nach der sichtbaren Liste die Höhe der gerenderten Komponenten manchmal zu groß/klein ist. Nach der Größenänderung der Liste durch Maus ist alles wieder in Ordnung. Ich spielte mit Validierung/Neubewertung, Wiederholung, Zurücksetzen von zwischengespeicherten Höhen, aber nichts hat funktioniert. Swing ist manchmal ein bisschen seltsam ...

Die JList hat keine Fähigkeit, die Größe der Zelle je nach Auswahl oder was auch immer zu ändern. Die Liste verwendet "zwischengespeicherte" Größen. Wenn neue CellRenderer vorhanden sind, werden diese Größen erzählt und in allen Zellen in der Liste angewendet. Ich denke, der Grund ist die Leistung für die Liste mit vielen Einträgen. Die mögliche Lösung besteht darin, eine eigene ListUi -Implementierung zu schreiben, die verschiedene Größen für ausgewählte und nicht ausgewählte Zellen verwenden kann. Dies bringt auch die Möglichkeit, die Größe der Zellen um die Selektion durch Logarithmus oder andere Interpolation anzupassen. Ich hoffe, Sie haben einen großen Grund, warum Sie dies tun können. Es ist viel Arbeit!

Ich habe mir die Haare über dieses dumme Problem der JList Row Height ausgerissen. Ich habe einen Zell -Renderer, der für jede Zeile eine variable Zeilenhöhe festlegt - das Problem ist, dass JList einen Cache der Höhen behält.

Mit den anderen Antworten habe ich den Heiligen Gral geschlagen. Hier ist es:

Verwenden Sie eine vereinfachte Version des BasicListui, wie von JAAP erstellt:

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

Wenn Sie dann eine JList erstellen, erweitern Sie sie so:

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);

Möglicherweise müssen Sie je nach den Umständen eine Wache um den Abzugsumfang während der Erstellung setzen.

Die JList "zwischengespeichert" Ihren Zellenrenderer. Versuchen Sie, eine Listentwicklunglistener beizuhängen, und legen Sie den Renderer erneut fest, wenn die Auswahl geändert wird.

...
addListSelectionListener(new ListSelectionListener() {  
  public void valueChanged(ListSelectionEvent event) { 
    if(event.getValueIsAdjusting() == false) {
      list.setCellRenderer(new MyRenderer());
    }
  }
)
...

Dies ist eine einfache Lösung:

public class VariableHeightListUI extends BasicListUI {

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

Natürlich müssen Sie Ihre eigene Implementierung von schreiben ListCellRenderer, und nach unterschiedlichem Auswahlzustand des Listenelements können Sie eine andere bevorzugte Höhe der zurückgegebenen Komponente festlegen.

Nur ein Problem muss antreten, ist: Wenn Sie zum ersten Mal ein Element der Liste auswählen, zeichnen Sie nicht korrekt. Aber danach funktionieren alle gut.

Hoffe das kann dir helfen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top