2017-10-20 3 views
1

Ich versuche, eine Karte mit benutzerdefinierten HBox CardCells zu erstellen.Gluon Mobile Cardpane UI Verbesserungen: Cardcell Generation/Löschen & Cardpane Styling

Ausgabe # 1

Wie kann ich den Hintergrund dieser CardPane eingestellt? Ich möchte, dass es transparent ist, aber es wird sich nicht von dieser grauen Farbe ändern. Ich habe versucht, den Knoten direkt mit Stilen zu versehen und ein benutzerdefiniertes Stylesheet hinzuzufügen. Ich habe auch versucht die setBackground Methode:

enter image description here

Issue # 2

Entnommen this SO Post konnte ich eine Animation für Zellgeneration hinzuzufügen, in dem sie in nach oben verblasst . Bei zufälligen Karteneinsätzen verlieren jedoch verschiedene Zellen den Knoten, den ich in diese Zelle eingebettet habe. Ich weiß nicht, ob dies wegen des Recycling-Konzepts dieser Karten ist (basierend auf Gluon docs) oder was:

enter image description here

Ausgabe # 3

I-Funktionalität erstellt, so dass der Benutzer kann die Karten löschen, indem Sie nach links wischen. Es tritt jedoch das gleiche Problem von Problem Nr. 2 auf, jedoch in einem noch größeren Ausmaß, in dem die gesamte Zelle fehlt, aber immer noch Platz beansprucht. Wenn ich nur eine Zelle habe und nach links wische, funktioniert es die ganze Zeit. Wenn ich jedoch mehr als eine Zelle habe (zum Beispiel habe ich 3 Zellen und lösche die zweite Zelle), werden die Dinge kaputt, Event-Handler für Zellen werden entfernt, nach einer Zelle nach links wischen beginnt die Animation auf einer Zelle darunter usw. Gibt es eine Möglichkeit, diese Funktionalität zu nutzen, oder ist es meine beste Sache, den CardPane einfach loszuwerden und eine Kombination aus VBox und HBox-Elementen zu verwenden?

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

ARBEITS DEMO AUSGABETAG:

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")))); 
    } 

} 

Dies ist auf die folgende Ausgabe führt beim Hinzufügen und zum Löschen links Streichen nach:

enter image description here

Antwort

1

Wenn Sie mit ScenicView überprüfen, werden Sie feststellen, dass die CardPane eine CharmListView Steuerung enthält, die in Begriffen eine innere ListView verwendet, die die Größe ihrer übergeordneten verwendet.

dies sollte also funktionieren:

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

Wie bereits erwähnt, wird die Steuerung auf einem ListView basiert, so dass die Art und Weise Zellen, um die Zellfabrik verwendet. Wie Sie in den JavaDoc der Steuerung lesen können:

Der CardPane ist für eine große Anzahl von Elementen durch die Wiederverwendung seiner Karten vorbereitet.

Ein Entwickler kann die Zellenerstellung personalisieren, indem er eine Zellenfabrik über cellFactoryProperty() angibt. Die Standardzellenfabrik ist bereit, Objekte von Klassen zu akzeptieren, die Knoten oder andere Klassen erweitern, die nicht von Knoten ausgehen, im letzteren Fall wird der Kartentext durch die Object.toString() - Implementierung des Objekts angegeben.

Wenn Sie es noch nicht verwenden, sollten Sie so etwas wie dies mit:

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); 
     } 
    } 
}); 

Dies ist für Sie die Karten-Layout verwalten sollte, zu vermeiden leere Zellen oder falsch Wiederverwendung von ihnen.

Wie für die Animation sollte es kein Problem bei der Verwendung sein.

Für Swipe-Animationen bietet Comments2.0 sample einen ähnlichen Anwendungsfall: A ListView wobei jede Zelle eine SlidingListTile verwendet. Sehen Sie sich die Implementierung an.

Sie sollten es mit dem CardPane wiederverwenden können.

Probieren Sie es aus, und wenn Sie immer noch Probleme haben, veröffentlichen Sie hier ein funktionierendes Beispiel (oder stellen Sie einen Link zur Verfügung), damit wir sie reproduzieren können.

EDIT

auf der entsandten Code-Basis, ein Kommentar im Zusammenhang, wie die Fabrik Zelle eingestellt werden soll:

All JavaFX steuern Zellen (wie ListView oder TableView), und auch die Gluon mit CardPane, folgen Sie dem MVC-Muster:

  • Modell. Das Steuerelement ist an ein Modell gebunden, wobei eine beobachtbare Liste von Elementen dieses Modells verwendet wird. Im Falle der Probe, eine String, oder eine regelmäßige POJO, oder, als bevorzugte Wahl, eine JavaFX Bean (mit beobachtbaren Eigenschaften).

So in diesem Fall sollten Sie haben:

CardPane<String> cardPane = new CardPane<>(); 
  • Ansicht. Das Steuerelement verfügt über eine Methode, um festzulegen, wie die Zelle das Modell, die cellFactory, rendert. Diese Factory kann nur Text oder einen Grafikknoten wie Ihre CustomCard definieren.

In diesem Fall sollten Sie haben:

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); 
      } 
     } 
    }); 

wo:

class CustomCard extends StackPane { 

    private final Label label; 

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

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

Intern wird die Steuerung eine VirtualFlow verwendet, die Zellen wieder zu verwenden verwaltet, und ändern nur den Inhalt (die Modell) beim Scrollen.

Wie Sie in der Zellfabrik sehen können, werden Sie jetzt über das Modell (String) iterieren, während die CustomCard bleibt gleich, und nur der Inhalt ist aktualisiert.

Die Verwendung dieses Ansatzes zeigt keine der Probleme, die Sie beschrieben haben, zumindest beim Hinzufügen von Zellen.

EDIT 2

Ich habe mit einer Lösung zu kommen, die für mich gut funktioniert und sollte alle genannten Probleme lösen. Neben dem, was zuvor erwähnt wurde, ist es auch erforderlich, die auf die CustomCard angewendeten Transformationen in den Callbacks updateItem wiederherzustellen.

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, ich habe eine vollständige Demo des Problems veröffentlicht. Lass es mich wissen, wenn es hilft. Unglücklicherweise scheint die Zellfabrik-Methode für mich nicht zu funktionieren, wenn ich sie nicht falsch implementiert habe. Sie können sehen, wenn Sie eine Reihe von Boxen unter Scroll hinzufügen, einige der Inhalte in den Feldern verschwindet. Wenn Sie mehr als eine Zelle haben und versuchen, zum Entfernen nach links zu wischen, beginnen seltsame Dinge zu passieren. –

+0

Könnte es sein, weil ich die Zellen verberge und wenn die CardPane sie dann recyceln will, sind sie immer noch versteckt? –

+0

Ich habe meine Antwort bearbeitet, mit einer Erklärung, wie Sie die Zellfabrik machen sollten. Ich habe den Entfernungsteil nicht überprüft. –

Verwandte Themen