Last active
June 2, 2023 20:54
-
-
Save uphy/6dc368160f182cd2f80f to your computer and use it in GitHub Desktop.
JavaFX Expandable ListView
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (C) 2015 uphy.jp | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package expandablelistview; | |
import javafx.collections.ObservableList; | |
import javafx.event.EventHandler; | |
import javafx.scene.control.ListCell; | |
import javafx.scene.control.ListView; | |
import javafx.scene.control.TitledPane; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.layout.BorderPane; | |
import javafx.scene.text.Text; | |
import javafx.util.Callback; | |
import java.util.HashSet; | |
import java.util.Set; | |
/** | |
* @author Yuhi Ishikura | |
*/ | |
public class ExpandableListView<E> extends ListView<E> { | |
private ContentProvider<E> contentProvider = new ContentProvider<E>() { | |
@Override | |
public String getTitleOf(final E item) { | |
return item.toString(); | |
} | |
@Override | |
public String getContentOf(final E item) { | |
return getTitleOf(item); | |
} | |
}; | |
private final Set<E> expandedItems = new HashSet<E>(); | |
public ExpandableListView() { | |
setSelectionModel(null); | |
setCellFactory(new Callback<ListView<E>, ListCell<E>>() { | |
@Override | |
public ListCell<E> call(final ListView<E> param) { | |
final TitledPane titledPane = new TitledPane(); | |
final Text contentArea = new Text(); | |
titledPane.setAnimated(false); | |
titledPane.setCollapsible(true); | |
titledPane.setExpanded(false); | |
final BorderPane contentAreaWrapper = new BorderPane(); | |
contentAreaWrapper.setLeft(contentArea); | |
titledPane.setContent(contentAreaWrapper); | |
titledPane.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() { | |
@Override | |
public void handle(final MouseEvent event) { | |
final boolean expanded = titledPane.isExpanded(); | |
final E item = (E)titledPane.getUserData(); | |
if (item == null) { | |
return; | |
} | |
if (expanded) { | |
expandedItems.add(item); | |
} else { | |
expandedItems.remove(item); | |
} | |
} | |
}); | |
return new ListCell<E>() { | |
@Override | |
protected void updateItem(final E item, final boolean empty) { | |
super.updateItem(item, empty); | |
if (empty) { | |
titledPane.setText(""); | |
contentArea.setText(""); | |
return; | |
} | |
final boolean expanded = isExpanded(item); | |
titledPane.setUserData(item); | |
titledPane.setExpanded(expanded); | |
titledPane.setText(contentProvider.getTitleOf(item)); | |
contentArea.setText(contentProvider.getContentOf(item)); | |
setGraphic(titledPane); | |
} | |
}; | |
} | |
}); | |
getStylesheets().add(String.format("/%s/style.css", getClass().getPackage().getName().replaceAll("\\.", "/"))); | |
} | |
public void setContentProvider(final ContentProvider<E> contentProvider) { | |
this.contentProvider = contentProvider; | |
} | |
public void expand(E item) { | |
expand(item, true); | |
} | |
public void collapse(E item) { | |
expand(item, false); | |
} | |
private void expand(E item, boolean expand) { | |
if (expand) { | |
this.expandedItems.add(item); | |
} else { | |
this.expandedItems.remove(item); | |
} | |
ObservableList<E> o = getItems(); | |
setItems(null); | |
setItems(o); | |
} | |
public boolean isExpanded(E item) { | |
return this.expandedItems.contains(item); | |
} | |
public static interface ContentProvider<E> { | |
String getTitleOf(E item); | |
String getContentOf(E item); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Copyright (C) 2015 uphy.jp | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package expandablelistview; | |
import javafx.application.Application; | |
import javafx.scene.Scene; | |
import javafx.scene.layout.BorderPane; | |
import javafx.stage.Stage; | |
/** | |
* @author Yuhi Ishikura | |
*/ | |
public class Main extends Application { | |
@Override | |
public void start(final Stage primaryStage) throws Exception { | |
final ExpandableListView<Item> listView = new ExpandableListView<Item>(); | |
listView.setContentProvider(new ExpandableListView.ContentProvider<Item>() { | |
@Override | |
public String getTitleOf(final Item item) { | |
return item.getTitle(); | |
} | |
@Override | |
public String getContentOf(final Item item) { | |
return item.getContent(); | |
} | |
}); | |
for (int i = 0; i < 100000; i++) { | |
listView.getItems().add(new Item("title" + i, "content" + i)); | |
} | |
final BorderPane group = new BorderPane(); | |
group.setCenter(listView); | |
final Scene scene = new Scene(group); | |
primaryStage.setScene(scene); | |
primaryStage.show(); | |
} | |
public static class Item { | |
String title; | |
String content; | |
public Item(String title, String content) { | |
this.title = title; | |
this.content = content; | |
} | |
public String getTitle() { | |
return title; | |
} | |
public String getContent() { | |
return content; | |
} | |
} | |
public static void main(String[] args) { | |
Main.launch(args); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.list-cell { | |
-fx-padding: 0; | |
-fx-background-color: white; | |
} | |
.titled-pane:focused { | |
-fx-text-fill: black; | |
} | |
.titled-pane > .title { | |
-fx-background-color: -fx-box-border, -fx-inner-border, white; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Pretty cool piece of code! I just have one question, have you already implemented a way to get selected children items? Thanks