iOS Question Menu works in B4A but not in B4I

boastrike

Member
Licensed User
Longtime User
I have created a menu which works fine in B4A. Almost identical code does not seem to work in B4I. When debugging, a person can see instance object data being cleared for no reason with break points. I have included the code for both the working B4A version first, then the not working B4I version. To create the project in each case make a class module WCSlider and paste the contents as is intuitive. I am not sure if there is some bug here, or where the problem lies. Thanks for the input.

ANDROID / B4A

MAIN
B4X:
#Region  Project Attributes 
    #ApplicationLabel: Wildcat Slider
    #VersionCode: 1
    #VersionName: 
    'SupportedOrientations possible values: unspecified, landscape or portrait.
    #SupportedOrientations: portrait
    #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes 
    #FullScreen: True
    #IncludeTitle: False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Private Banner As ImageView
   
    Private Menu As WCSlider
    Private MenuContainer As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")
    Activity.Color = Colors.Black
   
    Banner.Initialize("")
    Activity.AddView(Banner, 0, 0, 0, 0)
    Banner.Top = 0
    Banner.Left = 0
    Banner.Width = Activity.Width
    Banner.Height = Activity.Height * .2
    Banner.Gravity = Gravity.FILL
    Banner.Bitmap = LoadBitmap(File.DirAssets, "europa-banner.png")
   
    MenuContainer.Initialize("")
    Activity.AddView(MenuContainer, 0, 0, Activity.Width, Activity.Height)
    MenuContainer.Top = Activity.Height * .2
    MenuContainer.Left = 0
    MenuContainer.Width = Activity.Width
    MenuContainer.Height = Activity.Height * .55
   
    Menu.Initialize(3, 2, MenuContainer, Me)
   
    Menu.AddButton("box.png", "Cereal", "cerealButton")
    Menu.AddButton("check2.png", "Remember, Vote", "voteButton")
    Menu.AddButton("dollar.png", "Pay Taxes", "taxButton")
    Menu.AddButton("folder3.png", "File Stuff", "folderButton")
    Menu.AddButton("graph_green.png", "Success", "graphButton")
    Menu.AddButton("question_mark.png", "Huh?", "qmarkButton")
    Menu.AddButton("box.png", "Cereal", "cerealButton")
    Menu.AddButton("check2.png", "Remember, Vote", "voteButton")
    Menu.AddButton("dollar.png", "Pay Taxes", "taxButton")
    Menu.AddButton("folder3.png", "File Stuff", "folderButton")
    Menu.AddButton("graph_green.png", "Success", "graphButton")
    Menu.AddButton("question_mark.png", "Huh?", "qmarkButton")
    Menu.DrawButtons
End Sub

Sub WCSliderClick (CallbackString As String)
    ToastMessageShow(CallbackString,True)
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

SLIDER CLASS
B4X:
'Class module
Sub Class_Globals
    Private CreatingModule As Object
    Private WCSliderContainer As ScrollView

    Private Buttons As List
    Private Maps As List
   
    Private ButtonHeight As Float
    Private ButtonWidth As Float
   
    Private PrimaryColor As Int
    Private SecondaryColor As Int
    Private BorderColor As Int
    Private LabelColor As Int
   
    Private NumVisColumns As Int
    Private NumVisRows As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(VisibleColumns As Int, VisibleRows As Int, ParentPanel As Panel,  TargetCreatingModule As Object)
    CreatingModule = TargetCreatingModule

    WCSliderContainer.Initialize(ParentPanel.Height)
    ParentPanel.AddView(WCSliderContainer, 0, 0, ParentPanel.Width, ParentPanel.Height)
    Buttons.Initialize
    Maps.Initialize
    PrimaryColor = Colors.White
    SecondaryColor = Colors.RGB(200, 200, 200)
    BorderColor = Colors.RGB(150, 150, 150)
    LabelColor = Colors.Black

    WCSliderContainer.Panel.Width = ParentPanel.Width
    WCSliderContainer.Height = ParentPanel.Height
    WCSliderContainer.Width = ParentPanel.Width
   
    WCSliderContainer.Top = 0
    WCSliderContainer.Left = 0
    WCSliderContainer.Width = ParentPanel.Width
    WCSliderContainer.Height = ParentPanel.Height
   

    WCSliderContainer.Panel.Width = ParentPanel.Width
    WCSliderContainer.Panel.Height = ParentPanel.Height
   
    WCSliderContainer.Color = PrimaryColor
   
    ButtonHeight = WCSliderContainer.Height / VisibleRows
    ButtonWidth = WCSliderContainer.Width / VisibleColumns
   
    NumVisRows = VisibleRows
    NumVisColumns = VisibleColumns
