2014-09-06 14 views
6

JavaDoc per ListChangeListener fornisce un modello per la gestione delle modifiche. Non so come gestire le permutazioni, comunque. Per ogni indice, posso scoprire dove è il nuovo indice dell'articolo, ma non so cosa fare con esso. Questo è un po 'un puzzle che è indipendente dal linguaggio di programmazione. Una ObservableList può solo aggiungere(), remove(), set() e ha anche un iteratore.ListChangeListener wasPermutated block

Se ho un elenco originale [1,2,3] e associa un elenco [] ad esso, l'elenco associato [1,2,3] deve corrispondere. Se l'elenco originale ottiene il suo comparatore scambiato in modo che l'elenco originale ora legga [3,2,1], come faccio a seguire l'elenco dei limiti?

/** 
* Binds a source list's elements to a destination list. Any changes made in 
* the source list will reflect in the destination list. 
* 
* @param <SRC> The source list's object type. 
* @param <DEST> The destination list's object type. 
* @param dest The destination list that will be bound to the src list. 
* @param src The source list to watch for changes, and propagate up to the 
* destination list. 
* @param transformer A function that will transform a source list data 
* type, A, into a destination list data type, B. 
*/ 
public static <SRC, DEST> void bindLists(
     ObservableList<DEST> dest, ObservableList<SRC> src, Function<? super SRC, ? extends DEST> transformer) { 
    /*Add the initial data into the destination list.*/ 
    for (SRC a : src) { 
     dest.add(transformer.apply(a)); 
    } 
    /*Watch for future data to add to the destination list. Also watch for removal 
    of data form the source list to remove its respective item in the destination 
    list.*/ 
    src.addListener((ListChangeListener.Change<? extends SRC> c) -> { 
     while (c.next()) { 
      if (c.wasPermutated()) { 
       /*How do you handle permutations? Do you remove and then add, 
       or add and then remove, or use set, or use a copy arraylist 
       and set the right indices? Removing/adding causes concurrent modifications.*/ 
       for (int oldIndex = c.getFrom(); oldIndex < c.getTo(); oldIndex++) { 
        int newIndex = c.getPermutation(oldIndex); 
        dest.remove(oldIndex); 
        dest.add(newIndex, dest.get(oldIndex)); 
       } 
      } else if (c.wasUpdated()) { 

      } else { 
       /*Respond to removed data.*/ 
       for (SRC item : c.getRemoved()) { 
        int from = c.getFrom(); 
        dest.remove(from); 
       } 
       /*Respond to added data.*/ 
       for (SRC item : c.getAddedSubList()) { 
        int indexAdded = src.indexOf(item); 
        dest.add(indexAdded, transformer.apply(item)); 
       } 
      } 
     } 
    }); 
} 
+0

Mi piacerebbe anche sapere come wasUpdated() anche viene attivato. Gli oggetti di ObservableList devono implementare Observable o qualcosa del genere? –

risposta

6

Per il caso permutazione, non vorrei perdere tempo cercando di utilizzare add() e remove() fare gestirlo. Ciò farà sì che gli indici si spostino e renderanno le cose confuse (almeno per me).

Concettualmente ciò che si ottiene è un intervallo di elementi interessati e un array contenente alcuni numeri che indicano dove è stato spostato ciascun elemento. Penso che tu capisca molto. Nel codice che hai,

newIndex = getPermutation(oldIndex); 

il che significa che l'elemento era a oldIndex deve essere spostato a newIndex. La ruga è che se fai semplicemente la mossa direttamente, potresti sovrascrivere un elemento che non è stato ancora spostato. Penso che il modo più semplice per affrontarlo sia quello di creare una copia del subrange interessato e quindi semplicemente passare attraverso l'array di permutazione e spostare gli elementi dalla copia nelle loro nuove posizioni. Il codice per farlo è:

