B4J Question Beginner trouble with B4XCanvas

TheRealMatze

Active Member
Licensed User
Hi,
i´m new here and try to make a very simple tool to input some data.
On the first hand i was unsure wich views are safe to use over android and ios, but after some time it looks like the one i search for is missing anyway.
I need a button with 4 states - unselected, on, type a and type b. I want to display this with circles inside the button.
With a idea in my head i start to build something like that. I look at the forum how to draw what i need and at the beginning it looks "ok". But then i add a timer to roll through the options - and i see the old element is stay there... My "canvas.clearRect(rect)" makes nothing. Than i add a filled background to my button, so the dots, in the code later, should be over this. Short answer - no, it works the first time, after that ist stays in this position.

Can anybody tell me what is wrong in my head...

"Drawing" has only one view, a Pane named "pnlDrawing"

B4X:
Sub Class_Globals

    Dim fx As JFX

    Dim xui As XUI

    Dim pnlDrawing As B4XView

    Dim canvas As B4XCanvas

    Dim rect As B4XRect

    Dim path As B4XPath

    Dim SelectedOption As Int

    Dim tmr As Timer

End Sub



Public Sub Initialize(Parent As B4XView)

    Parent.LoadLayout("Drawing")

 

    SelectedOption=1

    tmr.Initialize("timer",1000)

    tmr.Enabled = True



End Sub



Sub timer_tick

    SelectedOption=SelectedOption+1

    If SelectedOption=4 Then SelectedOption=0

    UpdateButton

End Sub



Private Sub UpdateButton()

    canvas.Initialize(pnlDrawing)

 

    rect.Left =0:rect.Top=0:rect.Width=60:rect.Height=60

    canvas.ClearRect(rect)                                             



    path.InitializeRoundedRect (rect,5)

    canvas.DrawPath(path,xui.Color_Gray ,True,0)                '< this stops updating the rest after the first execute

    canvas.DrawPath(path,xui.Color_RGB(145,0,41) ,False,2)



    If SelectedOption=1 Then

        rect.Left =15:rect.Top=10:rect.Width=30:rect.Height=10

        canvas.DrawRect(rect,xui.Color_RGB(43,137,4) ,True,2)

    End If

    If SelectedOption=1 Or SelectedOption=2 Then

        canvas.DrawCircle(15,15,5,xui.Color_RGB(43,137,4) ,True,0)

    End If

    If SelectedOption=1 Or SelectedOption=3 Then

        canvas.DrawCircle(45,15,5,xui.Color_RGB(43,137,4) ,True,0)

    End If

 

    canvas.Invalidate

End Sub

Thank you
Matze
 

Brian Dean

Well-Known Member
Licensed User
Longtime User
Erel is correct, of course. I have tested your code. Move this line ...
B4X:
    canvas.Initialize(pnlDrawing)
... into
B4X:
Public Sub Initialize(Parent As B4XView)
and everything works.
 
Upvote 0

TheRealMatze

Active Member
Licensed User
Thank you Erel, Brian!
I´ve moved the canvas initialize into the sub initialize - that works. I misstranslated "invalidate" and was thinking this terminates the canvas...
Next step is to put it into a custom view...
 
Upvote 0

TheRealMatze

Active Member
Licensed User
Ok not so easy as i hope... In B4J it works perfect, B4J give me a error-message...

It´s basically the same code only ported to a custom view.
The error in B4A says:

fourstatebutton_designercreateview (java line: 86)
java.lang.RuntimeException: java.lang.ClassCastException: b4a.example.fourstatebutton cannot be cast to android.widget.TextView


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
    Public mBase As B4XView
    Private xui As XUI 'ignore
    Public Tag As Object
    Dim pnlDrawing As B4XView
    Dim canvas As B4XCanvas
    Dim rect As B4XRect
    Dim path As B4XPath
    Dim SelectedOption As Int
End Sub

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

Public Sub DesignerCreateView (Base As Object, Lbl As Label, Props As Map)
    mBase = Base
    Tag = mBase.Tag
    mBase.Tag = Me
    'Dim clr As Int = xui.PaintOrColorToColor(Props.Get("TextColor")) 'Example of getting a color value from Props
    
    mBase.LoadLayout("Drawing")
    canvas.Initialize(pnlDrawing)
    SelectedOption=0
    UpdateButton                
End Sub