End Sub

Public Sub SetColors(PrimaryColorArg As Int, SecondaryColorArg As Int, BorderColorArg As Int, LabelColorArg As Int)

End Sub

Public Sub AddButton(ImageName As String, LabelText As String, CallbackString As String)
    Dim ButtonMap As Map
    ButtonMap.Initialize
    ButtonMap.Put("Image", ImageName)
    ButtonMap.Put("Label", LabelText)
    ButtonMap.Put("Callback", CallbackString)
    Maps.Add(ButtonMap)
End Sub

Public Sub DrawButtons
    Dim AdHocMap As Map
    Dim AdHocBitmap As Bitmap
    Dim AdHocLabel As Label
   
    Dim Row As Int
    Dim Column As Int
   
    Row = 0
    Column = 0
   
    For index = 0 To Maps.Size - 1
        Dim AdHocPanel As Panel
        Dim AdHocButton As Button
        AdHocMap.Initialize
        AdHocMap = Maps.Get(index)
       
        AdHocPanel.Initialize("")
        WCSliderContainer.Panel.AddView(AdHocPanel, 0, 0, 0, 0)
        AdHocPanel.Left = Column * ButtonWidth
        AdHocPanel.Top = Row * ButtonHeight
        AdHocPanel.Width = ButtonWidth
        AdHocPanel.Height = ButtonHeight
        BPanel(AdHocPanel, 1, PrimaryColor, BorderColor)
       
        AdHocButton.Initialize("WCSliderClick")
        AdHocPanel.AddView(AdHocButton, 0, 0, 0, 0)
        AdHocBitmap.Initialize(File.DirAssets, AdHocMap.Get("Image"))
        AdHocButton.SetBackgroundImage(AdHocBitmap)
       
        AdHocLabel.Initialize("")
        AdHocPanel.AddView(AdHocLabel, 0, 0, 0, 0)
        AdHocLabel.TextSize = 20
        AdHocLabel.Gravity = Gravity.CENTER
        AdHocLabel.Typeface = Typeface.DEFAULT_BOLD
        AdHocLabel.TextColor = LabelColor
        AdHocLabel.Color = SecondaryColor
        AdHocLabel.Text = AdHocMap.Get("Label")
       
        If ButtonHeight > ButtonWidth Then
            AdHocButton.Top = ((ButtonHeight * .8) - ButtonWidth) / 2
            AdHocButton.Left = 0
            AdHocButton.Width = ButtonWidth
            AdHocButton.Height = ButtonWidth
        Else If ButtonHeight < ButtonWidth Then
            AdHocButton.Top = 0
            AdHocButton.Left = (ButtonWidth - (ButtonHeight * .8)) / 2
            AdHocButton.Width = ButtonHeight * .8
            AdHocButton.Height = ButtonHeight  * .8
        End If
       
        AdHocLabel.Top = ButtonHeight * .8
        AdHocLabel.Left = 0
        AdHocLabel.Width = ButtonWidth
        AdHocLabel.Height = ButtonHeight * .2
       
        Buttons.Add(AdHocButton)
       
        WCSliderContainer.Panel.Height = AdHocPanel.Top + AdHocPanel.Height
       
        If Column <> NumVisColumns - 1 Then
            Column = Column + 1
        Else
            Column = 0
            Row = Row + 1
        End If
    Next
End Sub

Private Sub WCSliderClick_Click
   
    Dim AdHocMap As Map
    AdHocMap.Initialize
    AdHocMap = Maps.Get(Buttons.IndexOf(Sender))
    CallSub2(CreatingModule, "WCSliderClick", AdHocMap.Get("Callback"))
End Sub

