【问题标题】:Gluon Mobile Cardpane UI Enhancements: Cardcell Generation/Deletion & Cardpane StylingGluon Mobile Cardpane UI 增强功能:Cardcell 生成/删除和 Cardpane 样式
【发布时间】:2017-10-20 13:21:56
【问题描述】:

我正在尝试使用自定义 HBox CardCells 创建一个卡片窗格。

问题 #1

如何设置此 CardPane 的背景?我希望它是透明的,但它不会从这种灰色改变。我尝试直接向节点添加样式以及添加自定义样式表。我也试过 setBackground 方法:

问题 #2

取自this SO 帖子,我能够为单元格生成添加动画,在该动画中它会向上淡入。但是,在随机插入卡片时,不同的单元格会丢失我嵌入在该单元格中的节点。我不知道这是因为这些卡片的回收概念(基于 Gluon 文档)还是什么:

问题 #3

我创建了这样的功能,用户可以通过向左滑动来删除卡片。然而,问题 #2 中的相同问题出现了,但在更大程度上,整个单元丢失但仍占用空间。如果我只有一个单元格并向左滑动,它会一直有效。但是,当我有多个单元格时(例如,我有 3 个单元格并且我删除了第二个单元格),事情会被破坏,单元格的事件处理程序会被删除,在一个单元格上向左滑动会在其下方的一个单元格上启动动画,等等。有没有办法我可以执行此功能,或者我最好的办法是摆脱 CardPane 并使用 VBox 和 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();
}

问题的工作演示:

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

}

这会在添加和向左滑动删除时导致以下输出:

【问题讨论】:

    标签: javafx gluon gluon-mobile


    【解决方案1】:

    如果您检查ScenicView,您会注意到CardPane 拥有一个CharmListView 控件,它使用内部ListView 来获取其父级的大小。

    所以这应该有效:

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

    正如我所提到的,控件是基于ListView,所以提供单元格的方式是使用单元格工厂。正如您在控件的JavaDoc 中看到的那样:

    CardPane 通过重复使用其卡片为大量项目做好了准备。

    开发人员可以通过cellFactoryProperty() 指定单元工厂来个性化单元创建。默认单元工厂准备接受来自扩展 Node 的类或其他不从 Node 扩展的类的对象,在后一种情况下,卡片文本将由对象的 Object.toString() 实现给出。

    如果您还没有使用它,请考虑使用以下内容:

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

    这应该为您管理卡片布局,避免空白单元格或错误地重复使用它们。

    至于动画,使用应该没有问题。

    对于滑动动画,Comments2.0 sample 提供了类似的用例:ListView,其中每个单元格使用 SlidingListTile。看看它的实现。

    您应该可以在 CardPane 中重复使用它。

    试一试,如果仍有问题,请在此处发布工作示例(或提供链接),以便我们重现它们。

    编辑

    根据发布的代码,关于如何设置工厂单元格的注释:

    所有使用单元格的 JavaFX 控件(如 ListViewTableView)以及 Gluon CardPane,都遵循 MVC 模式:

    • 型号。控件绑定到模型,使用该模型的可观察项列表。在示例的情况下,String 或任何常规 POJO,或者作为首选 JavaFX bean(具有可观察属性)。

    所以在这种情况下,你应该有:

    CardPane<String> cardPane = new CardPane<>();
    
    • 查看。该控件有一个方法来设置单元格如何呈现模型,cellFactory。该工厂可以只定义文本或任何图形节点,例如您的 CustomCard

    在这种情况下,您应该:

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

    地点:

    class CustomCard extends StackPane {
        
        private final Label label;
        
        public CustomCard(){
            label = new Label();
            getChildren().add(label);
        }
    
        public void setText(String text) {
            label.setText(text);
        }
    }
    

    在内部,控件使用 VirtualFlow 来管理重用单元格,并且仅在滚动时修改内容(模型)。

    正如您在单元工厂中看到的,现在您将迭代模型(字符串),而 CustomCard 保持不变,只有内容更新。

    使用这种方法不会出现您所描述的任何问题,至少在添加单元格时是这样。

    编辑 2

    我想出了一个适合我的解决方案,应该可以解决所有提到的问题。除了前面提到的,还需要恢复updateItem回调中应用于CustomCard的转换。

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

    【讨论】:

    • 何塞,我发布了该问题的完整工作演示。让我知道它是否有帮助。不幸的是,除非我错误地实现了单元工厂方法,否则它似乎对我不起作用。您可以看到如果您在滚动下方添加一堆框,则框中的某些内容会消失。如果您有超过 1 个单元格并尝试向左滑动以进行删除,那么奇怪的事情就会开始发生。
    • 可能是因为我隐藏了单元格,然后当 CardPane 想要回收它们时,它们仍然隐藏?
    • 我已经编辑了我的答案,并解释了你应该如何做细胞工厂。我还没有检查删除部分。
    • 我应该在单元工厂本身中添加删除逻辑吗?如何从单元工厂内部的卡窗格项目集合中删除单元?我需要将卡片设为静态吗?
    • 我没有提到它,但是是的,我删除了你所有的statics。一步一步来。让它工作添加卡片(包括动画),但注释掉删除调用。一旦这个工作(如果你按照我发布的内容应该),那么你可以专注于删除部分。对于这个:你不删除细胞!您删除单元格中的图形节点 (CustomCard)。这肯定包括动画,正如您在 Comments 2.0 示例中看到的那样(您应该先尝试一下)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-19
    • 1970-01-01
    • 2012-07-25
    • 2013-04-09
    • 1970-01-01
    • 1970-01-01
    • 2013-04-05
    相关资源
    最近更新 更多