Private Sub UpdateButton()
  
    rect.Left =0:rect.Top=0:rect.Width=60:rect.Height=60
    canvas.ClearRect(rect)
    
    path.InitializeRoundedRect (rect,5)
    canvas.DrawPath(path,xui.Color_RGB(145,0,41) ,False,2)
    
    If SelectedOption=1 Then
        rect.Left =15:rect.Top=10:rect.Width=30:rect.Height=10
        canvas.DrawRect(rect,xui.Color_RGB(43,137,4) ,True,2)
    End If
    If SelectedOption=1 Or SelectedOption=2 Then
        canvas.DrawCircle(15,15,5,xui.Color_RGB(43,137,4) ,True,0)
    End If
    If SelectedOption=1 Or SelectedOption=3 Then
        canvas.DrawCircle(45,15,5,xui.Color_RGB(43,137,4) ,True,0)
    End If
    
    canvas.Invalidate
End Sub


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

#if B4J
Private Sub pnlDrawing_MouseClicked (EventData As MouseEvent)
    SelectedOption=SelectedOption+1
    If SelectedOption=4 Then SelectedOption=0
    UpdateButton
End Sub
#end if

#if B4A
Private Sub pnlDrawing_Touch (Action As Int, X As Float, Y As Float)
    SelectedOption=SelectedOption+1
    If SelectedOption=4 Then SelectedOption=0
    UpdateButton
End Sub
#end if

Sorry for the newbie-questions, but the messages are right now a little bit cryptic for me ;)

Matze
 
Upvote 0

TheRealMatze

Active Member
Licensed User
I have not defined anything manually...

B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
End Sub

Public Sub Initialize
    
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("1")
End Sub

'You can see the list of page related events in the B4XPagesManager object. The event name is B4XPage.

Sub Button1_Click
    xui.MsgboxAsync("Hello world!", "B4X")
End Sub

The full error message is

Java:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
fourstatebutton_designercreateview (java line: 80)
java.lang.RuntimeException: java.lang.ClassCastException: b4a.example.fourstatebutton cannot be cast to android.widget.TextView
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:170)
    at anywheresoftware.b4a.objects.PanelWrapper.LoadLayout(PanelWrapper.java:134)
    at anywheresoftware.b4a.objects.B4XViewWrapper.LoadLayout(B4XViewWrapper.java:312)
    at b4a.example.fourstatebutton._designercreateview(fourstatebutton.java:80)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:213)
    at anywheresoftware.b4a.objects.CustomViewWrapper.AfterDesignerScript(CustomViewWrapper.java:67)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:162)
    at anywheresoftware.b4a.objects.PanelWrapper.LoadLayout(PanelWrapper.java:134)
    at anywheresoftware.b4a.objects.B4XViewWrapper.LoadLayout(B4XViewWrapper.java:312)
    at b4a.example.b4xmainpage._b4xpage_created(b4xmainpage.java:38)
    at b4a.example.b4xmainpage.callSub(b4xmainpage.java:67)
    at anywheresoftware.b4a.keywords.Common.CallSub4(Common.java:1066)
    at anywheresoftware.b4a.keywords.Common.CallSubNew2(Common.java:1037)
    at b4a.example.b4xpagesmanager._createpageifneeded(b4xpagesmanager.java:505)
    at b4a.example.b4xpagesmanager._showpage(b4xpagesmanager.java:828)
    at b4a.example.b4xpagesmanager._addpage(b4xpagesmanager.java:199)
    at b4a.example.b4xpagesmanager._addpageandcreate(b4xpagesmanager.java:206)
    at b4a.example.b4xpagesmanager._initialize(b4xpagesmanager.java:690)
    at b4a.example.main._activity_create(main.java:365)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:213)
    at b4a.example.main.afterFirstLayout(main.java:105)
    at b4a.example.main.access$000(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:83)
    at android.os.Handler.handleCallback(Handler.java:873)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:193)
    at android.app.ActivityThread.main(ActivityThread.java:6669)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.ClassCastException: b4a.example.fourstatebutton cannot be cast to android.widget.TextView
    at anywheresoftware.b4a.objects.CustomViewWrapper.AfterDesignerScript(CustomViewWrapper.java:44)
    at anywheresoftware.b4a.keywords.LayoutBuilder.loadLayout(LayoutBuilder.java:162)
    ... 31 more

I don´t know where you mean...
 
Upvote 0

TheRealMatze

