Android Tutorial [B4X] [XUI] Creating custom views with XUI

Start with this tutorial if you are not familiar with custom views: [B4X] Custom Views with Enhanced Designer Support

With the help of XUI it is quite simple to create custom views classes that will work with B4A, B4i and B4J.

The first step is to use a slightly modified template:
B4X:
#DesignerProperty: Key: BooleanExample, DisplayName: Show Seconds, FieldType: Boolean, DefaultValue: True
#DesignerProperty: Key: TextColor, DisplayName: Text Color, FieldType: Color, DefaultValue: 0xFFFFFFFF, Description: Text color

Sub Class_Globals
   Private mEventName As String 'ignore
   Private mCallBack As Object 'ignore
   Private mBase As B4XView
  Private xui as XUI
End Sub

Public Sub Initialize (Callback As Object, EventName As String)
   mEventName = EventName
   mCallBack = Callback
End Sub

'Base type must be Object
Public Sub DesignerCreateView (Base As Object, Lbl As Label, Props As Map)
   mBase = Base
  
End Sub

Private Sub Base_Resize (Width As Double, Height As Double)
  
End Sub

Example of a clock view:

SS-2017-10-17_16.04.04.png


Code:
B4X:
#DesignerProperty: Key: ShowSeconds, DisplayName: Show Seconds, FieldType: Boolean, DefaultValue: True
#DesignerProperty: Key: TextColor, DisplayName: Text Color, FieldType: Color, DefaultValue: 0xFFA2A2A2, Description: Text color

Sub Class_Globals
   Private mEventName As String 'ignore
   Private mCallBack As Object 'ignore
   Private mBase As B4XView
   Private mLbl As B4XView
   Private xui As XUI
   Private Timer1 As Timer
   Private showSeconds As Boolean
End Sub

Public Sub Initialize (Callback As Object, EventName As String)
   mEventName = EventName
   mCallBack = Callback
   Timer1.Initialize("Timer1", 1000)
End Sub

'Base type must be Object
Public Sub DesignerCreateView (Base As Object, Lbl As Label, Props As Map)
   mBase = Base
   mLbl = Lbl
  mBase.AddView(mLbl, 0, 0, mBase.Width, mBase.Height)
   mLbl.SetTextAlignment("CENTER", "CENTER")
   mLbl.TextColor = xui.PaintOrColorToColor(Props.Get("TextColor"))
   showSeconds = Props.Get("ShowSeconds")
   Timer1.Enabled = True
   Timer1_Tick
End Sub

Private Sub Base_Resize (Width As Double, Height As Double)
   mLbl.SetLayoutAnimated(0, 0, 0, Width, Height)
End Sub

Sub Timer1_Tick
   Dim t As Long = DateTime.Now
   mLbl.Text = $"$2.0{DateTime.GetHour(t)}:$2.0{DateTime.GetMinute(t)}"$
   If showSeconds Then
     mLbl.Text = mLbl.Text & $":$2.0{DateTime.GetSecond(t)}"$
   End If
End Sub

I think that the only thing interesting here is the xui.PaintOrColorToColor method.
In B4A and B4i colors are represented as Ints (0xAARRGGBB).
In B4J colors are natively represented as Paint objects.

In the XUI library colors are represented as Ints.
The default color constants are available in the XUI type:
B4X:
Button1.Color = xui.Color_Red

Back to PaintOrColorToColor, the Props map in B4J will hold Paint objects for color values. xui.PaintOrColorToColor checks the object type and converts it, if needed, to an Int representing the same color. In B4A and B4i these methods do not do anything.

Note that Base_Resize will only be called in B4J and B4i.

A more advanced example: [B4X] [XUI] [custom view] CircularProgressBar
 

Attachments

  • ClockView.bas
    1.4 KB · Views: 1,222

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
Hello everyone,

What's the best way to put in a CustomProperty some DIP value?

The question is: I need to create a property with DIP value, to put in this property

Props.Get("BorderCornerRadius")

But we dont have DIP types, just INT.

So, when receive this property I need to convert from INT to DIP, and need to work to B4i and B4A.

Thank you
 

klaus

Expert
Licensed User
Longtime User
My answer in post #4 was wrong.
You need to use:
B4X:
BorderCornerRadius = Props.Get("BorderCornerRadius")
#If B4A Then
    BorderCornerRadius = BorderCornerRadius / GetDeviceLayoutValues.Scale
#End If
The code above is wrong! It aconverts pixels to dip values.

It's the other way, thank you Erel, I remembered this answer this morning and wanted to change it, but you were faster:
B4X:
BorderCornerRadius = DipToCurrent(Props.Get("BorderCornerRadius"))
 
Last edited:

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
You absolutely right:
B4X:
    Dim iBorderCornerRadius As Int = Props.Get("BorderCornerRadius")
    LogColor("iBorderCornerRadius1=" & iBorderCornerRadius,Colors.Blue)
    iBorderCornerRadius = iBorderCornerRadius / GetDeviceLayoutValues.Scale
    LogColor("iBorderCornerRadius2=" & iBorderCornerRadius,Colors.Blue)
    LogColor("iBorderCornerRadius3=" & DipToCurrent(Props.Get("BorderCornerRadius")),Colors.Blue)
    LogColor("10dip DtC=" & DipToCurrent(10),Colors.Blue)
    LogColor("10dip=" & 10dip,Colors.Blue)

Result:
B4X:
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
iBorderCornerRadius1=10
iBorderCornerRadius2=5
iBorderCornerRadius3=20
10dip DtC=20
10dip=20

The confusion is because the name of function DipToCurrent should be something like :
IntToDip, CurrentToDip, right? Because the final result is in DIP.

Thank you again!

Alberto
 

klaus

Expert
Licensed User
Longtime User
for B4i I can use GetDeviceLayoutValues.Scale?
You should use GetDeviceLayoutValues in B4A only, for a conversion from pixels to dip values. The GetDeviceLayoutValues method doesn't exist in B4i nor in B4J.

The confusion is because the name of function DipToCurrent should be something like :
IntToDip, CurrentToDip, right? Because the final result is in DIP.
No, the name DipToCurrent is correct because it converts a dip value into a pixel value.
In B4i you don't need the conversion, it is done automatically by the OS.

In your example you have a value of 10 and a scale of 2.
In B4A, 10 is considered as a dip value so you need to convert it to a pixel value with DipToCurrent to get 20 pixels with:
iBorderCornerRadius = DipToCurrent(Props.Get("BorderCornerRadius".
This is the correct line:
iBorderCornerRadius = DipToCurrent(Props.Get("BorderCornerRadius"))
 
Last edited:
Top