本文是仿微信聊天程序专栏的第三篇文章,主要记录了【登录界面】的实现。
界面设计
仿微信聊天程序的登录界面跟注册界面差不多,只是比注册界面少了一个昵称输入框,如下图所示:
界面布局
登录界面的界面布局和注册界面的布局差不多,也是使用fxml,采用VBox从上到下布局,中间的表单使用formsfx
,预留一个StackPane给表单。
<VBox alignment="CENTER" prefHeight="300.0" prefWidth="480.0" spacing="10.0"
stylesheets="@/css/bootstrapfx.css"
xmlns:fx="http://javafx.com/fxml" fx:controller="michong.javafx.wx.view.login.LoginController">
<children>
<HBox alignment="CENTER_LEFT">
<children>
<Label text="登录" styleClass="h3,b"/>
<Pane HBox.hgrow="ALWAYS"/>
<Hyperlink text="注册" onAction="#onRegisterClick"/>
</children>
<padding>
<Insets left="20.0" right="20.0"/>
</padding>
</HBox>
<StackPane fx:id="formStackPane"/>
<Button minWidth="120" text="登录" styleClass="btn-primary" onAction="#onLoginClick"/>
</children>
<padding>
<Insets bottom="30.0" left="30.0" right="30.0" top="5.0"/>
</padding>
</VBox>
表单构建
同样的表单构建也跟注册界面一样,只不过少了一个昵称的输入框:
/**
* @author michong
*/
public class LoginForm {
private final LoginVO vo = new LoginVO();
private Form form;
private FormRenderer renderer;
private LoginForm() {
}
public static LoginForm getInstance() {
LoginForm rf = new LoginForm();
rf.build();
return rf;
}
private void build() {
// @formatter:off
String username = "账号";
String password = "密码";
String placeholderTextFmt = "请输入%s";
String requiredTextFmt = "%s必填";
form = Form.of(
Group.of(
Field.ofStringType(vo.usernameProperty())
.label(username)
.placeholder(String.format(placeholderTextFmt, username))
.required(String.format(requiredTextFmt, username)),
PasswordField.ofPasswordType(vo.passwordProperty())
.label(password)
.placeholder(String.format(placeholderTextFmt, password))
.required(String.format(requiredTextFmt, password))
)
);
// @formatter:on
renderer = new FormRenderer(form);
}
}
事件处理
事件处理这里,要跟注册界面的跳转关联起来,在注册界面点击登录时,切换成登录界面,而在登录界面点击注册时,切换成注册界面:
public class LoginController implements Initializable {
public StackPane formStackPane;
private LoginForm form;
@Override
public void initialize(URL location, ResourceBundle resources) {
initializeUI();
initializeEvent();
}
void initializeUI() {
form = LoginForm.getInstance();
formStackPane.getChildren().add(form.getRenderer());
}
void initializeEvent() {
}
public void onRegisterClick(ActionEvent actionEvent) {
formStackPane.getScene().setRoot(FXComponent.register());
}
public void onLoginClick(ActionEvent actionEvent) {
form.getForm().persist();
if (form.getForm().isValid()) {
// FX.info(form.getVO().getUsername() + "登录成功", FXContext.getLoginStage());
FXContext.getLoginStage().close();
FXContext.getPrimaryStage().show();
}
}
}
更新注册页面点击登录的处理以及点击注册的处理:
public void onLoginClick(ActionEvent actionEvent) {
formStackPane.getScene().setRoot(FXComponent.login());
}
public void onRegisterClick(ActionEvent actionEvent) {
form.getForm().persist();
if (form.getForm().isValid()) {
FX.info(form.getVO().getUsername() + "注册成功", FXContext.getLoginStage());
onLoginClick(actionEvent);
}
}
登录窗口
登录和注册使用独立的窗口,登录成功后再显示住窗口,定制登录窗口:
public class LoginStage extends Stage {
public static LoginStage build() {
LoginStage stage = new LoginStage();
stage.setTitle("米虫IM");
stage.setScene(new Scene(FXComponent.login()));
if (Platform.isSupported(ConditionalFeature.EFFECT)) {
stage.initStyle(StageStyle.UNIFIED);
}
stage.getIcons().clear();
stage.getIcons().add(FXIcon.logo());
stage.setResizable(false);
stage.setWidth(450);
stage.setHeight(350);
stage.initOwner(FXContext.getPrimaryStage());
return stage;
}
}
启动流程
调整启动流程,启动时显示登录窗口,不直接显示主窗口:
public class AppStarter {
public static void start(Stage primaryStage) {
FXContext.setPrimaryStage(primaryStage);
BorderPane root = new BorderPane();
root.setCenter(new Label("米虫IM"));
primaryStage.setScene(new Scene(root));
primaryStage.getIcons().clear();
primaryStage.getIcons().add(FXIcon.logo());
primaryStage.setResizable(false);
primaryStage.setWidth(640);
primaryStage.setHeight(480);
LoginStage loginStage = LoginStage.build();
FXContext.setLoginStage(loginStage);
loginStage.show();
}
}