2015-04-28 31 views
5

Làm cách nào để làm cho hộp thoại javaFX/8 của tôi rung động thanh lịch hơn bất cứ khi nào người dùng nhập một cặp tên/mật khẩu đăng nhập sai ?.cách lắc hộp thoại đăng nhập bằng javaFX/8

Vì hộp thoại trong java8u40 KHÔNG có, tôi đặt ra để tự làm cho nó. Tuy nhiên, nó không đủ tốt.

Có vấn đề gì với nó? Ai đó có thể giúp đỡ? Có cách nào tốt hơn để làm điều đó không?


public void loginDialog() { 
    // Create the custom dialog. 
    Dialog<Pair<String, String>> dialog = new Dialog<>(); 
    dialog.setTitle("Mars Simulation Project"); 
    dialog.setHeaderText("Log in"); 
    dialog.setContentText("Enter your username and password : "); 
    dialog.initModality(Modality.NONE); 
    // Set the button types. 
    ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE); 
    dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL); 

    // Create the username and password labels and fields. 
    GridPane grid = new GridPane(); 
    grid.setHgap(10); 
    grid.setVgap(10); 
    grid.setPadding(new Insets(20, 150, 10, 10)); 

    TextField tfPlayer = new TextField(); 
    tfPlayer.setPromptText("e.g. m03j"); 
    PasswordField tfPassword = new PasswordField(); 
    tfPassword.setPromptText("xxxx"); 

    Button defaultPWB = new Button("Use Default"); 
    Button guestB = new Button("As Guest"); 

    defaultPWB.setOnAction(event -> { 
     tfPassword.setText("msp0"); 
    }); 

    guestB.setOnAction(event -> { 
     tfPlayer.setText("Guest_"); 
     tfPassword.setText("msp0"); 
    }); 

    grid.add(new Label("Player Name :"), 0, 0); 
    grid.add(tfPlayer, 1, 0); 
    grid.add(guestB, 2, 0); 
    grid.add(new Label("Password :"), 0, 1); 
    grid.add(tfPassword, 1, 1); 
    grid.add(defaultPWB, 2, 1); 

    // Enable/Disable login button depending on whether a username was entered. 
    Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType); 
    loginButton.setDisable(true); 

    // Do some validation (using the Java 8 lambda syntax). 
    tfPlayer.textProperty().addListener((observable, oldValue, newValue) -> { 
     loginButton.setDisable(newValue.trim().isEmpty()); 
    }); 

    dialog.getDialogPane().setContent(grid); 

    // Request focus on the player name field by default. 
    Platform.runLater(() -> tfPlayer.requestFocus()); 

    // Convert the result to a player name /host address pair when the login 
    // button is clicked. 
    dialog.setResultConverter(dialogButton -> { 
     if (dialogButton == loginButtonType) { 
      return new Pair<>(tfPlayer.getText(), tfPassword.getText()); 
     } 
     return null; 
    }); 

    Optional<Pair<String, String>> result = dialog.showAndWait(); 

    result.ifPresent(input -> { 
     playerName = tfPlayer.getText(); 
     logger.info("Player " + input.getKey() + " connecting to server at " + serverAddressStr); 

     try { 
      dialog.show(); 
      makeContact(serverAddressStr); 
      // obtain a client id 
      boolean isSuccessful = sendRegister(); 

      if (isSuccessful) { 
       dialog.close(); 
       // establish chat... 

      } else { 
       // shake the dialog or send an alert to inform the user the 
       // player name is NOT valid 
       DialogEarthquakeCenter dec = new DialogEarthquakeCenter(dialog); 
       dec.startTimer(); 

       try { 
        System.out.println("start sleeping "); 
        Thread.sleep(2000); 
        System.out.println("done sleeping "); 
       } 
       catch (InterruptedException e) {} 

       loginDialog(); 
      } 

     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    }); 

Cho đến nay, vấn đề của tôi là ngay sau khi tôi nhấn nút "đăng nhập", hộp thoại sẽ đóng theo mặc định.

Vì vậy, tôi phải sử dụng dialog.show() để hiển thị lại.

[sửa] Điều này, tuy nhiên, vẫn không thể ngăn chặn khoảng cách tạm thời xảy ra (nhìn thấy hộp thoại biến mất và xuất hiện lại).

Sau đó, tôi tạo một thể hiện của DialogEarthquakeCenter để lắc hộp thoại.

Lưu ý rằng DialogEarthquakeCenter của tôi dưới đây là một sự sửa đổi trực tiếp của ban này:

https://github.com/gigiigig/Java-Chat/blob/master/tag/FacebookChatCore_Original/src/facebookchat/ui/common/DialogEarthquakeCenter.java

import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import javax.swing.Timer; 
import javafx.animation.KeyFrame; 
import javafx.animation.Timeline; 
import javafx.application.Platform; 
import javafx.scene.control.Dialog; 
import javafx.util.Duration; 
import javafx.util.Pair; 

public class DialogEarthquakeCenter { 

public static final int SHAKE_DISTANCE = 10; 
public static final double SHAKE_CYCLE = 50; 
public static final int SHAKE_DURATION = 500; 
public static final int SHAKE_UPDATE = 5; 

private Dialog<Pair<String, String>> dialog; 
private int x, y; 
private long startTime; 
private Timer shakeTimer; 
private final double TWO_PI = Math.PI * 2.0; 
private Timeline timeline; 

public DialogEarthquakeCenter(Dialog<Pair<String, String>> parent) { 
    dialog = parent; 
} 

/** 
* Creates and starts the timer 
* 
* @return Scene 
*/ 
public void startTimer() { 
    x = (int) dialog.getX(); 
    y = (int) dialog.getY(); 
    startTime = System.currentTimeMillis(); 
    // Set up earth time text update 
    timeline = new Timeline(new KeyFrame(Duration.millis(SHAKE_DURATION), ae -> startNudging())); 
    //timeline.setCycleCount(javafx.animation.Animation.INDEFINITE); 
    timeline.play(); 
} 

public void startNudging() { 
    x = (int) dialog.getX(); 
    y = (int) dialog.getY(); 
    startTime = System.currentTimeMillis(); 
    shakeTimer = new Timer(SHAKE_UPDATE, new ActionListener() { 
     public void actionPerformed(ActionEvent e) { 
      shake(); 
     } 
    }); 

    shakeTimer.start(); 
} 

public void shake() { 
    // calculate elapsed time 
    long elapsed = System.currentTimeMillis() - startTime; 
    //System.out.println("elapsed is " + elapsed); 
    // use sin to calculate an x-offset 
    double waveOffset = (elapsed % SHAKE_CYCLE)/SHAKE_CYCLE; 
    double angle = waveOffset * TWO_PI; 
    // offset the x-location by an amount 
    // proportional to the sine, up to shake_distance 
    int shakenX = (int) ((Math.sin(angle) * SHAKE_DISTANCE) + x); 

    Platform.runLater(() -> { 
     //dialog.hide(); 
     dialog.setX(shakenX); 
     //System.out.println("set shakenX to " + shakenX); 
     dialog.setY(y); 
     dialog.show(); 
    }); 

    //try {Thread.sleep(20);} 
    //catch (InterruptedException ex) {} 

    // should we stop timer 
    if (elapsed >= SHAKE_DURATION) { 
     stopShake(); 

    } 
} 

public void stopShake() { 
    shakeTimer.stop(); 
    Platform.runLater(() -> { 
     timeline.stop(); 
     dialog.close(); 
    }); 
} 
} 

tôi đã thông báo rằng controlsfx thoại có một phương pháp rung().

Có ai biết nó hoạt động tốt không?

xem https://code.google.com/p/mqtt-spy/source/browse/mqtt-spy/src/main/java/org/controlsfx/dialog/CustomDialogs.java?r=6ec0240e4e64d1b8cc2b59bc77cd5902a68e0c81

Cảm ơn bạn đã bình luận!

+1

Bạn nên thử hộp thoại ControlsFX tự lắc và xem nó có hoạt động tốt cho bạn không. Bạn cũng có thể xem [hoạt ảnh đóng hộp từ fxexperience] (http://fxexperience.com/2012/03/canned-animations/) - mặc dù đó là nút dựa trên và bạn sẽ phải điều chỉnh chúng để làm việc với một giai đoạn . – jewelsea

+1

Không bao giờ gọi Thread.sleep() trên chuỗi ứng dụng JavaFX, thay vào đó hãy sử dụng PauseTransition. – jewelsea

+0

ok. hộp thoại controlsfx sẽ không được dùng nữa và đó là lý do tại sao tôi không sử dụng nó. nhưng nó dons trên tôi mà tôi thực sự có thể tìm kiếm cách họ mã lắc(). sẽ xem hoạt hình đóng hộp. Và thx đã nhắc tôi chuyển sang PauseTransition! – mk7

Trả lời

5

Có cách bạn có thể thêm chuyển đổi khi người dùng nhấp vào nút đăng nhập bằng API Dialog trước khi cửa sổ đóng.

Sử dụng dialog.show() thay vì dialog.showAndWait() `, mẹo chỉ là bẫy hành động nhấp vào nút, tiêu thụ sự kiện và sau đó thực hiện logic bắt buộc.

dialog.initModality(Modality.APPLICATION_MODAL); 
dialog.show();    

loginButton.addEventFilter(EventType.ROOT, 
    e->{ 
     if(e.getEventType().equals(ActionEvent.ACTION)){     
      e.consume(); 
      // (hardcoded) Login Validation 
      boolean isSuccessful = false; 
      if (isSuccessful) { 
       dialog.close(); 
      } 
      else { 
       // perform animation and close the dialog (or any other action) 
       ShakeTransition anim = new ShakeTransition(dialog.getDialogPane(), t->dialog.close()); 
       anim.playFromStart(); 
      } 
     } 
    }); 

Đối với các hình ảnh động lắc, tôi đã sửa đổi ShakeTransition từ Jasper Potts, để di chuyển cửa sổ hộp thoại, như @jewelsea đã chỉ ra:

/** 
* Animate a shake effect on the given node 
* 
* Based on CachedTimelineTransition, a Transition that uses a Timeline internally 
* and turns SPEED caching on for the animated node during the animation. 
* 
* https://github.com/fxexperience/code/blob/master/FXExperienceControls/src/com/fxexperience/javafx/animation/CachedTimelineTransition.java 
* 
* and ShakeTransition 
* 
* https://github.com/fxexperience/code/blob/master/FXExperienceControls/src/com/fxexperience/javafx/animation/ShakeTransition.java 
* 
* @author Jasper Potts 
*/ 
class ShakeTransition extends Transition { 

    private final Interpolator WEB_EASE = Interpolator.SPLINE(0.25, 0.1, 0.25, 1); 
    private final Timeline timeline; 
    private final Node node; 
    private boolean oldCache = false; 
    private CacheHint oldCacheHint = CacheHint.DEFAULT; 
    private final boolean useCache=true; 
    private final double xIni; 

    private final DoubleProperty x = new SimpleDoubleProperty(); 

    /** 
    * Create new ShakeTransition 
    * 
    * @param node The node to affect 
    */ 
    public ShakeTransition(final Node node, EventHandler<ActionEvent> event) { 
     this.node=node; 
     statusProperty().addListener((ov, t, newStatus) -> { 
      switch(newStatus) { 
       case RUNNING: 
        starting(); 
        break; 
       default: 
        stopping(); 
        break; 
      } 
     }); 

     this.timeline= new Timeline(
       new KeyFrame(Duration.millis(0), new KeyValue(x, 0, WEB_EASE)), 
       new KeyFrame(Duration.millis(100), new KeyValue(x, -10, WEB_EASE)), 
       new KeyFrame(Duration.millis(200), new KeyValue(x, 10, WEB_EASE)), 
       new KeyFrame(Duration.millis(300), new KeyValue(x, -10, WEB_EASE)), 
       new KeyFrame(Duration.millis(400), new KeyValue(x, 10, WEB_EASE)), 
       new KeyFrame(Duration.millis(500), new KeyValue(x, -10, WEB_EASE)), 
       new KeyFrame(Duration.millis(600), new KeyValue(x, 10, WEB_EASE)), 
       new KeyFrame(Duration.millis(700), new KeyValue(x, -10, WEB_EASE)), 
       new KeyFrame(Duration.millis(800), new KeyValue(x, 10, WEB_EASE)), 
       new KeyFrame(Duration.millis(900), new KeyValue(x, -10, WEB_EASE)), 
       new KeyFrame(Duration.millis(1000), new KeyValue(x, 0, WEB_EASE)) 
      ); 
     xIni=node.getScene().getWindow().getX(); 
     x.addListener((ob,n,n1)->(node.getScene().getWindow()).setX(xIni+n1.doubleValue())); 

     setCycleDuration(Duration.seconds(1)); 
     setDelay(Duration.seconds(0.2)); 
     setOnFinished(event); 
    } 

    /** 
    * Called when the animation is starting 
    */ 
    protected final void starting() { 
     if (useCache) { 
      oldCache = node.isCache(); 
      oldCacheHint = node.getCacheHint(); 
      node.setCache(true); 
      node.setCacheHint(CacheHint.SPEED); 
     } 
    } 

    /** 
    * Called when the animation is stopping 
    */ 
    protected final void stopping() { 
     if (useCache) { 
      node.setCache(oldCache); 
      node.setCacheHint(oldCacheHint); 
     } 
    } 

    @Override 
    protected void interpolate(double d) { 
     timeline.playFrom(Duration.seconds(d)); 
     timeline.stop(); 
    } 
} 

Và đây sẽ là một ứng dụng JavaFX sử dụng hộp thoại đăng nhập của bạn:

@Override 
public void start(Stage primaryStage) { 
    Button btn = new Button(); 
    btn.setText("Show Login Dialog"); 
    btn.setOnAction(mevent -> { 

     // Create the custom dialog. 
     Dialog<Pair<String, String>> dialog = new Dialog<>(); 
     dialog.setTitle("Mars Simulation Project"); 
     dialog.setHeaderText("Log in"); 
     dialog.setContentText("Enter your username and password : "); 
     dialog.initModality(Modality.NONE); 
     // Set the button types. 
     ButtonType loginButtonType = new ButtonType("Login", ButtonData.OK_DONE); 
     dialog.getDialogPane().getButtonTypes().addAll(loginButtonType, ButtonType.CANCEL); 

     // Create the username and password labels and fields. 
     GridPane grid = new GridPane(); 
     grid.setHgap(10); 
     grid.setVgap(10); 
     grid.setPadding(new Insets(20, 150, 10, 10)); 

     TextField tfPlayer = new TextField(); 
     tfPlayer.setPromptText("e.g. m03j"); 
     PasswordField tfPassword = new PasswordField(); 
     tfPassword.setPromptText("xxxx"); 

     Button defaultPWB = new Button("Use Default"); 
     Button guestB = new Button("As Guest"); 
     defaultPWB.setOnAction(event -> { 
      tfPassword.setText("msp0"); 
     }); 

     guestB.setOnAction(event -> { 
      tfPlayer.setText("Guest_"); 
      tfPassword.setText("msp0"); 
     }); 

     grid.add(new Label("Player Name :"), 0, 0); 
     grid.add(tfPlayer, 1, 0); 
     grid.add(guestB, 2, 0); 
     grid.add(new Label("Password :"), 0, 1); 
     grid.add(tfPassword, 1, 1); 
     grid.add(defaultPWB, 2, 1); 

     // Enable/Disable login button depending on whether a username was entered. 
     Node loginButton = dialog.getDialogPane().lookupButton(loginButtonType); 
     loginButton.setDisable(true); 

     // Do some validation (using the Java 8 lambda syntax). 
     tfPlayer.textProperty().addListener((observable, oldValue, newValue) -> { 
      loginButton.setDisable(newValue.trim().isEmpty()); 
     }); 

     dialog.getDialogPane().setContent(grid); 

     // Request focus on the player name field by default. 
     Platform.runLater(() -> tfPlayer.requestFocus()); 

     dialog.initModality(Modality.APPLICATION_MODAL); 
     dialog.show();    

     loginButton.addEventFilter(EventType.ROOT, 
      e->{ 
       if(e.getEventType().equals(ActionEvent.ACTION)){ 
        e.consume(); 
        // (hardcoded) Login Validation 
        boolean isSuccessful = false; 
        if (isSuccessful) { 
         dialog.close(); 
        } 
        else { 
         ShakeTransition anim = new ShakeTransition(dialog.getDialogPane(), t->dialog.close()); 
         anim.playFromStart(); 
        } 
       } 
      }); 
    }); 

    StackPane root = new StackPane(); 
    root.getChildren().add(btn); 

    Scene scene = new Scene(root, 300, 250); 

    primaryStage.setTitle("Shaky Login Dialog"); 
    primaryStage.setScene(scene); 
    primaryStage.show(); 
} 
+0

thx cho ShakeTransition được sửa đổi của bạn.Nó lắc tốt hơn rất nhiều! có, tôi nên có nhìn vào bẫy nút bấm thay vì dựa vào cách mặc định của việc sử dụng tùy chọn <> và showAndWait()! – mk7

+0

Jose, Thay đổi duy nhất tôi cần thực hiện ở trên là sử dụng t-> tfPlayer.requestFocus(), intead của t-> dialog.close(). Nếu tên đăng nhập sai/pd đã được nhập, bây giờ tôi không phải gọi loginDialog của tôi() đệ quy một lần nữa ngay bên dưới dòng mã: anim.playFromStart(). Cảm ơn một lần nữa vì đã làm cho nó hoạt động tốt hơn rất nhiều ngay bây giờ! – mk7

+0

Có, tôi đã thêm 'EventHandler' vào hoạt ảnh để cho phép một số hành động trong sự kiện đã kết thúc. Thay vì đóng hộp thoại, bạn có thể thêm bất kỳ hành động nào khác. –

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