Private Sub BPanel(bpnl As Panel, bBorderWidth As Float, bFillColor As Int, bBorderColor As Int)
    Dim Rec As Rect
    Dim Canvas1 As Canvas
    Dim BorderWidth_2 As Float
   
    BorderWidth_2=bBorderWidth/2
    Rec.Initialize(BorderWidth_2,BorderWidth_2,bpnl.Width-BorderWidth_2,bpnl.Height-BorderWidth_2)
    Canvas1.Initialize(bpnl)
    Canvas1.DrawRect(Rec,bFillColor,True,bFillColor)
    Canvas1.DrawRect(Rec,bBorderColor,False,bBorderWidth)
End Sub

iOS / B4I

MAIN
B4X:
'Code module
#Region  Project Attributes 
    #ApplicationLabel: WCSlider
    #Version: 1.0.0 
    'Orientation possible values: Portrait, LandscapeLeft, LandscapeRight and PortraitUpsideDown
    #iPhoneOrientations: Portrait, LandscapeLeft, LandscapeRight
    #iPadOrientations: Portrait, LandscapeLeft, LandscapeRight, PortraitUpsideDown
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'Public variables can be accessed from all modules.
    Public App As Application
    Public NavControl As NavigationController
    Private MainPage As Page
   
    Private Banner As ImageView
   
    Private Menu As WCSlider
    Private MenuContainer As Panel
   
    Private Toast As HUD
End Sub

Private Sub Application_Start (Nav As NavigationController)
    NavControl = Nav
    MainPage.Initialize("MainPage")
    MainPage.RootPanel.Color = Colors.Black
    NavControl.ShowPage(MainPage)
End Sub

Sub MainPage_Appear
    MainPage.RootPanel.Color = Colors.Black
   
    Banner.Initialize("")
    MainPage.RootPanel.AddView(Banner, 0, 0, 0, 0)
    Banner.Top = 0
    Banner.Left = 0
    Banner.Width = MainPage.RootPanel.Width
    Banner.Height = MainPage.RootPanel.Height * .2
    Banner.Bitmap = LoadBitmap(File.DirAssets, "europa-banner.png")
   
    MenuContainer.Initialize("")
    MainPage.RootPanel.AddView(MenuContainer, 0, 0, MainPage.RootPanel.Width, MainPage.RootPanel.Height)
    MenuContainer.Top = MainPage.RootPanel.Height * .2
    MenuContainer.Left = 0
    MenuContainer.Width = MainPage.RootPanel.Width
    MenuContainer.Height = MainPage.RootPanel.Height * .55
   
    Menu.Initialize(3, 2, MenuContainer, Me)
   
    Menu.AddButton("box.png", "Cereal", "cerealButton")
    Menu.AddButton("check2.png", "Remember, Vote", "voteButton")
    Menu.AddButton("dollar.png", "Pay Taxes", "taxButton")
    Menu.AddButton("folder3.png", "File Stuff", "folderButton")
    Menu.AddButton("graph_green.png", "Success", "graphButton")
    Menu.AddButton("question_mark.png", "Huh?", "qmarkButton")
    Menu.AddButton("box.png", "Cereal", "cerealButton")
    Menu.AddButton("check2.png", "Remember, Vote", "voteButton")
    Menu.AddButton("dollar.png", "Pay Taxes", "taxButton")
    Menu.AddButton("folder3.png", "File Stuff", "folderButton")
    Menu.AddButton("graph_green.png", "Success", "graphButton")
    Menu.AddButton("question_mark.png", "Huh?", "qmarkButton")
    Menu.DrawButtons
End Sub

Sub WCSliderClick(CallbackString As String)
    Toast.ToastMessageShow(CallbackString,True)
End Sub

Private Sub Page1_Resize(Width As Int, Height As Int)
   
End Sub

Private Sub Application_Background
   
End Sub