Active Member
Licensed User
Ok, Sleep(0) was the solution.
Next Problem is sizing. Private Sub Base_Resize (Width As Double, Height As Double) gives me a value based on the view-size on the called Layout, but the rect will not fit in it...

B4X:
    Private Sub UpdateButton()
        rect.Initialize(0,0,0,0)
        rect.Left =0:rect.Top=0:rect.Width=Breite:rect.Height=Hoehe
        canvas.ClearRect(rect)
        path.InitializeRoundedRect (rect,5)
        canvas.DrawPath(path,xui.Color_RGB(145,0,41) ,False,2)
  
    ...
    End Sub
  
    Private Sub Base_Resize (Width As Double, Height As Double)
        Breite = Width
        Hoehe = Height
    End Sub

It looks like there are different units, because when i subtract a value from width and height i have to substract more if the view is larger.

How can i use XUI.CreatePanel instead?
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
I played a little bit with your project.
I modified it to show what could be done and how.
Up to you to look at the changes and understand why and what for.
Tested with all three platforms.
 

Attachments

  • Project.zip
    31.8 KB · Views: 201
Upvote 0

TheRealMatze

Active Member
Licensed User
Perfect Klaus, that helps me a lot.
Can u also tell me why i can add a "normal" button dynamicly at runtime, but my button returns an exception?

B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Dim  Buttons(99) As Button
    Dim  myButtons(99) As FourStateButton
End Sub

'This event will be called once, before the page becomes visible.
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    Buttons(1).Initialize("Buttons")
    Root.AddView(Buttons(1),0,0,100,100) '< Works
    
    myButtons(1).Initialize("Buttons","ButtonE") '< Why a second Attribute?
    Root.AddView(myButtons(1),0,0,100,100) '< Exception
End Sub

Java:
Waiting for debugger to connect...
shell switching to alternate port: 9054
shell switching to alternate port: 9055
Program started.
Fehler in Zeile: 28 (B4XMainPage)
java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.keywords.Common.CallSubDebug2(Common.java:460)
    at b4j.FourStateButtonDemo.b4xpagesmanager._createpageifneeded(b4xpagesmanager.java:842)
    at b4j.FourStateButtonDemo.b4xpagesmanager._showpage(b4xpagesmanager.java:324)
    at b4j.FourStateButtonDemo.b4xpagesmanager._addpage(b4xpagesmanager.java:153)
    at b4j.FourStateButtonDemo.b4xpagesmanager._addpageandcreate(b4xpagesmanager.java:167)
    at b4j.FourStateButtonDemo.b4xpagesmanager._initialize(b4xpagesmanager.java:118)
    at b4j.FourStateButtonDemo.main._appstart(main.java:79)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:632)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:91)
    at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:98)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:78)
    at b4j.FourStateButtonDemo.main.start(main.java:38)
    at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:846)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$12(PlatformImpl.java:455)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:428)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$11(PlatformImpl.java:427)
    at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
    at javafx.graphics/com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:174)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:134)
    at anywheresoftware.b4a.debug.Debug.CallSubNew2(Debug.java:81)
    ... 35 more
Caused by: java.lang.reflect.InvocationTargetException
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
    at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:115)
    ... 36 more
Caused by: java.lang.IllegalArgumentException: Children: duplicate children added: parent = AnchorPane@6142bae0[styleClass=root]
    at javafx.graphics/javafx.scene.Parent$3.onProposedChange(Parent.java:558)
    at javafx.base/com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:317)
    at anywheresoftware.b4j.objects.PaneWrapper.InsertNode(PaneWrapper.java:330)
    at anywheresoftware.b4j.objects.PaneWrapper.AddNode(PaneWrapper.java:311)
    at anywheresoftware.b4a.objects.B4XViewWrapper.AddView(B4XViewWrapper.java:523)
    at b4j.FourStateButtonDemo.b4xmainpage._b4xpage_created(b4xmainpage.java:65)
    ... 41 more
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
Because CustomViews do not work the same way as standard views.
Two posiibilies:
1. Make a layout file with only one FourStateButton button and load this layout for each button in a loop.
2. You can add a routine in your FourStateButton class to add an instance to the parent view.
You may have a look at the chapter 4.3 Adding a custom view by code in the B4x CustomViews Booklet.
 
Last edited:
Upvote 0

TheRealMatze

Active Member
Licensed User
Thank you Klaus, this is fine for now - my next question is about another theme, so i think it´s better to open a new thread...
 
Upvote 0
Top