2017-10-20 35 views
1

J'essaie de créer un cardpane avec des cardes HBox personnalisées.Gluon Mobile Cardpane Améliorations de l'interface utilisateur: Génération/suppression de cardes et stylet

Issue # 1

Comment puis-je configurer l'arrière-plan de cette CardPane? Je veux que ce soit transparent, mais ça ne changera pas de cette couleur grise. J'ai essayé d'ajouter directement un style au noeud et d'ajouter une feuille de style personnalisée. J'ai aussi essayé la méthode setbackground:

enter image description here

Issue # 2

Taken de this SO post, je suis en mesure d'ajouter une animation pour la génération de cellule dans laquelle il apparaît en fondu vers le haut . Cependant, dans les insertions de cartes aléatoires, différentes cellules perdent le nœud que j'ai intégré dans cette cellule. Je ne sais pas si cela est dû au fait du concept de recyclage de ces cartes (basé sur docs Gluon) ou quoi:

enter image description here

Numéro 3

J'ai créé des fonctionnalités telles que l'utilisateur pouvez supprimer les cartes en balayant à gauche. Cependant, le même problème de l'édition n ° 2 se pose, mais dans une plus grande mesure où toute la cellule est manquante mais prend encore de l'espace. Si je n'ai qu'une seule cellule et que je la glisse à gauche, cela fonctionne tout le temps. Cependant quand j'ai plus d'une cellule (par exemple j'ai 3 cellules et je supprime la 2ème cellule), les choses se cassent, les gestionnaires d'événements pour les cellules sont supprimés, balayant à gauche sur une cellule démarre l'animation sur une cellule en dessous, etc. Existe-t-il un moyen de réaliser cette fonctionnalité ou est-ce mon meilleur pari de me débarrasser du CardPane et d'utiliser une combinaison d'éléments VBox et HBox?

private void addToCardPane(CustomCard newCard) { 

    ObservableList<Node> items = cardpane.getItems(); 
    boolean override = false; 

    for (int i = 0; i < cardpane.getItems().size(); i++) { 
     CustomCard box = (CustomCard) items.get(i); 
     if (box.checkEquality(newCard)) { 
      box.increaseNumber(newCard); 
      override = true; 
      break; 
     } 
    } 

    if (override == false) { 
     cardpane.getItems().add(newCard); 
     cardpane.layout(); 
     VirtualFlow vf = (VirtualFlow) cardpane.lookup(".virtual-flow"); 
     Node cell = vf.getCell(cardpane.getItems().size() - 1); 
     cell.setTranslateX(0); 
     cell.setOpacity(1.0); 
     if (!cardpane.lookup(".scroll-bar").isVisible()) { 
      FadeInUpTransition f = new FadeInUpTransition(cell); 
      f.setRate(2); 
      f.play(); 
     } else { 
      PauseTransition p = new PauseTransition(Duration.millis(20)); 
      p.setOnFinished(e -> { 
       vf.getCell(cardpane.getItems().size() - 1).setOpacity(0); 
       vf.show(cardpane.getItems().size() - 1); 
       FadeTransition f = new FadeTransition(); 
       f.setDuration(Duration.seconds(1)); 
       f.setFromValue(0); 
       f.setToValue(1); 
       f.setNode(vf.getCell(cardpane.getItems().size() - 1)); 
       f.setOnFinished(t -> { 
       }); 
       f.play(); 
      }); 
      p.play(); 
     } 
    } 
    initializeDeletionLogic(); 
} 

private void initializeDeletionLogic() { 
    VirtualFlow vf = (VirtualFlow) cardpane.lookup(".virtual-flow"); 
    for (int i = 0; i < cardpane.getItems().size(); i++) { 
     CustomCard card = (CustomCard) cardpane.getItems().get(i); 
     Node cell2 = vf.getCell(i); 
     addRemovalLogicForCell(card, cell2); 
    } 
} 

private static double initX = 0; 
private void addRemovalLogicForCell(OpioidCard card, Node cell) { 
    card.setOnMousePressed(e -> { 
     initX = e.getX(); 
    }); 

    card.setOnMouseDragged(e -> { 
     double current = e.getX(); 
     if (current < initX) { 
      if ((current - initX) < 0 && (current - initX) > -50) { 
       cell.setTranslateX(current - initX); 
      } 
     } 
    }); 

    card.setOnMouseReleased(e -> { 
     double current = e.getX(); 
     double delta = current - initX; 
     System.out.println(delta); 
     if (delta > -50) { 
      int originalMillis = 500; 
      double ratio = (50 - delta)/50; 
      int newMillis = (int) (500 * ratio); 
      TranslateTransition translate = new TranslateTransition(Duration.millis(newMillis)); 
      translate.setToX(0); 
      translate.setNode(cell); 
      translate.play(); 
     } else { 
      FadeTransition ft = new FadeTransition(Duration.millis(300), cell); 
      ft.setFromValue(1.0); 
      ft.setToValue(0); 

      TranslateTransition translateTransition 
        = new TranslateTransition(Duration.millis(300), cell); 
      translateTransition.setFromX(cell.getTranslateX()); 
      translateTransition.setToX(-400); 

      ParallelTransition parallel = new ParallelTransition(); 
      parallel.getChildren().addAll(ft, translateTransition); 
      parallel.setOnFinished(evt -> { 

       removeCard(card); 

       ObservableList<CustomCard > cells = FXCollections.observableArrayList(); 

       for(int i = 0; i < this.cardpane.getItems().size(); i++){ 
        cells.add((CustomCard)this.cardpane.getItems().get(i)); 
       } 

       this.cardpane.getItems().clear(); 
       for(int i = 0; i < cells.size(); i++){ 
        this.cardpane.getItems().add(cells.get(i)); 
       } 

       initializeDeletionLogic(); 
       initX = 0; 
      }); 

      parallel.play(); 
     } 
    }); 
} 

private void removeCard(OpioidCard card) { 
    for (int i = 0; i < cardpane.getItems().size(); i++) { 
     if (cardpane.getItems().get(i) == card) { 
      cardpane.getItems().remove(i); 
      updateNumber(this.totalNumber); 
      break; 
     } 
    } 

    for (int i = 0; i < dataList.size(); i++) { 
     if (dataList.get(i).getName().equalsIgnoreCase(card.getName())) { 
      dataList.remove(i); 
     } 
    } 

    this.cardpane.layout(); 
    initializeDeletionLogic(); 
} 

enter image description here

DE TRAVAIL DE DEMO NUMÉRO:

package com.mobiletestapp; 

import com.gluonhq.charm.glisten.animation.FadeInUpTransition; 
import com.gluonhq.charm.glisten.control.AppBar; 
import com.gluonhq.charm.glisten.control.CardCell; 
import com.gluonhq.charm.glisten.control.CardPane; 
import com.gluonhq.charm.glisten.mvc.View; 
import com.gluonhq.charm.glisten.visual.MaterialDesignIcon; 
import com.sun.javafx.scene.control.skin.VirtualFlow; 
import javafx.animation.FadeTransition; 
import javafx.animation.ParallelTransition; 
import javafx.animation.PauseTransition; 
import javafx.animation.TranslateTransition; 
import javafx.scene.Node; 
import javafx.scene.control.Label; 
import javafx.scene.layout.StackPane; 
import javafx.util.Duration; 

public class BasicView extends View { 

    class CustomCard extends StackPane{ 
     public CustomCard(String text){ 
      this.getChildren().add(new Label(text)); 
     } 
    } 
    private static double initX = 0; 
    private static void addRemovalLogicForCell(CustomCard card, Node cell) { 
     card.setOnMousePressed(e -> { 
      initX = e.getX(); 
     }); 

     card.setOnMouseDragged(e -> { 
      double current = e.getX(); 
      if (current < initX) { 
       if ((current - initX) < 0 && (current - initX) > -50) { 
        cell.setTranslateX(current - initX); 
       } 
      } 
     }); 

     card.setOnMouseReleased(e -> { 
      double current = e.getX(); 
      double delta = current - initX; 
      System.out.println(delta); 
      if (delta > -50) { 
       int originalMillis = 500; 
       double ratio = (50 - delta)/50; 
       int newMillis = (int) (500 * ratio); 
       TranslateTransition translate = new TranslateTransition(Duration.millis(newMillis)); 
       translate.setToX(0); 
       translate.setNode(cell); 
       translate.play(); 
      } else { 
       FadeTransition ft = new FadeTransition(Duration.millis(300), cell); 
       ft.setFromValue(1.0); 
       ft.setToValue(0); 

       TranslateTransition translateTransition 
         = new TranslateTransition(Duration.millis(300), cell); 
       translateTransition.setFromX(cell.getTranslateX()); 
       translateTransition.setToX(-400); 

       ParallelTransition parallel = new ParallelTransition(); 
       parallel.getChildren().addAll(ft, translateTransition); 
       parallel.setOnFinished(evt -> { 

        for(int i = 0; i < cardPane.getItems().size(); i++){ 
         if(cardPane.getItems().get(i) == card){ 
          cardPane.getItems().remove(i); 
         } 
        } 
        initX = 0; 
       }); 

       parallel.play(); 
      } 
     }); 
    } 

    private static CardPane cardPane = null; 
    public BasicView(String name) { 
     super(name); 

     cardPane = new CardPane(); 

     cardPane.setCellFactory(p -> new CardCell<CustomCard>() { 

      @Override 
      public void updateItem(CustomCard item, boolean empty) { 
       super.updateItem(item, empty); 
       if (!empty) { 
        setText(null); 
        setGraphic(item); 
       } else { 
        setText(null); 
        setGraphic(null); 
       } 
      } 
     }); 



     setCenter(cardPane); 
    } 

    private static void addCard(CustomCard newCard){ 
     cardPane.getItems().add(newCard); 
      cardPane.layout(); 
      VirtualFlow vf = (VirtualFlow) cardPane.lookup(".virtual-flow"); 
      Node cell = vf.getCell(cardPane.getItems().size() - 1); 
      cell.setTranslateX(0); 
      cell.setOpacity(1.0); 
      if (!cardPane.lookup(".scroll-bar").isVisible()) { 
       FadeInUpTransition f = new FadeInUpTransition(cell); 
       f.setRate(2); 
       f.play(); 
      } else { 
       PauseTransition p = new PauseTransition(Duration.millis(20)); 
       p.setOnFinished(e -> { 
        vf.getCell(cardPane.getItems().size() - 1).setOpacity(0); 
        vf.show(cardPane.getItems().size() - 1); 
        FadeTransition f = new FadeTransition(); 
        f.setDuration(Duration.seconds(1)); 
        f.setFromValue(0); 
        f.setToValue(1); 
        f.setNode(vf.getCell(cardPane.getItems().size() - 1)); 
        f.setOnFinished(t -> { 
        }); 
        f.play(); 
       }); 
       p.play(); 
      } 
      addRemovalLogicForCell(newCard, cell); 
    } 

    @Override 
    protected void updateAppBar(AppBar appBar) { 
     appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu"))); 
     appBar.setTitleText("Basic View"); 
     appBar.getActionItems().add(MaterialDesignIcon.ADD.button(e -> addCard(new CustomCard("Hello")))); 
    } 

} 

Cela conduit à la sortie suivante lors de l'ajout et la suppression de balayage vers la gauche:

enter image description here

Répondre

1

Si vous vérifiez avec ScenicView, vous remarquerez que le CardPane détient un contrôle CharmListView, qui utilise un ListView qui prend la taille de son parent.

Donc cela devrait fonctionner:

.card-pane > .charm-list-view > .list-view { 
    -fx-background-color: transparent; 
} 

Comme je l'ai mentionné, le contrôle est basé sur un ListView, donc la façon de fournir des cellules est en utilisant l'usine cellulaire. Comme vous pouvez le lire dans JavaDoc de la commande:

Le CardPane est préparé pour un grand nombre d'éléments en réutilisant ses cartes.

Un développeur peut personnaliser la création de cellule en spécifiant une fabrique de cellules via cellFactoryProperty(). La fabrique de cellules par défaut est prête à accepter des objets provenant de classes qui étendent Node ou d'autres classes qui ne s'étendent pas depuis le nœud, dans le dernier cas, le texte de la carte sera donné par l'implémentation Object.toString() de l'objet.

Si vous ne l'utilisez pas encore, pensez à utiliser quelque chose comme ceci:

cardPane.setCellFactory(p -> new CardCell<T>() { 

    @Override 
    public void updateItem(T item, boolean empty) { 
     super.updateItem(item, empty); 
     if (!empty) { 
      setText(null); 
      setGraphic(createContent(item)); 
     } else { 
      setText(null); 
      setGraphic(null); 
     } 
    } 
}); 

Cela devrait gérer pour vous la mise en page des cartes, en évitant les cellules vides ou mauvaise réutilisation d'entre eux. En ce qui concerne l'animation, il ne devrait pas y avoir de problème à l'utiliser.

Pour les animations de balayage, les commentaires2.0 sample fournissent un cas d'utilisation similaire: A ListView où chaque cellule utilise un SlidingListTile. Jetez un oeil à sa mise en œuvre.

Vous devriez être capable de le réutiliser avec le CardPane. Essayez-le, et si vous avez encore des problèmes, postez un exemple de travail ici (ou fournissez un lien), afin que nous puissions les reproduire.

EDIT

Basé sur le code affiché, un commentaire lié à la façon dont la cellule de l'usine doit être réglée:

Toutes les commandes JavaFX en utilisant des cellules (comme ListView ou TableView), et aussi le gluons CardPane, suivre le modèle MVC:

  • Modèle. Le contrôle est lié à un modèle, en utilisant une liste observable d'éléments de ce modèle. Dans le cas de l'échantillon, un String, ou n'importe quel POJO ordinaire, ou, comme choix préféré, un bean JavaFX (avec des propriétés observables).

Donc dans ce cas, vous devriez avoir:

CardPane<String> cardPane = new CardPane<>(); 
  • Voir. Le contrôle a une méthode pour définir comment la cellule rend le modèle, cellFactory. Cette usine peut définir seulement du texte, ou n'importe quel nœud graphique, comme votre CustomCard.

Dans ce cas, vous devriez avoir:

cardPane.setCellFactory(p -> new CardCell<String>() { 

     private final CustomCard card; 
     { 
      card = new CustomCard(); 
     } 

     @Override 
     public void updateItem(String item, boolean empty) { 
      super.updateItem(item, empty); 
      if (item != null && !empty) { 
       card.setText(item); 
       setGraphic(card); 
       setText(null); 
      } else { 
       setGraphic(null); 
       setText(null); 
      } 
     } 
    }); 

où:

class CustomCard extends StackPane { 

    private final Label label; 

    public CustomCard(){ 
     label = new Label(); 
     getChildren().add(label); 
    } 

    public void setText(String text) { 
     label.setText(text); 
    } 
} 

En interne, le contrôle utilise un VirtualFlow qui parvient à réutiliser les cellules et modifier uniquement le contenu (le modèle) lors du défilement.

Comme vous pouvez le voir dans la fabrique de cellules, vous allez maintenant parcourir le modèle (String), tandis que le CustomCard reste le même, et seul le contenu est mis à jour.

Cette approche ne présente aucun des problèmes que vous avez décrits, au moins lors de l'ajout de cellules.

EDIT 2

Je suis venu avec une solution qui fonctionne très bien pour moi et devrait résoudre tous les problèmes mentionnés. Outre ce qui a été mentionné précédemment, il est également nécessaire de restaurer les transformations appliquées à la CustomCard dans les rappels updateItem.

public class BasicView extends View { 

    private final CardPane<String> cardPane; 

    public BasicView(String name) { 
     super(name); 

     cardPane = new CardPane<>(); 

     cardPane.setCellFactory(p -> new CardCell<String>() { 

      private final CustomCard card; 
      private final HBox box; 
      { 
       card = new CustomCard(); 
       card.setMaxWidth(Double.MAX_VALUE); 
       card.prefWidthProperty().bind(widthProperty()); 
       box = new HBox(card); 
       box.setAlignment(Pos.CENTER); 
       box.setStyle("-fx-background-color: grey"); 
       addRemovalLogicForCell(card); 
      } 

      @Override 
      public void updateItem(String item, boolean empty) { 
       super.updateItem(item, empty); 
       if (item != null && !empty) { 
        card.setText(item); 
        card.setTranslateX(0); 
        card.setOpacity(1.0); 
        setGraphic(box); 
        setText(null); 
       } else { 
        setGraphic(null); 
        setText(null); 
       } 
      } 
     }); 

     setCenter(cardPane); 
    } 

    class CustomCard extends StackPane { 

     private final Label label; 

     public CustomCard(){ 
      label = new Label(); 
      label.setStyle("-fx-font-size: 20;"); 
      getChildren().add(label); 
      setStyle("-fx-padding: 20; -fx-background-color: white"); 
      setPrefHeight(100); 
     } 

     public void setText(String text) { 
      label.setText(text); 
     } 

     public String getText() { 
      return label.getText(); 
     } 
    } 

    private double initX = 0; 
    private void addRemovalLogicForCell(CustomCard card) { 
     card.setOnMousePressed(e -> { 
      initX = e.getX(); 
     }); 

     card.setOnMouseDragged(e -> { 
      double current = e.getX(); 
      if ((current - initX) < 0 && (current - initX) > -50) { 
       card.setTranslateX(current - initX); 
      } 
     }); 

     card.setOnMouseReleased(e -> { 
      double current = e.getX(); 
      double delta = current - initX; 
      if (delta < 50) { 
       if (delta > -50) { 
        int originalMillis = 500; 
        double ratio = (50 - delta)/50; 
        int newMillis = (int) (500 * ratio); 
        TranslateTransition translate = new TranslateTransition(Duration.millis(newMillis)); 
        translate.setToX(0); 
        translate.setNode(card); 
        translate.play(); 
       } else { 
        FadeTransition ft = new FadeTransition(Duration.millis(300), card); 
        ft.setFromValue(1.0); 
        ft.setToValue(0); 

        TranslateTransition translateTransition 
          = new TranslateTransition(Duration.millis(300), card); 
        translateTransition.setFromX(card.getTranslateX()); 
        translateTransition.setToX(-400); 

        ParallelTransition parallel = new ParallelTransition(); 
        parallel.getChildren().addAll(ft, translateTransition); 
        parallel.setOnFinished(evt -> { 
         cardPane.getItems().remove(card.getText()); 
         initX = 0; 
        }); 

        parallel.play(); 
       } 
      } 

     }); 
    } 

    private void addCard(String newCard){ 
     cardPane.getItems().add(newCard); 
     cardPane.layout(); 

     VirtualFlow vf = (VirtualFlow) cardPane.lookup(".virtual-flow"); 
     IndexedCell cell = vf.getCell(cardPane.getItems().size() - 1); 
     cell.setTranslateX(0); 
     cell.setOpacity(0); 
     if (! cardPane.lookup(".scroll-bar").isVisible()) { 
      FadeInUpTransition f = new FadeInUpTransition(cell, true); 
      f.setRate(2); 
      f.play(); 
     } else { 
      PauseTransition p = new PauseTransition(Duration.millis(20)); 
      p.setOnFinished(e -> { 
       vf.show(cardPane.getItems().size() - 1); 
       FadeInTransition f = new FadeInTransition(cell); 
       f.setRate(2); 
       f.play(); 
      }); 
      p.play(); 
     } 
    } 

    @Override 
    protected void updateAppBar(AppBar appBar) { 
     appBar.setNavIcon(MaterialDesignIcon.MENU.button(e -> System.out.println("Menu"))); 
     appBar.setTitleText("Basic View"); 
     appBar.getActionItems().add(MaterialDesignIcon.ADD.button(e -> addCard("Hello #" + new Random().nextInt(100)))); 
    } 

} 
+0

Jose, j'ai posté une démo de travail complète du numéro. Faites-moi savoir si ça aide. Malheureusement, la méthode de l'usine de cellules ne semble pas fonctionner pour moi si je ne l'ai pas implémentée correctement. Vous pouvez voir si vous ajoutez un tas de cases en dessous de défilement, une partie du contenu dans les cases disparaît. Si vous avez plus d'une cellule et essayez de balayer vers la gauche pour l'enlever, des choses étranges commencent à se produire. –

+0

Serait-ce parce que je cache les cellules et quand le CardPane veut les recycler, elles sont encore cachées? –

+0

J'ai édité ma réponse, avec une explication de la façon dont vous devriez faire l'usine de cellules. Je n'ai pas vérifié la partie retrait. –