2013-05-30 59 views
8

FlowPane cơ bản trong JavaFX đưa ra các mục bên trong mỗi lần cửa sổ được thay đổi kích thước. Tuy nhiên, không có hình động và kết quả là khá chói tai.Hoạt ảnh khi thay đổi bố cục

Tôi đã ghép một trình nghe thay đổi trên thuộc tính layoutX và layoutY của mỗi Nút bên trong FlowPane và kết quả nhiều hơn hoặc ít hoạt động hơn, nhưng đôi khi tôi thay đổi kích thước cửa sổ một cách nhanh chóng, các thành phần còn lại ở những vị trí không nhất quán.

Tôi đang thiếu gì?

package javafxapplication1; 

import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
import javafx.animation.Transition; 
import javafx.animation.TranslateTransition; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 
import javafx.scene.Node; 
import javafx.util.Duration; 

/** 
* Animates an object when its position is changed. For instance, when 
* additional items are added to a Region, and the layout has changed, then the 
* layout animator makes the transition by sliding each item into its final 
* place. 
*/ 
public class LayoutAnimator implements ChangeListener, ListChangeListener<Node> { 

    private Map<Node, Transition> nodesInTransition; 

    public LayoutAnimator() { 
    this.nodesInTransition = new HashMap<>(); 
    } 

    /** 
    * Animates all the children of a Region. 
    * <code> 
    * VBox myVbox = new VBox(); 
    * LayoutAnimator animator = new LayoutAnimator(); 
    * animator.observe(myVbox.getChildren()); 
    * </code> 
    * 
    * @param nodes 
    */ 
    public void observe(ObservableList<Node> nodes) { 
    for (Node node : nodes) { 
     this.observe(node); 
    } 
    nodes.addListener(this); 
    } 

    public void unobserve(ObservableList<Node> nodes) { 
    nodes.removeListener(this); 
    } 

    public void observe(Node n) { 
    n.layoutXProperty().addListener(this); 
    n.layoutYProperty().addListener(this); 
    } 

    public void unobserve(Node n) { 
    n.layoutXProperty().removeListener(this); 
    n.layoutYProperty().removeListener(this); 
    } 

    @Override 
    public void changed(ObservableValue ov, Object oldValue, Object newValue) { 
    final Double oldValueDouble = (Double) oldValue; 
    final Double newValueDouble = (Double) newValue; 
    final Double changeValueDouble = newValueDouble - oldValueDouble; 
    DoubleProperty doubleProperty = (DoubleProperty) ov; 

    Node node = (Node) doubleProperty.getBean(); 
    final TranslateTransition t; 
    if ((TranslateTransition) nodesInTransition.get(node) == null) { 
     t = new TranslateTransition(Duration.millis(150), node); 
    } else { 
     t = (TranslateTransition) nodesInTransition.get(node); 
    } 

    if (doubleProperty.getName().equals("layoutX")) { 
     Double orig = node.getTranslateX(); 
     if (Double.compare(t.getFromX(), Double.NaN) == 0) { 
     t.setFromX(orig - changeValueDouble); 
     t.setToX(orig); 
     } 
    } 
    if (doubleProperty.getName().equals("layoutY")) { 
     Double orig = node.getTranslateY(); 
     if (Double.compare(t.getFromY(), Double.NaN) == 0) { 
     t.setFromY(orig - changeValueDouble); 
     t.setToY(orig); 
     } 
    } 
    t.play(); 

    } 

    @Override 
    public void onChanged(ListChangeListener.Change change) { 
    while (change.next()) { 
     if (change.wasAdded()) { 
     for (Node node : (List<Node>) change.getAddedSubList()) { 
      this.observe(node); 
     } 
     } else if (change.wasRemoved()) { 
     for (Node node : (List<Node>) change.getRemoved()) { 
      this.unobserve(node); 
     } 
     } 
    } 
    } 
} 

Một Gist có sẵn ở đây để có thể đọc và đi kèm với một trường hợp thử nghiệm: https://gist.github.com/teyc/5668517

Trả lời

10

Đây là một ý tưởng thực sự gọn gàng. Tôi thích điều này. Bạn nên cân nhắc việc đóng góp trình quản lý bố cục mới của mình cho jfxtras.

Tại sao Solution Nguyên văn của bạn không hoạt động

Vấn đề của bạn là logic xung quanh cố gắng để ghi lại một giá trị ban đầu cho translateX giá trị/Y và kết thúc dịch của bạn chuyển tiếp tại đó giá trị ban đầu.

Khi dịch TranslateTransition đang diễn ra, nó sửa đổi giá trị dịchX/Y. Với mã hiện tại của bạn, nếu bạn thay đổi kích thước màn hình nhanh chóng trước khi hoạt ảnh của bạn kết thúc, bạn đặt thuộc tính toX/Y của TranslateTransition của bạn thành giá trị dịchX/Y hiện tại. Điều này làm cho nơi an nghỉ cuối cùng của một số điểm trung gian trước thay vì vị trí nghỉ cuối cùng mong muốn cho nút (vị trí mong muốn chỉ là điểm mà các giá trị dịchX/Y cho nút là 0).

