Managing multiple scenes in JavaFx

Although JavaFx scenes are different than Java panels, they both can be used for displaying what needs to be shown in the screen at run time. Suppose that we  have a login screen that is displayed when the application runs, and after the user submits  the user name and password lets say the application directs the user to the respective page according to the user credential. Learning how this works was quite a handful and one of the approach that I found was interesting and useful was by using abstract class. Keep in mind that this can be done other ways as well.  Here, I am introducing a method using abstract class to navigate to different pages that needs to be displayed.

Lets start with the abstract class. This class will be extended by all other pages or screens that needs to be displayed.


public abstract class View extends CustomNode
{
  protected var currentView: Node; //holds the value of the current page to be  displayed
  protected var controller:Controller;//business logic to decide what to display

  protected abstract function createView():Void;

  override function create(): Node
  {
     createView();
     return currentView
  }
}

The abstract class extends CustomNode and overrides the function create()
to return the current node of interest. the currentView holds the node
that needs to be displayed and the controller is of type Controller class
which I will introduce now. Basically what the controller class does is
decide on what page needs to be displayed and then forwards the page to the
scene to be displayed.

public class Controller
{
    public var contents: Node;
    public var loginForm: Login; //holds login page
    public var adminScreen: AdminPage; //holds admin page
    public var guestScreen: GuestPage;//holds guest page

    public function showLoginScreen()
    {
       if(loginForm == null)
       {
         loginForm = Login
         {
           controller: this;
         }
       }

       contents = loginForm;
    }

    public function showAdminPage()
    {
        if(adminScreen == null)
        {
            adminScreen = AdminPage
            {
                controller: this;
            }
        }
        contents= adminScreen;
    }

    public function showGuestPage()
    {
        if(guestScreen == null)
        {
            guestScreen = GuestPage
            {
               controller: this;
            }
        }
        contents = guestScreen;
    }
}

Now that the hard part is over, lets introduce the Main.fx.


var controller = new Controller;

var stage: Stage = Stage
{
   title: "Demo"
   scene: Scene
   {
      width: 800
      height: 800

      content: bind
      [
         controller.contents
      ]
   }
}

The content of the stage is bound to the contents of the controller. So,
everytime the content of the controller changes the scene is updated with the
new content.

Here is a simple code for login.fx

public class Login extends View
{
   var userNameLbl = Label
   {
      text:"User Name:"
   }

   var passwordLbl = Label
   {
      text:"Password:"
   }

   var userNameTxt = TextBox
   {
      columns:10
   }

   var passwordTxt = PasswordBox
   {
      columns:10
   }

   var submitBtn = Button
   {
      action: function()
      {
         if((userNameTxt.text.compareTo("admin") == 0) and (passwordTxt.text.compareTo("admin") == 0))
         {
             controller.showAdminPage();
         }
         else
         {
             controller.showGuestPage();
         }
      }
      text:"Login"
   }

   var loginBox:VBox = VBox
   {
      spacing: 5
      nodeHPos: HPos.RIGHT

      content:
      [
          HBox
          {
              nodeVPos: VPos.BOTTOM //bottom aligning the nodes

              spacing: 2 //spacing between the two corresponding nodes

             content:
             [
               userNameLbl,
               userNameTxt
             ]
          }

          HBox
          {
             hpos: HPos.RIGHT //right align within the containers width
             nodeVPos: VPos.BOTTOM //bottom align the nodes
             spacing: 2 //spacing between the two corresponding nodes
             content:
             [
                passwordLbl,
                passwordTxt,

             ]
          }

          submitBtn
       ]
    }

    //must override this function
    override function createView():Void
    {
       currentView = loginBox;
    }

}

Here a simple login form is created and the class extends the abstract class
view. So, when the create() method (called automatically) of the View is called
the createView() method of this class is called which will assign the loginBox to currentView.

Similarly we create AdminPage class and the GuestPage class. Both extends the view class


public class AdminPage extends View
{
   /*
     write the code for what needs to be displayed in admin page
     For example: adminPageView.AdminPageView can be any node i.e.
     Group, Image, label, HBox, etc.
   */
   override function  createView():Void
   {
      currentView = adminPageView;
   }
}

public class GuestPage extends View
{
    /*
      write the code for what needs to be displayed in guest page
      For example: guestPageView. GuestPageView can be any node i.e.
       Group, Image, label, HBox, etc.
     */
    override function createView():Void
    {
       currentView = guestPageView
    }
}
Advertisements