B4J Tutorial [BANano] v2: (1) B4J Abstract Designer

Discussion in 'B4J Tutorials' started by alwaysbusy, Jan 16, 2019.

  1. alwaysbusy

    alwaysbusy Expert Licensed User

    DOWNLOAD THE LATEST VERSION OF BANANO:

    https://www.b4x.com/android/forum/threads/banano-progressive-web-app-library.99740/#post-627764

    INTRODUCTION


    BANano v2 has a completely new (better) way of using the B4J Abstract Designer. Now, you can make Custom Views very easy that can be dragged and dropped in the Designer, with its own properties and events. Kudos to @Kiffi for helping me testing and evaluating v2!

    There are a couple of ways to use these Custom Views:

    1. by putting them on a layout and loading BANAno.LoadLayout
    2. by creating them in code
    3. NEW: by putting them on a layout and loading them with BANano.Layout

    In part 2 of this tutorial, I will cover all three of them. But for now we concentrate on building a Custom View.

    The best way to make a bunch of custom views (e.g. for wrapping a CSS library) is by creating a BANanoLibrary: it will keep your code cleaner.

    Unlike v1, BANano v2 needs to be a UI B4J project (we do want to use the Abstract Designer after all).

    The BANano download contains a wrapper library for the Skeleton CSS library. @tchart has written a wrapper for B4J already, but as this is a very small library with just some basic components, I asked him if he would mind me wrapping it again for learning purposes. Asking was the polite thing to do and he gave me the go-ahead. :)

    A demo using this BANanoLibrary can be seen here: http://gorgeousapps.com/BANano

    For this tutorial, we are going to recreate the SKTextbox component.

    GETTING STARTED

    Create a new B4J UI project if you haven't done that yet. Replace the Main source code with the BANano default project code:

    Code:
    #Region Project Attributes
       
    #MainFormWidth: 600
       
    #MainFormHeight: 600
       
    #IgnoreWarnings: 16, 10, 14, 15
    #End Region

    Sub Process_Globals
       
    Private BANano As BANano 'ignore
    End Sub

    Sub AppStart (Form1 As Form, Args() As String)
       
    ' you can change some output params here
       BANano.Initialize("BANano""BANanoSkeleton",12)
       BANano.HTML_NAME = 
    "index.html"
     
       BANano.Header.Title=
    "BANano Skeleton"
       BANano.ExternalTestConnectionServer = 
    "http://gorgeousapps.com"
       BANano.Header.AddCSSFile(
    "skeleton-all.min.css")
     
       
    ' start the build
       #if release
           BANano.BuildAsLibrary()
       #else
           BANano.Build(File.DirApp)
       
    #end if
     
       
    ExitApplication
    End Sub

    ' HERE STARTS YOUR APP
    Sub BANano_Ready()

    End Sub
    CREATING A CUSTOM VIEW

    1. Adding a new BANano Custom View

    [​IMG]

    NOTE:
    due to a current limitation of B4J, if you use Uppercase letters in the class name, the events will not show up in the IDE when typing Sub + TAB until you compile it as a library. When you use the library in a project, they will show.

    You will see quite some code is prepared for you. On top, the most common events in Javascript have been pre-defined. These are the events you want to expose in the Abstract Designer to the user.

    Next are some common properties like 'Classes', 'Style', 'Margins' and 'Padding'.

    Some basic code is also inserted. The initialize and DesignerCreateView method definitions can NOT be changed: you can not add or remove parameters to/from it! This is nothing new, as you can't do this for a normal B4J Custom component either.

    The DesignerCreateView method is where the magic happens, but I get back to this when we need to.

    2. Activating events

    You can just active an event by uncommenting them. If can also add events that are not in this list. They always have to be in format:

    Code:
    javascripteventname (event As BANanoEvent)
    For our textbox, let's activate the following events:
    Code:
    #Event: Focus (event As BANanoEvent)
    #Event: Blur (event As BANanoEvent)
    ...
    #Event: Keydown (event As BANanoEvent)
    #Event: KeyUp (event As BANanoEvent)
    ...
    #Event: Change (event As BANanoEvent)
    In DesignerCreateView, we have to attach those events:

    Code:
    mElement.HandleEvents("change", mCallBack, mEventName & "_change"' eventname must be lowercased!
    mElement.On("keydown", mCallBack, mEventName & "_keydown"' must be On because be do not want the default to be cancelled at the point of keydown
    mElement.HandleEvents("keyup", mCallBack, mEventName & "_keyup")
    mElement.HandleEvents(
    "blur", mCallBack, mEventName & "_blur")
    mElement.HandleEvents(
    "focus", mCallBack, mEventName & "_focus")
    Note we do use mCallBack here. This is because we want the class that initializes this component to be the one to handle our events.

    In some cases, you want to handle events that are not exposed to the user (like a hover). In this case use Me as the callback parameter.

    For example from the SKTable custom view:
    Code:
    Elems(0).HandleEvents("mouseenter", Me, "rowmouseenter"' eventname must be lowercased!
    Elems(0).HandleEvents("mouseleave", Me, "rowmouseleave")
    ...
    #Region Internal Events
    public Sub RowMouseEnter(event As BANanoEvent)
       
    Dim row As BANanoElement
       row.Initialize(
    "#" & event.ID)
       row.AddClass(
    "tablehover")
    End Sub

    public Sub RowMouseLeave(event As BANanoEvent)
       
    Dim row As BANanoElement
       row.Initialize(
    "#" & event.ID)
       row.RemoveClass(
    "tablehover")
    End Sub
    #End Region
    3. Adding properties

    We do want some custom properties on our TextBox:

    Code:
    #DesignerProperty: Key: Label, DisplayName: Label, FieldType: String, DefaultValue: , Description: Label of the TextBox.
    #DesignerProperty: Key: Width, DisplayName: Full Width, FieldType: Boolean, DefaultValue: True, Description: Set the width 100%
    #DesignerProperty: Key: Text, DisplayName: Text, FieldType: String, DefaultValue: , Description: Text of the TextBox
    #DesignerProperty: Key: Placeholder, DisplayName: Placeholder, FieldType: String, DefaultValue: , Description: Placeholder text
    #DesignerProperty: Key: Type, DisplayName: Type, FieldType: String, DefaultValue: text, List: text|email|password, Description: Type TextBox
    All B4J supported FieldTypes can be used: Int, Float, String, Color, Boolean...

    Add some variables to hold the properties:
    Code:
    Private mLabel As String = ""
    Private mWidth As Boolean = False
    Private mText As String = ""
    Private mPlaceHolder As String = ""
    Private mType As String = "text"
    Now, when we use this component in the Abstract Designer, we want the view to use the ones we have set in the desinger. This is done by getting them from the Props map (Case does matter!).

    Code:
    If Props <> Null Then
           ...
           mLabel = Props.Get(
    "Label")
           mWidth = Props.Get(
    "Width")
           mText = Props.Get(
    "Text")
           mPlaceHolder = Props.Get(
    "Placeholder")
           mType = Props.Get(
    "Type")
    End If
    In case you want to be able to set these properties in code too e.g. to change the labels text, we create getters and setters. The method name must start with get/set (Lowercased!).

    Code:
    public Sub setLabel(label As String)
       
    If mElementLabel <> Null Then
           mElementLabel.SetHTML(BANano.SF(
    label))
       
    End If
       mLabel = 
    label
    End Sub

    public Sub getLabel() As String
       
    Return mLabel
    End Sub
    4. Building the view with HTML.

    In Skeleton CSS, a textbox with a label needs to have a certain structure:

    Code:
    <label for="txt1">Text box</label>
    <input placeholder=
    "Enter some text" type="text">
    This is simple to do in BANano thanks to B4Js smartstrings:
    Code:
    ' preparing some variables
    Dim exStyle As String = BuildExStyle
     
    Dim extraWidth As String
    If mWidth Then
       extraWidth = 
    "u-full-width"
    End If

    ' building the html
    Dim ElemsLabel() As BANanoElement = mTarget.Append($"<label id="${mName}label" for="${mName}">${BANano.SF(mLabel)}</label>"$).Children("#" & mName & "label")
    mElementLabel = ElemsLabel(
    0)

    Dim Elems() As BANanoElement = mTarget.Append($"<input id="${mName}" class="${extraWidth} ${mClasses}" style="${exStyle}${mStyle}" placeholder="${mPlaceHolder}" type=${mType}>"$).Children("#" & mName)
    mElement = Elems(
    0)
    mElement.SetValue(mText)
    One can keep a reference of the created parts (mElementLabel and mElement), but in some cases you can just grab it when needed. So this could also be written as:

    Code:
    mTarget.Append($"<label id="${mName}label" for="${mName}">${BANano.SF(mLabel)}</label>
                   <input id="$
    {mName}" class="${extraWidth} ${mClasses}" style="${exStyle}${mStyle}" placeholder="${mPlaceHolder}" type=${mType}>"$)
    mElementLabel.Initialize(
    "#" & mName & "label")
    mElement.Initialize(
    "#" & mName)
    mElement.SetValue(mText)
    It is a personal preference I guess.

    Et voila, our View is finished: yes that is it! :)
    Check out the source code of the Skeleton library in the download to see the whole class.

    In part 2 of this tutorial, I'll show you how to use such a view and make a BANano library out of it.

    Continue to part 2: https://www.b4x.com/android/forum/threads/banano-v2-b4j-abstract-designer-part-2.101544/

    Alwaysbusy
     
    Last edited: Jan 16, 2019
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice