Android Tutorial DialogView: A complete walk-through guide

Introduction

Since we discovered b4a I looked for a flexible and developer friendly dialog library with an extensive and versatile object model. Unfortunately I could not find anything that could satisfy our development requirements so I decided to write one.

This tutorial is available in the latest XtraViews package and as standalone APK

Part 1 Creating a custom login dialog

DialogViews are based on layouts that are build with the b4a integrated dialog designer. First of all, design the layout like you do with any other activity layout. Start with dropping a panel and then drop all needed controls inside that panel. In our case, 2 textedit controls. Set the appropriate anchors save the layout into the "dialog1" file and move on to the code.

upload_2014-7-20_2-14-9.png
upload_2014-7-20_2-15-33.png


Let's instantiate our DialogView in our Sub Globals:
B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim Dialog As DialogView
End Sub
In our button click handler, load the layout in the dialog and show it.
B4X:
Dialog.LoadLayout("dialog1").ShowYesNo("Login", "proceed", "later")
Note that dialog1 is the name of the layout file. The engine will open the layout, extract the first panel that it will find and will generate our dialog with the views inside. The ShowYesNo method will show the dialog with a system title and 2 buttons.


Part 2 Accessing views in the dialog

Now that we have our barebone dialog, we can proceed with accessing its views. Lets add a label in our layout and change it's text and color in code. In order to do this we have to break our single function call into several parts.

First, create a DialogViewLayout object using our global DialogView instance and load our layout.
B4X:
Dim DialogLayout As DialogViewLayout
DialogLayout = Dialog.LoadLayout("dialog2")
Then, get a handle of the label we want to access
B4X:
Dim Label1 As Label = DialogLayout.Views.Get("label1")
Set the label properties
B4X:
Label1.Text = "Please login below:"
Label1.TextColor = Colors.Red
Finally, call our ShowYesNo method
B4X:
Dim Result As Int = DialogLayout.ShowYesNo("Login", "proceed", "later")
Main.Toast.Show("Result = " & Result)

One powerful feature of the DialogView engine are the events that will be raised before the dialog is shown and before the dialog is dismissed.
B4X:
Sub Dialog2_Show
    'This event will be raised just before the dialog is closed passing the DialogResult
    Main.Toast.Show(Dialog.Name & " is shown")
End Sub

Sub Dialog2_Dismiss (DialogResult As Int) As Boolean
    'This event will be raised just before the dialog is closed passing the DialogResult
    'Return true to dismiss or false to cancel
    Return True
End Sub


Part 3 Advanced event handling

Now that we know how to access views in our layout, let's go a step further and do something more complicated.

upload_2014-7-20_4-8-4.png
upload_2014-7-20_4-8-58.png


Lets add a Button that we will use to clear the fields. When ever the text changes, our code will enable or disable the clear button accordingly.

Now, let's write the code that will enable/disable the clear button
B4X:
Sub SetBtnClearState
    'Get the EdUserName and EdPassword from the layout
    Dim EdUserName As Label = Dialog.Views.Get("EdUserName")
    Dim EdPassword As Label = Dialog.Views.Get("EdPassword")

    'Get the BtnClear from the layout
    Dim BtnClear As Button = Dialog.Views.Get("BtnClear")

    'Enable or disable the BtnClear according to user input
    BtnClear.Enabled = EdUserName.Text <> "" OR EdPassword.Text <> ""
End Sub
Generate the TextChanged event from the layout designer for both textedits and call the previous function.
B4X:
Sub EdElement_TextChanged (Old As String, New As String)
    'Use CallSubDelayed in order to avoid deadlocks
    CallSubDelayed(Me, "SetBtnClearState")
End Sub
Generate the Click handler for the BtnClear button from the layout designer
B4X:
Sub BtnClear_Click
    'Get the EdUsername from the layout AND clear it
    Dim EdUserName As Label = Dialog.Views.Get("EdUserName")
    EdUserName.Text = ""

    'Get the EdPassword from the layout and clear it
    Dim EdPassword As Label = Dialog.Views.Get("EdPassword")
    EdPassword.Text = ""
End Sub
In our Dialog3_Dismiss event we cancel the dialog dismiss if the user has clicked the "proceed" button and the textedits are not filled in.
B4X:
Sub Dialog3_Dismiss (DialogResult As Int) As Boolean
    'This event will be raised just before the dialog is closed passing the DialogResult
    'Return true to dismiss or false to cancel

    Dim AllowDismiss As Boolean

    If DialogResult = DialogResponse.POSITIVE Then
        'Get the edit views contents
        Dim EdUserName As Label = Dialog.Views.Get("EdUserName")
        Dim EdPassword As Label = Dialog.Views.Get"EdPassword")

        'if any of them is empty return false
        AllowDismiss = EdUserName.Text <> "" AND EdPassword.Text <> ""
    Else
        AllowDismiss = True
    End If

    If Not(AllowDismiss) Then Main.Toast.Show("all fields are required!")

    Return AllowDismiss
End Sub
Finally, we show the dialog and with the Msgbox function of the DialogView the user input.
B4X:
Dim Result As Int = DialogLayout.ShowYesNo("Login", "proceed", "later")
If Result = DialogResponse.POSITIVE Then
    Dim EdUserName As Label = Dialog.Views.Get("EdUserName")
    Dim EdPassword As Label = Dialog.Views.Get("EdPassword")

    Dialog.Msgbox("DialogView", "<b>Username:</b> " & EdUserName.Text & "<br/><br/>" & "<b>Password:</b> " & EdPassword.Text, "done", "", "", LoadBitmap(File.DirAssets, "icon1.png"))
End If


Part 4 Listview and layout per orientation

Now, lets make it a little bit more complex. Let's create 2 different layouts containing some buttons and a listview. One for portrait and one for landscape.

upload_2014-7-20_1-21-54.png
upload_2014-7-20_1-23-19.png


Another powerful feature of the DialogView engine is that whenever the user clicks on a button with a numeric id in its tag, the DialogView will dismiss itself and will return the tag of that button as a result. Following that methodology, let's set Button1 tag to 1 and Button2 tag to 2.

Create the function that we will use to add some items to the listview
B4X:
Sub AddItemsToTheListView(ListView As ListView)
    Dim Count As Int = ListView.Size
    For i=1 To 10
        ListView.AddSingleLine("item #" & (Count + i))
    Next
    ListView.SetSelection(ListView.Size)
End Sub
Then, create the Click handler of the BtnBeepAndAddItems button in the layout designer
B4X:
Sub BtnBeepAndAddItems_Click
    Dim Beeper As Beeper : Beeper.Initialize(300, 500) : Beeper.Beep
    Dim ListView As ListView = Dialog.Views.Get("ListView1")

    AddItemsToTheListView(Dialog.Views.Get("ListView1"))
    Main.Toast.Show("10 items added to the listview (total: " & ListView.Size & ")")
End Sub
Create the ItemClick handler of the ListView1 in the layout designer
B4X:
Sub ListView1_ItemClick (Position As Int, Value As Object)
    Main.Toast.Show("listview (pos #" & Position & ") = " & Value)
End Sub
Load the multiple layouts by using the LoadLayoutMultiple method
B4X:
Dim DialogLayout As DialogViewLayout
DialogLayout = Dialog.LoadLayoutMultiple("dialog4_portrait", "dialog4_landscape", "dialog4")
Get a reference of the ListView1 from the layout, set the textcolor and add 10 items to it
B4X:
Dim ListView1 As ListView = DialogLayout.Views.Get("ListView1")
ListView1.SingleLineLayout.Label.TextColor = Colors.Black
AddItemsToTheListView(ListView1)
Finally, show the dialog without the system title and buttons
B4X:
Dim Result = DialogLayout.ShowDefault


Part 5 A reusable custom dialog

After having gone through the previous steps, it is very easy even to create a reusable custom dialog.

upload_2014-7-20_1-42-33.png
upload_2014-7-20_1-43-52.png


Set the tag of the label that your want to use as a title to "title" and the tag of the label you want to use as content to "content" and call the following function:
B4X:
Dim Result As Int = Dialog.LoadLayout("dialog5").ShowDefault2("This is a title", "This is a custom content")


Part 6 Customizing the system and any other dialog

The DialogView.Options property has a wide range of configuration options that you can set in order to totally alter the look and feel of everything in a dialog. For example:

upload_2014-7-20_2-8-22.png
upload_2014-7-20_2-8-50.png


Configure the dialog title
B4X:
Dim TitleBackground As ColorDrawable : TitleBackground.Initialize(Colors.Gray, 0)
Dialog.Options.Title.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_BOLD_ITALIC), Colors.Blue, 25, Gravity.CENTER, TitleBackground, Colors.Red)
Configure the message content
B4X:
Dim MessageBackground As ColorDrawable : MessageBackground.Initialize(Colors.Magenta, 0)
Dialog.Options.Message.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_BOLD_ITALIC), Colors.Blue, 25, Gravity.CENTER, MessageBackground)
Configure the dialog buttons
B4X:
Dim ButtonBackground As StateListDrawable : ButtonBackground.Initialize
Dim ButtonBackgroundNormal As ColorDrawable : ButtonBackgroundNormal.Initialize(Colors.Yellow, 0)
Dim ButtonBackgroundPressed As ColorDrawable : ButtonBackgroundPressed.Initialize(Colors.Cyan, 0)
ButtonBackground.AddState(ButtonBackground.State_Pressed, ButtonBackgroundPressed)
ButtonBackground.AddCatchAllState(ButtonBackgroundNormal)

Dim ButtonStyle1, ButtonStyle2, ButtonStyle3 As DialogViewButtonStyle

ButtonStyle1.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_NORMAL), Colors.Blue, 14, Gravity.LEFT + Gravity.BOTTOM, ButtonBackground)
ButtonStyle2.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_BOLD), Colors.Red, 14, Gravity.LEFT + Gravity.Top, ButtonBackground)
ButtonStyle3.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_BOLD_ITALIC), Colors.Black, 24, Gravity.CENTER, ButtonBackground)

'Dialog.Options.Buttons.Default.Style = ButtonStyle1 <= apply the style to all buttons
Dialog.Options.Buttons.Positive.Style = ButtonStyle1
Dialog.Options.Buttons.Neutral.Style = ButtonStyle2
Dialog.Options.Buttons.Negative.Style = ButtonStyle3
Or set the style in one line
B4X:
Dialog.Options.Buttons.Default.Style.Set(Typeface.CreateNew(Typeface.SANS_SERIF, Typeface.STYLE_NORMAL), Colors.Blue, 14, Gravity.LEFT + Gravity.BOTTOM, ButtonBackground)


Part 7 Applying animations and gravity

Adding animation to any type of dialog is very simple.
  • Copy the AssetsXtra folder from the tutorial sample root folder to your project's root folder
  • Add this directive to your project attributes:
    B4X:
    #AdditionalRes: ..\AssetsXtra
  • Finally set the animation:
    B4X:
        Dialog.Options.Animation = "<animation_name>"
Currently the available animations are:
  • zoom
  • slide_from_top, slide_from_top_and_back
  • slide_from_bottom, slide_from_bottom_and_back,
  • slide_from_left,slide_from_left_and_back,
  • slide_from_right,slide_from_right_and_back

To set the gravity of the dialog:
B4X:
    Dialog.Options.Gravity = Gravity.RIGHT + Gravity.BOTTOM


Part 8 Customizing dialog dimensions

If you want to have specific dialog dimensions based on a display ratio or fixed values, just use the DialogView.Options.Dimensions property.

If you use the DialogView.Options.Dimensions.MODE_ANCHORS mode, the engine will respect any anchors that are set and will resize the views accordingly. Using the DialogView.Options.Dimensions.MODE_STRETCH mode, will stretch and reposition all views to fit the new dimensions nicely. Using the DialogView.Options.Dimensions.MODE_STRETCH_TEXT mode, is the same like MODE_STRETCH but it will try to adjust the textsize of affected buttons and textviews.

B4X:
Dialog.Options.Dimensions.Set(100%x, 100%y, Dialog.Options.Dimensions.MODE_ANCHORS)

--

That was all for now folks!

Tip: All dialog fields (title, message, button captions) accept HTML as an input that will be properly displayed.

There are a lot more things hidden. Play with it and you will discover them!

Enjoy! :D
 
Last edited:

ivan.tellez

Active Member
Licensed User
Longtime User
Hi, Im sorry, but Im a little confused about the Dimensions property, it seams to have a problem when is set to 100% and you have System Title and buttons. For example, in part 1 change the code to:

B4X:
Sub BtnDialog_Click
    Dialog.Options.Dimensions.Set(100%x, 100%y)
    Dim Result As Int = Dialog.LoadLayout("dialog1").ShowYesNo("Login", "proceed", "later")
    Main.Toast.Show("Result = " & Result)
End Sub

Or in the Part 4:

B4X:
Dim DialogLayout As DialogViewLayout
   
    dialog.Options.Dimensions.Set(100%x, 100%y)
    DialogLayout = Dialog.LoadLayoutMultiple("dialog4_portrait", "dialog4_landscape", "dialog4")
...

It seems that the scaling its Ok on X, but not on Y.


Also, you are resizing the views using only the Anchors, but maybe this is not allways a practical way (or maybe Im doing something wrong).

Ffor example, if you want to make a dialog with a layout of a keypad (Emulating the Numeric keypad of the PC), what will be the best way to resize the views to show the dialog always to 100%?

Thanks
 

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
The % scaling does not work well on Y axis with dialogs that have a system title and buttons (yet). this is why the tutorial sample uses dialogs without system title and buttons ;)

When resizing a dialog, well designed anchors will ensure a consistent display. It is not always wise to have resizable dialogs. Better use multiple layouts.
 
Last edited:

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
For your example with the numeric pad, you should not do any scale (resizing) but use a different layout per orientation with a fixed size.
 
Last edited:

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
Update: 2.1
  • Added: DialogView.Options.Dimensions.MODE_STRETCH and DialogView.Options.Dimensions.MODE_ANCHORS
Thanks to @ivan.tellez for the suggestion :)
 
Last edited:

ivan.tellez

Active Member
Licensed User
Longtime User
Wow, thats great

I was working in a layout like the image




Now its resizing :D, Its posible to resize also the text on the lib? or that chould be on the user?



The only catch its on the upper left corner, there is a white panel that its supose to cover the gray area, but its not stretching. I tested adding more panels and none of them stretches. Is this behavior intended?

Thanks
 

Attachments

  • hex.png
    hex.png
    59.1 KB · Views: 530
Last edited:

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
Update: 2.2
  • Added: DialogView.Options.Dimensions.MODE_STRETCH_TEXT to allow textsize adjustment of buttons and textviews while scaled
  • Fixed: Panels are now being resized properly while scaled
 

ivan.tellez

Active Member
Licensed User
Longtime User
Hi, im really sorry, had to go out

You are genius, panes now resize.

But I think text its not really at scale, ill send you the test on 3 different devices, panes are resized ok:

240x320
800x1280
2560x1600
 

Attachments

  • hex2.png
    hex2.png
    321 KB · Views: 455

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
Update: 2.3
  • Added: DialogViewViews object with all known ViewWrapper members
  • Changed: DialogLayoutView.GetView and DialogView.GetView changed to DialogLayoutView.Views.Get and DialogView.Views.Get

Now you can get direct reference to a layout view without the need of casting:

Before:
B4X:
Dim Label1 as Label = DialogLayout.Views.Get("Label1")
Label1.Text = "hello!"

Now:
B4X:
DialogLayout.Views.Label("Label1").Text = "hello!"
 
Last edited:

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
@ivan.tellez

The method of scaling the text size of a view is the standard method.
B4X:
double factor = newHeight/oldHeight
view.setTextSize((float)(Math.floor(view.getTextSize() * factor)-1));

As we can see in part8 the text down/up-scales correctly (scale: original - 50% - 100%)

upload_2014-7-21_15-12-15.png
upload_2014-7-21_15-12-32.png
upload_2014-7-21_15-12-47.png


I also tested it in various real devices and it appears working as expected.

Note that the % factor is % of the SCREEN dimensions.
 
Last edited:

Thraka

Member
Licensed User
Longtime User
I don't understand how to use the msgbox function. I'm trying to show a msgbox on top of another dialog. From my original dialog, I trap the button event and try to show the dialog like so:

B4X:
Sub btnRemove_Click
    Dim removeMsgbox As DialogView
    removeMsgbox.Msgbox("Remove Location", "Are you sure you want to remove " & cities.GetValueAt(selectedIndex) & "?", "Yes", "No", "", Null)
   
End Sub

Sub removeMsgbox_Dismiss(DialogResult As Int) As Boolean

End Sub

The msgbox shows correctly, but the dismiss event never triggers.
 

Periklis Koutsogiannis

Active Member
Licensed User
Longtime User
It works like this:

B4X:
Sub btnRemove_Click
    Dim removeMsgbox As DialogView
    Dim Result as Int
   
    Result = removeMsgbox.Msgbox("Remove Location", "Are you sure you want to remove " & cities.GetValueAt(selectedIndex) & "?", "Yes", "No", "", Null)
    
    If Result = DialogResponse.NEGATIVE then
    End If
End Sub
 
Top