2016-05-19 8 views
1

JavaFX TabPane hat ein sehr seltsames Verhalten, wenn Sie versuchen, die Registerkarten zu sortieren, wenn nicht genügend Platz vorhanden ist, um alle Registerkarten anzuzeigen.JavaFX TabPane Sortierregister erstellt Chaos

Genauer gesagt, die Registerkarte Auswahl-Schaltfläche (runde Schaltfläche mit einem nach unten weisenden Pfeil auf der rechten Seite des Registerkartenkopfs), die eine Dropdown-Liste mit allen Registerkarten zeigen sollte zeigt nichts.

Ich habe einen kleinen Test erstellt, um das Problem zu reproduzieren. Klicken Sie einfach mehrere Male (bis nicht genug Platz für alle Registerkarten vorhanden ist) auf "Add new tab & sort" (oder mehrmals auf "Add new tab" und dann "Sort tabs"), und klicken Sie dann auf die Registerkarte Auswahl in der oberen rechten Ecke ... Nur um zu sehen, dass es überhaupt nichts zeigt!

enter image description here

Beachten Sie, dass die Größe des Fensters alle Registerkarten, um fit zu machen, und dann ist es wieder Ändern der Größe, so dass die Registerkarte Auswahltaste erneut angezeigt wird, das Problem löst.

Hier ist der Code zu reproduzieren. Ich benutze jdk1.8.0_92. Sieht wie ein JDK-Fehler aus?

public class TabPaneTest extends Application { 

public static void main(String[] args) { 
    TabPaneTest.launch(); 
} 

int i = 1; 

@Override 
public void start(Stage primaryStage) throws Exception { 
    TabPane tabPane = new TabPane(); 

    tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++)))); 

    Button add = new Button("Add new tab"); 
    add.setOnAction(event -> { 
     tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++)))); 
    }); 

    Button addSort = new Button("Add new tab & sort"); 
    addSort.setOnAction(event -> { 
     tabPane.getTabs().add(new Tab("My beautiful tab " + i, new TextArea("pane " + (i++)))); 
     tabPane.getTabs().sort((o1, o2) -> o2.getText().compareTo(o1.getText())); 
    }); 
    Button sort = new Button("Sort tabs"); 
    sort.setOnAction(event -> { 
     tabPane.getTabs().sort((o1, o2) -> o2.getText().compareTo(o1.getText())); 
    }); 


    VBox vbox = new VBox(tabPane, new HBox(add, addSort, sort)); 

    primaryStage.setScene(new Scene(vbox)); 
    primaryStage.setWidth(400); 
    primaryStage.setHeight(300); 
    primaryStage.show(); 
} 
} 
+0

Haben Sie eine andere JDK-Version verwendet, um dies zu reproduzieren? – Supahupe

+1

Ich habe überprüft - der Code in 'TabPaneSkin's' removeTabs' (siehe meine Antwort unten) ist immer noch ab heute auf dem Java 9-Zweig vorhanden, also ist der Fehler wahrscheinlich immer noch da. Ich habe die vorherigen Versionen nicht überprüft, aber bis vor kurzem funktionierte die Sortierung von Tabs aufgrund dieses Fehlers (behoben im Jahr 2015) überhaupt nicht: http://bugs.java.com/bugdatabase/view_bug.do?bug_id= 8118423 – Denis

+0

Danke für die Information. Das ist sehr interessant – Supahupe

Antwort

2

Es gibt eine ListChangeListener im TabPaneSkin für die Tab Liste, aber irgendwie funktioniert es nicht die Liste auf Sortierung, wie Sie bereits erwähnt.

Als Abhilfe können Sie die Registerkarten in einer neuen Liste setzen können und es auf die TabPane, nachdem es sortiert wird:

List<Tab> tabs = new ArrayList(tabPane.getTabs()); 
tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText())); 
tabPane.getTabs().clear(); 
tabPane.getTabs().setAll(tabs); 
+0

Danke @jns, ich denke, das ist eine gültige Problemumgehung, bis der Fehler behoben ist. BTW, brauche da keine "ObservableList", eine normale ArrayList reicht aus! – Denis

+0

Eigentlich funktioniert das nicht - es hat keine Wirkung (die Reihenfolge der Registerkarten wird nicht geändert). Damit die Problemumgehung funktioniert, müssen Sie alle Registerkarten entfernen, bevor Sie sie erneut hinzufügen: 'tabPane.getTabs(). Clear();' before 'tabPane.getTabs(). SetAll (Registerkarten); ' – Denis

+0

Sie sind Recht. Es aktualisiert nur die Dropdown-Liste. – jns

2