SLIDER CLASS
B4X:
'Class module
Sub Class_Globals
    Private CreatingModule As Object
    Private WCSliderContainer As ScrollView

    Private Buttons As List
    Private Maps As List
   
    Private ButtonHeight As Float
    Private ButtonWidth As Float
   
    Private PrimaryColor As Int
    Private SecondaryColor As Int
    Private BorderColor As Int
    Private LabelColor As Int
   
    Private NumVisColumns As Int
    Private NumVisRows As Int
   
    Private Toast As HUD
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(VisibleColumns As Int, VisibleRows As Int, ParentPanel As Panel,  TargetCreatingModule As Object)
    CreatingModule = TargetCreatingModule

    WCSliderContainer.Initialize("", ParentPanel.Width, ParentPanel.Height)
    ParentPanel.AddView(WCSliderContainer, 0, 0, ParentPanel.Width, ParentPanel.Height)
    Buttons.Initialize
    Maps.Initialize
    PrimaryColor = Colors.White
    SecondaryColor = Colors.RGB(200, 200, 200)
    BorderColor = Colors.RGB(150, 150, 150)
    LabelColor = Colors.Black

    WCSliderContainer.Panel.Width = ParentPanel.Width
    WCSliderContainer.Height = ParentPanel.Height
    WCSliderContainer.Width = ParentPanel.Width
   
    WCSliderContainer.Top = 0
    WCSliderContainer.Left = 0
    WCSliderContainer.Width = ParentPanel.Width
    WCSliderContainer.Height = ParentPanel.Height
   

    WCSliderContainer.Panel.Width = ParentPanel.Width
    WCSliderContainer.Panel.Height = ParentPanel.Height
   
    WCSliderContainer.Color = PrimaryColor
   
    ButtonHeight = WCSliderContainer.Height / VisibleRows
    ButtonWidth = WCSliderContainer.Width / VisibleColumns
   
    NumVisRows = VisibleRows
    NumVisColumns = VisibleColumns
End Sub

Public Sub SetColors(PrimaryColorArg As Int, SecondaryColorArg As Int, BorderColorArg As Int, LabelColorArg As Int)

End Sub

Public Sub AddButton(ImageName As String, LabelText As String, CallbackString As String)
    Dim ButtonMap As Map
    ButtonMap.Initialize
    ButtonMap.Put("Image", ImageName)
    ButtonMap.Put("Label", LabelText)
    ButtonMap.Put("Callback", CallbackString)
    Maps.Add(ButtonMap)
End Sub

Public Sub DrawButtons
    Dim AdHocMap As Map
    Dim AdHocBitmap As Bitmap
    Dim AdHocLabel As Label
   
    Dim Row As Int
    Dim Column As Int
   
    Row = 0
    Column = 0
   
    For index = 0 To Maps.Size - 1
        Dim AdHocPanel As Panel
        Dim AdHocButton As Button
        AdHocMap.Initialize
        AdHocMap = Maps.Get(index)
       
        AdHocPanel.Initialize("")
        WCSliderContainer.Panel.AddView(AdHocPanel, 0, 0, 0, 0)
        AdHocPanel.Left = Column * ButtonWidth
        AdHocPanel.Top = Row * ButtonHeight
        AdHocPanel.Width = ButtonWidth
        AdHocPanel.Height = ButtonHeight
        BPanel(AdHocPanel, 1, PrimaryColor, BorderColor)
       
        AdHocButton.Initialize("WCSliderClick", AdHocButton.STYLE_SYSTEM)
        AdHocPanel.AddView(AdHocButton, 0, 0, 0, 0)
        AdHocBitmap.Initialize(File.DirAssets, AdHocMap.Get("Image"))
        SetBackgroundImage(AdHocButton, AdHocBitmap, 0)
       
        AdHocLabel.Initialize("")
        AdHocPanel.AddView(AdHocLabel, 0, 0, 0, 0)
        AdHocLabel.TextAlignment = AdHocLabel.ALIGNMENT_CENTER
        AdHocLabel.Font = Font.CreateNewBold(20)
        AdHocLabel.TextColor = LabelColor
        AdHocLabel.Color = SecondaryColor
        AdHocLabel.Text = AdHocMap.Get("Label")
       
        If ButtonHeight > ButtonWidth Then
            AdHocButton.Top = ((ButtonHeight * .8) - ButtonWidth) / 2
            AdHocButton.Left = 0
            AdHocButton.Width = ButtonWidth
            AdHocButton.Height = ButtonWidth
        Else If ButtonHeight < ButtonWidth Then
            AdHocButton.Top = 0
            AdHocButton.Left = (ButtonWidth - (ButtonHeight * .8)) / 2
            AdHocButton.Width = ButtonHeight * .8
            AdHocButton.Height = ButtonHeight  * .8
        End If
       
        AdHocLabel.Top = ButtonHeight * .8
        AdHocLabel.Left = 0
        AdHocLabel.Width = ButtonWidth
        AdHocLabel.Height = ButtonHeight * .2
       
        Buttons.Add(AdHocButton)
       
        WCSliderContainer.Panel.Height = AdHocPanel.Top + AdHocPanel.Height
       
        If Column <> NumVisColumns - 1 Then
            Column = Column + 1
        Else
            Column = 0
            Row = Row + 1
        End If
    Next
