programar - crear arboles en java
ListView con adornos CellFactory personalizados nodos invisibles (2)
Mi problema de diseño
Tengo un pequeño problema con ListView
y no estoy seguro si es por algún conocimiento que me falta o si mi enfoque es defectuoso. Tengo que admitir que aún no tengo claro cómo JavaFX maneja el diseño en muchos casos posibles.
La captura de pantalla anterior muestra el resultado obtenido dos veces con exactamente el mismo código, excepto que en el segundo una forma invisible que uso para el diseño coherente se hace visible para la depuración .
Las diversas clases involucradas por el Group
extensión de CellFactory
, lo intenté con algún otro Parent
sin mucho éxito hasta el momento.
Cómo reproducir
En lugar de compartir StarShape
, StarRow
y algunas otras clases de misceláneos (me gustaría que me lo pidieran) escribí una muestra para reproducir el problema. La clase extiende la Application
y anula el método de start(...)
como tal:
@Override
public void start(Stage primaryStage) throws Exception {
final StackPane root = new StackPane();
final Scene scene = new Scene(root, 400, 600);
final ListView<Boolean> listView = new ListView<>();
listView.setCellFactory(this::cellFactory);
for (int i = 0; i < 5 ; i++) {
listView.getItems().add(true);
listView.getItems().add(false);
}
root.getChildren().add(listView);
primaryStage.setScene(scene);
primaryStage.setTitle("ListView trims the invisible");
primaryStage.show();
}
donde this::cellFactory
es
private ListCell<Boolean> cellFactory(ListView<Boolean> listView) {
return new ListCell<Boolean>() {
@Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
} else {
final Rectangle tabShape = new Rectangle();
tabShape.setHeight(20);
tabShape.setWidth(40);
tabShape.setVisible(item);
final Label label = new Label(item.toString());
label.setLayoutX(40);
final Group cellRoot = new Group();
cellRoot.getChildren().add(tabShape);
cellRoot.getChildren().add(label);
setGraphic(cellRoot);
}
}
};
}
Lo anterior mostrará un ListView<Boolean>
con formas negras delante de elementos true
(debido a tabShape.setVisible(item);
bit). Los elementos false
ven como objetos Label
normales como si la forma invisible en su Group
no estuviera allí (pero lo es).
Comentarios de cierre
Al depurar esto, resulta que a los grupos con las formas invisibles se les dan valores de propiedad negativos de layoutX . Por lo tanto, los controles de Label
no están alineados como me gustaría. No sucede cuando llamo a setLayoutX
y setLayoutY
fuera de un ListView
(las formas invisibles sí fuerzan las compensaciones), pero probablemente no sea el único lugar donde ocurriría.
¿Qué está pasando y cómo evitarlo? Alternativamente, como supongo que me estoy acercando a este error , ¿cuál sería el camino correcto? En otras palabras, ¿cuál es la pregunta que debería hacer en lugar de esto?
Supongo que me estoy acercando a esto mal
No tu no eres. Al igual que con todos los diseños, a menudo hay varias formas de abordar el mismo problema. Su enfoque es realmente correcto y está muy cerca de una solución funcional.
Puedes lograr lo que buscas con un simple cambio de 1 línea. Es decir, cambiar el Group
a un HBox
.
Un HBox
asegura que los elementos se ordenan horizontalmente, uno tras otro. También permiten que elementos invisibles ocupen espacio.
También comenté una línea: label.setLayoutX(40)
. Hice esto porque HBox
no respetará esta configuración, y en realidad no la necesita. Automáticamente desplazará los elementos horizontalmente en la medida necesaria.
@Override
protected void updateItem(Boolean item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
}
else {
final Rectangle tabShape = new Rectangle();
tabShape.setHeight(20);
tabShape.setWidth(40);
tabShape.setVisible(item);
final Label label = new Label(item.toString());
//label.setLayoutX(40);
final HBox cellRoot = new HBox();
cellRoot.getChildren().add(tabShape);
cellRoot.getChildren().add(label);
setGraphic(cellRoot);
}
}
Cuando realizo esos cambios, su diseño se procesará así:
Importante : su ejemplo y sus capturas de pantalla son ligeramente diferentes. Es posible que desee utilizar un VBox
para su ejemplo de estrella (V para ''vertical'', H para ''horizontal'').
Tomando el comentario de @dlatikay , en lugar de establecer los elementos del marcador de posición en invisible, puede hacerlos transparentes al establecer su opacidad en 0.0
.
Aplicado a MCVE a partir de su pregunta, esto se haría reemplazando:
tabShape.setVisible(item);
con:
tabShape.setOpacity(item ? 1.0 : 0.0);
En términos de experiencia del usuario, puede dar un paso más. En lugar de configurar las estrellas "inactivas" para que sean totalmente transparentes, puede configurarlas para que sean casi transparentes, como en esta maqueta (con la opacidad establecida en 0.1
):
Los beneficios que veo son:
- Indica no solo la calificación de un elemento en la lista, sino también la calificación máxima.
- Evita espacios vacíos incómodos para elementos de lista con cero estrellas.