Làm thế nào để Fix it

Việc sửa chữa rất đơn giản - Tox/Y cho TranslateTransition nên luôn luôn bằng không, vì vậy quá trình chuyển đổi luôn kết thúc lên dịch nút để bất cứ điều gì của vị trí bố trí hiện nay được coi là có không dịch delta.

Giải pháp Sample Code Snippet

Dưới đây là cốt lõi cập nhật mã chuyển:

TranslateTransition t; 
switch (doubleProperty.getName()) { 
    case "layoutX": 
    t = nodeXTransitions.get(node); 
    if (t == null) { 
     t = new TranslateTransition(Duration.millis(150), node); 
     t.setToX(0); 
     nodeXTransitions.put(node, t); 
    } 
    t.setFromX(node.getTranslateX() - delta); 
    node.setTranslateX(node.getTranslateX() - delta); 
    break; 

    default: // "layoutY" 
    t = nodeYTransitions.get(node); 
    if (t == null) { 
     t = new TranslateTransition(Duration.millis(150), node); 
     t.setToY(0); 
     nodeYTransitions.put(node, t); 
    } 
    t.setFromY(node.getTranslateY() - delta); 
    node.setTranslateY(node.getTranslateY() - delta); 
} 

t.playFromStart(); 

Giải pháp Sample Output

Output của phim hoạt hình được cập nhật sau khi thêm một vài hình chữ nhật hơn và thay đổi kích thước màn hình để buộc bố cục mới là (sử dụng công cụ kiểm tra mà bạn cung cấp trên gist):

layoutanimator

On Debugging Animations và Fixing Các vấn đề khác với luật mẫu của bạn

Trong hình ảnh động gỡ lỗi, tôi tìm thấy nó dễ dàng hơn nếu bạn làm chậm hình ảnh động. Để tìm hiểu cách khắc phục chương trình đã đăng, tôi đặt TranslateTransition thành giây để cho mắt đủ thời gian để xem điều gì đang thực sự xảy ra.

Làm chậm hoạt ảnh đã giúp tôi thấy rằng các hoạt ảnh thực tế do người nghe của bạn tạo ra dường như không diễn ra cho đến khi một khung hình sau khi nút được trả lại, tạo ra một lỗi ngắn trong đó nút sẽ xuất hiện trong mục tiêu vị trí, sau đó quay trở lại vị trí ban đầu của nó, sau đó từ từ animate đến vị trí mục tiêu.

Việc khắc phục trục trặc định vị ban đầu là dịch nút trong trình nghe trở lại vị trí ban đầu trước khi bắt đầu phát hoạt ảnh.

Mã của bạn sử dụng nút nodesĐịnh dạng bản đồ để theo dõi các nút đang chuyển đổi, nhưng nó không bao giờ đặt bất kỳ thứ gì trên bản đồ. Tôi đổi tên nó thành nodeXTransitions và nodeYTransitions (bởi vì tôi sử dụng các chuyển tiếp x và y riêng biệt hơn là một chuyển đổi đơn cho cả hai vì việc sử dụng các biến riêng biệt có vẻ dễ dàng hơn). Tôi đặt các nút chuyển tiếp vào bản đồ khi chúng được tạo ra để tôi có thể dừng quá trình chuyển đổi cũ khi tạo các chuyển đổi mới. Điều này dường như không cần thiết vì mọi thứ dường như hoạt động tốt mà không có logic bản đồ (có lẽ JavaFX đã làm một cái gì đó như thế này ngầm bên trong khung hoạt hình của nó), nhưng nó có vẻ như một điều an toàn để làm, vì vậy tôi giữ nó.

Sau khi tôi thực hiện các thay đổi được nêu chi tiết ở trên, mọi thứ dường như hoạt động OK.

cải thiện hơn nữa tiềm năng

Có lẽ một số cải tiến mà có thể được thực hiện cho thời gian của hình ảnh động để nếu một phim hoạt hình được phát một phần và một relayout xảy ra, có thể bạn không muốn chơi cả hoạt hình từ đầu cho relayout mới. Hoặc có lẽ bạn có thể muốn tất cả các nút hoạt hình di chuyển với tốc độ không đổi, giống hệt nhau hơn là trong một khoảng thời gian. Nhưng nhìn trực quan, điều đó dường như không quan trọng lắm, vì vậy tôi sẽ không thực sự lo lắng về nó.

Hệ thống thử nghiệm

tôi đã làm thử nghiệm của tôi trên Java8b91 và OS X 10.8.

Mã đầy đủ cho các cập nhật Solution

Toàn mã bố trí làm phim hoạt hình được cập nhật là:

import javafx.animation.TranslateTransition; 
import javafx.beans.property.DoubleProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 
import javafx.scene.Node; 
import javafx.util.Duration; 

import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 