int from = c.getFrom(); 
    int to = c.getTo(); 
    List<DEST> copy = new ArrayList<>(dest.subList(from, to)); 
    for (int oldIndex = from; oldIndex < to; oldIndex++) { 
     int newIndex = c.getPermutation(oldIndex); 
     dest.set(newIndex, copy.get(oldIndex - from)); 
    } 

Questa è una permutazione, quindi ogni elemento finisce da qualche parte e nessuno viene aggiunto o eliminato. Ciò implica che non è necessario copiare l'intervallo di elenchi e che è possibile spostare gli elementi uno alla volta seguendo la catena di mosse mentre si utilizza solo un singolo elemento di spazio temporaneo. Potrebbero esserci più cicli di catene, quindi dovresti rilevare e gestire anche questo. Sembra piuttosto complesso. Lo lascerò per un altro rispondente. :-) Per i miei soldi, copiare la gamma interessata è semplice e facile da capire.

Le modalità di modifica permutazione e aggiornamento non vengono attivate dalle normali azioni di elenco. Se si guarda javafx.collections.ObservableListBase, è possibile visualizzare un protocollo che può essere utilizzato dall'implementazione di un elenco per creare informazioni su una modifica specifica. Se l'implementazione fornisce le giuste informazioni ai metodi nextPermutation o nextUpdate, attiverà queste altre modalità di modifica. Non sono sicuro di cosa potrebbe attivarli in JavaFX. Ad esempio, i metodi Node.toFront() e Node.toBack() per modificare l'ordine di impilamento dei nodi potenzialmente potrebbero generare modifiche di permutazione, ma non sembrano. Neanche io so nulla che possa generare un cambiamento di aggiornamento.

Semanticamente, penso che un cambiamento di aggiornamento implichi che gli elementi in quell'intervallo della lista sono cambiati ma che la lunghezza della lista rimane la stessa. Ciò è in contrasto con la modalità di modifica "sostituita", in cui un intervallo di elementi potrebbe essere sostituito con un numero diverso di elementi. Potrebbe anche darsi che la modifica dell'aggiornamento significhi che gli elementi stessi non sono stati sostituiti, ovvero che il contenuto della lista non è cambiato, ma semplicemente che gli stati interni degli elementi sono cambiati.

+2

In generale, preferirei questo approccio. Basta notare che se la lista che stai manipolando è (o potrebbe essere) un elenco di 'Node' child in un 'Pane', allora questo si interromperà dato che ci saranno nodi duplicati nella lista mentre stai elaborando il ciclo. In questo caso, puoi applicare la permutazione alla copia con 'copy.set (newIndex - from, dest.get (oldIndex));' all'interno del ciclo. Poi, quando hai finito, sostituisci gli elementi di 'dest' con' dest.subList (da, a) .clear(); 'e' dest.addAll (from, copy); '. –

+1

@James_D Buon punto per evitare le voci duplicate 'Node' se' dest' si trova nel grafico di scena. –

2

Si noti che ciò che si sta facendo qui è implementato in EasyBind. Vedere come map e bind un elenco.

In sostanza, questo è il modo per implementare il metodo di bindLists:

public static <SRC, DEST> void bindLists(
     ObservableList<DEST> dest, ObservableList<SRC> src, 
     Function<? super SRC, ? extends DEST> transformer) { 

    EasyBind.listBind(dest, EasyBind.map(src, transformer)); 
} 
+0

Ricordo di aver deciso di non usare la tua classe per qualche motivo qualche mese fa, ma non riesco a ricordare cosa. Suppongo che a questo punto posso tornare a utilizzarlo però. :) –

+0

Inoltre, la domanda fuori tema, ma qual è la differenza tra un jar Javadoc e un jar sorgente? Non è già la fonte EasyBind.jar? Perché devo collegare una fonte ad esso? –

+0

Per utilizzare EasyBind, è necessario solo 'easybind- .jar'. Contiene solo classi compilate. Il vaso di origine contiene solo il codice sorgente. È utile collegare il vaso di origine nel tuo IDE in modo che possa mostrarti i commenti Javadoc e/o il codice sorgente, ad esempio quando esegui il debug e passi attraverso il codice. –