Ich denke, ich habe das Problem in der TabPaneSkin Klasse Methode removeTabs gefunden: es entfernt die Einträge aus der tabHeaderArea.controlButtons.popup:

   // remove the menu item from the popup menu 
      ContextMenu popupMenu = tabHeaderArea.controlButtons.popup; 
      TabMenuItem tabItem = null; 
      if (popupMenu != null) { 
       for (MenuItem item : popupMenu.getItems()) { 
        tabItem = (TabMenuItem) item; 
        if (tab == tabItem.getTab()) { 
         break; 
        } 
        tabItem = null; 
       } 
      } 
      if (tabItem != null) { 
       tabItem.dispose(); 
       popupMenu.getItems().remove(tabItem); 
      } 
      // end of removing menu item 

Dies ist ein Problem, weil:

  1. die entgegengesetzte Methode addTabs nicht das Gegenteil tut (dh nicht fügen Sie die Elemente in das Popup-Menü und
  2. tabHeaderArea.controlButtons.popup verwaltet seine Einträge selbst, indem Sie in der tabPane.getTabs() Änderungen abonnieren:

    tabPane.getTabs().addListener((ListChangeListener<Tab>) c -> setupPopupMenu()); 
    

Sie beide entfernen die Elemente aus dem Popup-Menü, aber da setupPopupMenu vor removeTabs aufgerufen wird, werden die Einträge nicht erneut hinzugefügt, wenn die Register erneut mit addTabs hinzugefügt.

Ich entfernte die obigen Zeilen von removeTabs Methode, und es funktioniert gut.

Wird ein Fehler zu JDK einreichen ...

UPDATE:

Übermittelt einen Fehlerbericht an http://Bugs.java.com (Bewertung ID JI-9.038.050), haben aber wenig Hoffnung, dass es (meine letzte vorgelegt behoben werden Fehlerbericht im September 2015 ist immer noch "ausstehend").gehen sehen

 List<Tab> tabs = Lists.newArrayList(tabPane.getTabs()); 
     tabs.sort((o1, o2) -> o2.getText().compareTo(o1.getText())); 
     tabPane.getTabs().clear(); 
     tabPane.getTabs().setAll(tabs); 

Es ist hässlich, weil man eigentlich die Registerkarten:

die hässliche Abhilfe (dank @jns) In der Zwischenzeit ist es, alle Registerkarten zu entfernen, sortieren sie, und fügen Sie dann zurück weg und dann wieder zurück.

UPDATE 2:

Eine schöne Abhilfe (nochmals vielen Dank @jns) ist die korrekte Position des neuen Tab vor dem Einsetzen zu bestimmen:

Comparator<Tab> comparator = (o1, o2) -> o2.getText().compareTo(o1.getText()); 
    Button addSort = new Button("Add new tab, sorted"); 
    addSort.setOnAction(event -> { 
     Tab newTab = new Tab("My beautiful tab " + i, new TextArea("pane " + (i++))); 

     // THIS IS WRONG! See UPDATE 3 below: 
     // int pos = Math.max(0, Collections.binarySearch(tabPane.getTabs(), newTab, comparator)); 
     tabPane.getTabs().add(pos, newTab); 
    }); 

Dies funktioniert nur natürlich, wenn die Sortierreihenfolge tut nicht jedes Mal ändern, wenn ein neuer Tab eingefügt wird. Wenn Sie vorhandene Registerkarten mit einer anderen Sortierreihenfolge sortieren müssen, müssen Sie weiterhin alle Registerkarten entfernen, sortieren und dann erneut hinzufügen (siehe Problemumgehung 1).

UPDATE 3:

Es stellt sich heraus, dass die Java binarySearch nur für genaue Spiel sucht, und nicht die Rückkehr der untere Schranke als I (gewürzt C++ Entwickler;) erwarten würde ... Sie müssen also setzen an Ort und Stelle folgende Grausamkeit, um den Einfügepunkt zu finden:

<...> 
    int pos = 0; 
    while(pos < tabPane.getTabs().size() && tabPane.getTabs().get(pos).getText().compareTo(newTab.getText()) < 0) { 
     pos++; 
    } 
    tabPane.getTabs().add(pos, newTab); 
    <...> 
+1

Ist es möglich, die Sortierung anzuwenden, wenn Sie ein 'Tab' hinzufügen? In diesem Fall könnten Sie den Index für die neue Registerkarte festlegen und ihn an der entsprechenden Position zur Tabliste hinzufügen. – jns

+0

Das ist eine gute Idee, fügte es als zweite Problemumgehung (für den Fall, dass die Leute nicht kleine Zeichen unter dem Post lesen :) – Denis