End Sub

Private Sub WCSliderClick_Click
    Dim AdHocMap As Map
    AdHocMap.Initialize
    AdHocMap = Maps.Get(Buttons.IndexOf(Sender))
    'Toast.ToastMessageShow(AdHocMap.Get("Callback"), True)
    CallSub2(CreatingModule, "WCSliderClick", AdHocMap.Get("Callback"))
End Sub

Private Sub BPanel(bpnl As Panel, bBorderWidth As Float, bFillColor As Int, bBorderColor As Int)
    Dim Rec As Rect
    Dim Canvas1 As Canvas
    Dim BorderWidth_2 As Float
   
    BorderWidth_2=bBorderWidth/2
    Rec.Initialize(BorderWidth_2,BorderWidth_2,bpnl.Width-BorderWidth_2,bpnl.Height-BorderWidth_2)
    Canvas1.Initialize(bpnl)
    Canvas1.DrawRect(Rec,bFillColor,True,bFillColor)
    Canvas1.DrawRect(Rec,bBorderColor,False,bBorderWidth)
End Sub

Sub SetBackgroundImage(b As Button, bmp As Bitmap, state As Int)
 Dim no As NativeObject = b
 no.RunMethod("setBackgroundImage:forState:", Array(bmp, state))
End Sub

Thanks to everyone for their time!
 

boastrike

Member
Licensed User
Longtime User
See this thread: https://www.b4x.com/android/forum/threads/scrollview-b4a-b4i.48834/#post-303749

Also note that the page width and height are only 100% accurate inside Page_Resize event.
Usually you should provide a resize method which is called from the resize event.

Thank you so much for getting back to me. The issue outlined in you response is not related to the issue at hand. The problem is that an object member list, that should be storing meta data like callback names, that is filled in the main module by calls to a filling instance method, is being erased for no reason. This prevents callback metadata from being sent back to the main module as it should be, when a button is pressed.

In short, everything appears to be working fine, and the problem has nothing to do with aesthetics. The problem occurs when a user presses a button. What should happen is a toast message should appear after the user presses a button. The details of the toast message should be setup when the buttons are created, using an instance method for creating buttons. Instead of the correct details appearing, a null is present. This implies the list which holds information about the buttons is being erased for some reason.

Further assistance regarding this would be very helpful. Thanks so much for your time.
 
Upvote 0

boastrike

Member
Licensed User
Longtime User
Thank you very much for taking the time to generate a response via email. You mentioned two bugs. I am posting them here in hopes of making sure the answers are shared. Looking at what you found I'm still struggling to get them resolved.


1. You are not setting the scrollview height and width correctly.

- I am not sure on this and would welcome suggestions. The part that is curious is that this code was scrolling as written and the only thing that changed was upgrading to the latest beta version a few days ago. The current problem is that the scrollview is no longer scrolling. Also, the same code works well in B4A as it did before this new beta. I want to add that no sizing is done until after the view appears allowing for scales to work well. This method has been used in native code on some of our teams other projects with success.

2. You are calling AdHocMap.Initialize at the beginning of the loop. This causes the data of the previous instance to be deleted.

- I actually want adhocmap to be erased and changed every time the loop iterates, as I am just loading one row of the Maps array. I simply use the temporary values in the adhocmap then move on and use another set of temp values.
The problem is that the instance member Maps (array) values are being completely and randomly erased. This is happening in the DrawButtons sub. It is easiest to see with the debugger. I am not sure why the Maps values are being cleared when initializing AdHocMap.

This problem does not happen with the same logic in B4A. I am hoping if I am doing something wrong regarding the list and why it keeps clearing, please assist. Thanks very much for your time!
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. As I previously wrote you should use ContentWidth and ContentHeight to change the width and height of the inner panel (see post #3).

2. You should add Dim AdHocMap As Map inside the loop (before the call to Initialize) to avoid reusing the same map. You should also do it in B4A though in B4A due to how Map is implemented a new instance is created anyway in this case.
 
Upvote 0
Top