Domanda

Io sto facendo una custom ListCellRenderer.So che si può avere dimensioni diverse per ogni singola cella.Ma ora voglio avere una dimensione diversa per la cella selezionata.In qualche modo, la JList è la cache di dimensione per ogni cella, il primo tempo si ha a calcolare i limiti per ogni cella.Questo è il mio codice:

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

Nei commenti potete vedere quello che ho già provato.

Ho già cercato un po ' lungo e trovato un sacco di inutili articoli, alcuni di loro toccare il ListCellRenderer/altezza dinamica cosa, ma funzionano solo perché l'altezza rimane la stessa per le singole celle.Il mio heights stanno cambiando, così come faccio a fare questo?

È stato utile?

Soluzione 5

Grazie a Rastislav Komara sono stato in grado di risolvere questo abbastanza facilmente:

Ho creato una classe interna che si estende BasicListUI e metodo pubblico creato che viene chiamato il ListSelectionListener.valueChanged:

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

Il metodo updateLayoutState è normalmente attivato quando i cambiamenti di altezza JList. L'unica cosa "folle" che sto facendo qui è che il mio renderer ha bisogno di sapere che cosa l'indice selezionato è. Questo perché il metodo updateLayoutState non utilizza l'indice selezionato nella sua calcoli dell'altezza. In qualche modo usando list.getSelectedIndex () all'interno getListCellRendererComponent non funziona bene.

Modifica
Controllare anche l'anser da nevster e Kleopatra, guardano modo più intelligente, provare prima ...

Altri suggerimenti

Fondamentalmente, ci sono due aspetti del problema, entrambi situati nell'interfaccia utente delegato

  • non riesce a configurare il renderer per la sua reale stato durante la misurazione, che ignora la selezione (e focus) completamente
  • è notoriamente testardo contro l'essere costretto a ri-calcolare la cache cella di misura:non ha api pubblica per farlo e non solo volontariamente modifiche al modello.

Il rimedio per risolvere il primo è, infatti, il motore di rendering:implementare ignorare il dato selezionato di bandiera e di eseguire una query nell'elenco per la selezione reale, come indicato da @Andy.Nel codice, utilizzando l'OP componenti

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

Per risolvere il secondo, di un'interfaccia utente personalizzata delegato (come suggerito in altre risposte) è una possibile soluzione.Anche se alcuni lavori nel caso generale, se il supporto di più LAFs è necessario.

Meno invadenti, ma un po ' sporca metodo per forzare l'interfaccia utente in volontariamente aggiornare la cache è quello di inviare un falso ListDataEvent su 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 del SwingX il progetto ha un'interfaccia utente personalizzata delegato - principalmente per il supporto di ordinamento/filtro - con api pubblica per calcolare la cache, quindi sopra ListSelectionListener sarebbe più semplice (e pulito :-) a

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

Ho appena implementato questa funzione. Il problema è, che il renderer di celle viene chiesto due volte per il rendering di una cella. Nel primo turno tutte le voci dell'elenco sono resi senza selezione, quindi le celle selezionate vengono resi nuovamente usando selezione. Quindi, se si fornisce un dimensione preferita al primo turno, si è memorizzato nella cache e utilizzato anche per il secondo turno.

Il trucco è ignorare il parametro isSelected booleano nella getListCellRendererComponent e di capire lo stato di selezione verificando se list.getSelectedIndices() contiene il dato indice.

Ma, ho ancora il problema, che, dopo l'elenco è reso visibile, l'altezza dei componenti resi a volte sono di grande / piccolo. Dopo aver ridimensionato la lista con il mouse tutto è ancora benissimo. Ho suonato in giro con validate / revalidate, ridisegnare, ripristino delle altezze nella cache, ma niente ha funzionato. Swing è a volte un po 'strano ...

Il JList ha alcuna possibilità di modificare le dimensioni delle celle a seconda selezione o qualsiasi altra cosa. L'uso lista "Copia cache" dimensioni. Se non v'è nuovo cellRenderer a condizione che questa dimensione sono raccontati e applicati all'interno di tutte le cellule in lista. Penso che il motivo è la prestazione per la lista con un sacco di voci. La soluzione possibile è quella di scrivere una propria implementazione ListUI che è in grado di utilizzare diversi formati per le celle selezionate e non selezionate. Questo porta anche la possibilità di regolare le dimensioni delle celle intorno selezione logaritmo o altro interpolazione. Spero che tu abbia un grande motivo per cui per fare questo. Si tratta di un sacco di lavoro!

Sono stato strappando i capelli di questo stupido problema altezza delle righe JList. Ho un renderer di celle che regola un'altezza di riga variabile per ogni riga - problema è che JList mantiene una cache delle altezze

.

Utilizzando le altre risposte, credo di aver colpito il Santo Graal. Eccolo:

Utilizzare una versione semplificata del BasicListUI come creato da Jaap:

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

Poi, quando si crea un JList - estendere in questo modo:

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

Potrebbe essere necessario mettere una guardia intorno al triggerUpdate durante la creazione a seconda delle circostanze.

Il JList è probabilmente "caching" renderer di celle. Provate a collegare un ListSelectionListener, e impostare di nuovo il renderer quando la selezione viene modificata.

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

questa è una soluzione semplice:

public class VariableHeightListUI extends BasicListUI {

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

naturalmente è necessario scrivere il proprio implementazione di ListCellRenderer, e in base alle diverse stato di selezione di elemento della lista, è possibile impostare diverse altezze del componente tornato preferiscono.

Un solo problema ha bisogno di andare avanti è: quando si seleziona un elemento della lista prima volta, non disegnare correttamente. ma dopo allora, tutto funziona bene.

Spero che questo ti può aiutare.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top