/** 
* Animates an object when its position is changed. For instance, when 
* additional items are added to a Region, and the layout has changed, then the 
* layout animator makes the transition by sliding each item into its final 
* place. 
*/ 
public class LayoutAnimator implements ChangeListener<Number>, ListChangeListener<Node> { 

    private Map<Node, TranslateTransition> nodeXTransitions = new HashMap<>(); 
    private Map<Node, TranslateTransition> nodeYTransitions = new HashMap<>(); 

    /** 
    * Animates all the children of a Region. 
    * <code> 
    * VBox myVbox = new VBox(); 
    * LayoutAnimator animator = new LayoutAnimator(); 
    * animator.observe(myVbox.getChildren()); 
    * </code> 
    * 
    * @param nodes 
    */ 
    public void observe(ObservableList<Node> nodes) { 
    for (Node node : nodes) { 
     this.observe(node); 
    } 
    nodes.addListener(this); 
    } 

    public void unobserve(ObservableList<Node> nodes) { 
    nodes.removeListener(this); 
    } 

    public void observe(Node n) { 
    n.layoutXProperty().addListener(this); 
    n.layoutYProperty().addListener(this); 
    } 

    public void unobserve(Node n) { 
    n.layoutXProperty().removeListener(this); 
    n.layoutYProperty().removeListener(this); 
    } 

    @Override 
    public void changed(ObservableValue<? extends Number> ov, Number oldValue, Number newValue) { 
    final double delta = newValue.doubleValue() - oldValue.doubleValue(); 
    final DoubleProperty doubleProperty = (DoubleProperty) ov; 
    final Node node = (Node) doubleProperty.getBean(); 

    TranslateTransition t; 
    switch (doubleProperty.getName()) { 
     case "layoutX": 
     t = nodeXTransitions.get(node); 
     if (t == null) { 
      t = new TranslateTransition(Duration.millis(150), node); 
      t.setToX(0); 
      nodeXTransitions.put(node, t); 
     } 
     t.setFromX(node.getTranslateX() - delta); 
     node.setTranslateX(node.getTranslateX() - delta); 
     break; 

     default: // "layoutY" 
     t = nodeYTransitions.get(node); 
     if (t == null) { 
      t = new TranslateTransition(Duration.millis(150), node); 
      t.setToY(0); 
      nodeYTransitions.put(node, t); 
     } 
     t.setFromY(node.getTranslateY() - delta); 
     node.setTranslateY(node.getTranslateY() - delta); 
    } 

    t.playFromStart(); 
    } 

    @Override 
    public void onChanged(Change change) { 
    while (change.next()) { 
     if (change.wasAdded()) { 
     for (Node node : (List<Node>) change.getAddedSubList()) { 
      this.observe(node); 
     } 
     } else if (change.wasRemoved()) { 
     for (Node node : (List<Node>) change.getRemoved()) { 
      this.unobserve(node); 
     } 
     } 
    } 
    } 
} 

Ngày Làm Layout Animation thay đổi độc lập từ tài TranslateX/Y Cài đặt

Một nhược điểm của giải pháp như được trình bày hiện nay là nếu người dùng trình quản lý bố cục tùy chỉnh đã áp dụng trực tiếp các cài đặt dịchX/Y o các nút được đặt ra, sau đó chúng sẽ mất các giá trị đó vì trình quản lý bố cục chuyển tiếp tất cả nội dung (khi phiên dịch/Y kết thúc được thiết lập về 0).

Để bảo tồn dịch của người dùngX/Y, giải pháp có thể được cập nhật để sử dụng chuyển tiếp tùy chỉnh thay vì dịch chuyển tiếp. Các chuyển tiếp tùy chỉnh có thể được áp dụng cho các thuộc tính của một Translate đến các nút 'transform. Sau đó, chỉ có phép biến đổi bổ sung trên mỗi nút bị ảnh hưởng bởi hoạt động bên trong của hoạt ảnh bố cục - giá trị bản dịchX/Y ban đầu của người dùng không bị ảnh hưởng.

I forked the gist from your question to implement the custom transition enhancement mentioned above.


Nếu bạn quan tâm, bạn có thể xem qua openjfx code cho tiêu đề tab, TitledPanes và accordion và xem có bao da cho những người điều khiển xử lý các hình ảnh động của những thay đổi bố trí cho các nút con của họ.

+0

Đối với tính tổng quát, có thể bảo toàn các giá trị ban đầu của dịchX/Y không? Kiểm tra Animation.STATUS dường như không giúp ích gì. Tôi rất muốn làm sạch nó trước khi nó đi vào jfxtras. –

+0

Đã thêm phần "Ngày tạo bố cục thay đổi hoạt ảnh Biểu mẫu độc lập người dùng Cài đặt TranslateX/Y" để cung cấp ý tưởng để giữ nguyên giá trị ban đầu của dịchX/Y. – jewelsea

+0

Đã thêm [liên kết ngã ba] (https://gist.github.com/jewelsea/5683558) vào giải pháp triển khai "Thực hiện thay đổi hoạt ảnh bố cục độc lập với cài đặt TranslateX/Y của người dùng". – jewelsea

Các vấn đề liên quan