B4J Question [SOLVED] How to Have Checkboxes in a CLV with only one been Checked at a time

Peter Lewis

Active Member
Licensed User
Longtime User
Hi All,
I have a customlistview which has 3 files on it and a Checkbox.
The checkbox is for Default supplier. Only one of the record in the custom list view should be Ticked as there can only be one Default supplier.

So I am using the Sender function to determine which one should be Ticked and then reading the values of all of them, clearing the CLV and re-writing the values with the correct Boolean value of the Checkbox.

Code Below
B4X:
'class globals
Type supplierproduct (supplier As String, id_product As Int,supplier_reference As String,wholesale_price As String, Currency As String, chkDfltSupplier As CheckBox)


Private Sub chkDefaultSupplier_CheckedChange(Checked As Boolean)
    Log(clvSupplierItems.IsInitialized)
    Dim index As Int = clvSupplierItems.GetItemFromView(Sender)
    Dim stocks As List
    stocks.Initialize
    Log("records "&clvSupplierItems.Size)

    For p = 0 To clvSupplierItems.Size-1
        Dim sp As supplierproduct
        Dim pnl As B4XView = clvSupplierItems.GetPanel(p)
        Dim pnl2 As B4XView=pnl.GetView(0)
 
        Log("index "&index)

        If p <> index Then
            sp.supplier=pnl2.GetView(0).Text
            sp.supplier_reference=pnl2.GetView(2).Text
            sp.wholesale_price=pnl2.GetView(1).Text
            sp.Currency=pnl2.GetView(3).Text
            sp.chkDfltSupplier.Checked=False
        Else
            sp.supplier=pnl2.GetView(0).Text
            sp.supplier_reference=pnl2.GetView(2).Text
            sp.wholesale_price=pnl2.GetView(1).Text
            sp.Currency=pnl2.GetView(3).Text
            sp.chkDfltSupplier.Checked=True
        End If
            stocks.Add(sp)
 
    Next
            clvSupplierItems.Clear
        For t=0 To stocks.Size-1
            clvSupplierItems.Add(createitemStock(stocks.Get(t)),createitemStock(stocks.Get(t)))
        Next
End Sub

Sub createitemStock (supplierStock As supplierproduct ) As B4XView

    Dim p As B4XView = Main.xui.CreatePanel("")
    p.SetLayoutAnimated(0, 0, 0, 35,40dip)
    p.LoadLayout("SupplierItemCLVLine")
    lblNameSupplier.Text=supplierStock.supplier
    dtaSupplierReference.Text=supplierStock.supplier_reference
    dtaSupplierPrice.Text=supplierStock.wholesale_price
    dtaCurrency.Text=supplierStock.Currency
    chkDefaultSupplier.Checked=supplierStock.chkDfltSupplier.Checked
 
    p.Tag=supplierStock
    Return p
End Sub

Private Sub chkSupplierItem_CheckedChange(Checked As Boolean)
        Dim sp As supplierproduct
        Dim index As Int = clvSuppliers.GetItemFromView(Sender)
        Dim pnl As B4XView = clvSuppliers.GetPanel(index)
        Dim pnl2 As B4XView = pnl.GetView(0)
        Dim suppliername As String = pnl2.GetView(0).Text
 
        Dim chksuppliers As Boolean
            chksuppliers= pnl2.GetView(1).Checked
    If chksuppliers = True Then
            pnlSupplItems.Visible=True
            sp.supplier=suppliername
        Dim queryStock As String = $"
            SELECT     pp.wholesale_price
                from pss_product as pp
                where pp.id_product = ?
                 "$
        Dim suppliersItems As dataTable
            suppliersItems.Initialize(Main.sql2,queryStock,Array As String(Main.lblid.Text))
        Dim queryStock As String = $"
        SELECT     name
            from pss_currency_lang
             "$
        Dim currency As dataTable
            currency.Initialize(Main.sql2,queryStock,Null)
            sp.Currency=currency.getRowValues(0)(0)
            sp.wholesale_price=suppliersItems.getRowValues(0)(0)
            suppliersItem.AddAll(Array As String(suppliername))
            clvSupplierItems.Add(createitemStock(sp),createitemStock(sp))
    Else
        Dim index As Int = suppliersItem.IndexOf(suppliername)
            clvSupplierItems.RemoveAt(index)
            suppliersItem.RemoveAt(index)
    End If
 
    If suppliersItem.Size= 0 Then
            pnlSupplItems.Visible=False
    End If
     
End Sub


This gives me an error
Options
Error occurred on line: 260 (clsOptions)
java.lang.NullPointerException
at b4j.example.clsoptions._createitemstock(clsoptions.java:288)
at b4j.example.clsoptions._chksupplieritem_checkedchange(clsoptions.java:387)
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:629)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at jdk.internal.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
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:111)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.BA$1.run(BA.java:236)
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)
The line I get an error on is
B4X:
    chkDefaultSupplier.Checked=supplierStock.chkDfltSupplier.Checked

Which is in the createitemstock sub

if I make that line
B4X:
chkDefaultSupplier=supplierStock.chkDfltSupplier

then I get a error code on a different line
Error occurred on line: 287 (clsOptions)
java.lang.NullPointerException
at b4j.example.clsoptions._chkdefaultsupplier_checkedchange(clsoptions.java:214)
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:629)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:237)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:167)
at jdk.internal.reflect.GeneratedMethodAccessor3.invoke(Unknown Source)
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:111)
at anywheresoftware.b4a.shell.ShellBA.raiseEvent2(ShellBA.java:100)
at anywheresoftware.b4a.BA$1.run(BA.java:236)
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)

which is in the chkDefaultSupplier_checkedChange Sub

B4X:
sp.chkDfltSupplier.Checked=False

I have tried multiple ways of doing it. Seems like it needs a default value of false and not a Null

This is how the screen looks if I do not add in the Checkbox values. If i click on a Checkbox then it does read the CLV info and write it out again correctly, except all the checkboxes are always un-checked

1727959530205.png


Any Ideas, thank you
 
Last edited:
Solution
1728017075382.png
1728017102411.png

This is another example of the CustomListView view with a checkbox per item.
Only one item can be checked.
The clv1_ItemClick subroutine also checks the checkbox of the selected item.
1728017306924.png

And here's the code:
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Public checkbx1 As CheckBox
    Public lblname As Label
    Private clv1 As CustomListView
End Sub
Public Sub Initialize
    B4XPages.GetManager.LogEvents = True
End Sub
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    fill_clv
End Sub
Private Sub fill_clv
    Dim names As List = Array As String("Peter","Ann","Nick","Sandra","John")
    For i = 0 To 4
        Dim pnl As...

Peter Lewis

Active Member
Licensed User
Longtime User
I can suggest 2 methods which I have used personally:
1. Using a Sub to create views by code and not loading using loadlayout ( allows you to set the default parameters and it is way faster if you have lots of records )
2. Use chklist library by informatix here: https://www.b4x.com/android/forum/threads/class-checklist.18853/
I have never had problems with CLV and checkboxes before. the chklist lib looks like it was for B4A. I do not see that it allows for a checkbox that is unique over multiple records. This table would only be a handful of record maybe up to 5. I will look into doing it by code (Have not done that before). Will have to look at the xCLV lib code to get an idea. What I do not understand is I have done this before and it worked. Just above this CLV I have another CLV which populates this one depending on a Checkbox, no problems there. It is something I am missing , will have to sleep on it and with fresh eyes might find the issue.
 
Last edited:
Upvote 0

Daestrum

Expert
Licensed User
Longtime User
I know it's not using clv (just a plain old tableview), but it may help you work out your logic for making only 1 checkbox checked.
B4X:
Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI 
    Private Button1 As B4XView
    Dim tv As TableView
    Dim auto As Boolean=False
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
    tv.Initialize("tv")
    tv.SetColumns(Array("tick","number","name"))
    tv.SetColumnWidth(0,100)
    tv.SetColumnWidth(1,100)
    tv.SetColumnWidth(2,100)
    MainForm.RootPane.AddNode(tv,10,10,300,300)
    For a = 0 To 5
        Dim chk As CheckBox
        chk.Initialize("chk")
        If a = 3 Then
            chk.Checked = True
        Else
            chk.Checked = False
        End If
        tv.Items.Add(Array(chk,Null,Null)) ' just interested in checkbox
    Next
End Sub

Sub chk_CheckedChange(Checked As Boolean)
    If Checked = False Then Return ' changing checkbox by code also triggers this sub dont want to know about true > false changes
    Dim sen As Object = Sender
    Dim pos As Int = 0
    For Each ob() As Object In tv.Items
        If ob(0) <> sen Then ' the checkbox
            ob(0).As(CheckBox).Checked = False
        End If
        pos = pos + 1
    Next
End Sub
 
Upvote 0

PaulMeuris

Active Member
Licensed User
1728017075382.png
1728017102411.png

This is another example of the CustomListView view with a checkbox per item.
Only one item can be checked.
The clv1_ItemClick subroutine also checks the checkbox of the selected item.
1728017306924.png

And here's the code:
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Public checkbx1 As CheckBox
    Public lblname As Label
    Private clv1 As CustomListView
End Sub
Public Sub Initialize
    B4XPages.GetManager.LogEvents = True
End Sub
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    fill_clv
End Sub
Private Sub fill_clv
    Dim names As List = Array As String("Peter","Ann","Nick","Sandra","John")
    For i = 0 To 4
        Dim pnl As Pane = set_clv1_item("clvitem_layout",i,names.Get(i))
        clv1.Add(pnl,i)       
    Next
End Sub
Private Sub set_clv1_item(strlayout As String, index As Int, name As String) As Pane
    Dim pn As Pane
    pn.Initialize("")
    pn.LoadLayout(strlayout)
    pn.SetLayoutAnimated(0, 0, 0, 550,60)
    checkbx1.Checked = False
    checkbx1.Tag = index
    lblname.Text = name
    Return pn
End Sub
Private Sub checkbx1_CheckedChange(Checked As Boolean)
    If Checked = False Then Return            ' only change when a checkbox is checked   
    Dim cbxsel As CheckBox = Sender
    Dim tag As Int = cbxsel.Tag
    For i = 0 To clv1.Size -1
        If i <> tag Then
            clv1.GetPanel(i).GetView(0).Checked = False
        End If
    Next
    clv1.GetPanel(tag).GetView(0).Checked = True
End Sub
Private Sub clv1_ItemClick (Index As Int, Value As Object)
    If clv1.GetPanel(Index).GetView(0).Checked = False Then
        clv1.GetPanel(Index).GetView(0).Checked = True
    End If
End Sub
The first test in the checkbx1_CheckedChange subroutine is important (as indicated by @Daestrum ).
The checkbox tag property is used to set the clv1 index and to test for the current item index in the checkbx1_CheckedChange subroutine.
The complete source code and layouts are available in the attachment (testenvironment72.zip).
 

Attachments

  • 1728017048131.png
    1728017048131.png
    12.4 KB · Views: 33
  • testenvironment72.zip
    4.4 KB · Views: 38
Upvote 1
Solution

Peter Lewis

Active Member
Licensed User
Longtime User
View attachment 157491 View attachment 157492
This is another example of the CustomListView view with a checkbox per item.
Only one item can be checked.
The clv1_ItemClick subroutine also checks the checkbox of the selected item.
View attachment 157493
And here's the code:
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Public checkbx1 As CheckBox
    Public lblname As Label
    Private clv1 As CustomListView
End Sub
Public Sub Initialize
    B4XPages.GetManager.LogEvents = True
End Sub
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    fill_clv
End Sub
Private Sub fill_clv
    Dim names As List = Array As String("Peter","Ann","Nick","Sandra","John")
    For i = 0 To 4
        Dim pnl As Pane = set_clv1_item("clvitem_layout",i,names.Get(i))
        clv1.Add(pnl,i)      
    Next
End Sub
Private Sub set_clv1_item(strlayout As String, index As Int, name As String) As Pane
    Dim pn As Pane
    pn.Initialize("")
    pn.LoadLayout(strlayout)
    pn.SetLayoutAnimated(0, 0, 0, 550,60)
    checkbx1.Checked = False
    checkbx1.Tag = index
    lblname.Text = name
    Return pn
End Sub
Private Sub checkbx1_CheckedChange(Checked As Boolean)
    If Checked = False Then Return            ' only change when a checkbox is checked  
    Dim cbxsel As CheckBox = Sender
    Dim tag As Int = cbxsel.Tag
    For i = 0 To clv1.Size -1
        If i <> tag Then
            clv1.GetPanel(i).GetView(0).Checked = False
        End If
    Next
    clv1.GetPanel(tag).GetView(0).Checked = True
End Sub
Private Sub clv1_ItemClick (Index As Int, Value As Object)
    If clv1.GetPanel(Index).GetView(0).Checked = False Then
        clv1.GetPanel(Index).GetView(0).Checked = True
    End If
End Sub
The first test in the checkbx1_CheckedChange subroutine is important (as indicated by @Daestrum ).
The checkbox tag property is used to set the clv1 index and to test for the current item index in the checkbx1_CheckedChange subroutine.
The complete source code and layouts are available in the attachment (testenvironment72.zip).
I ran the demo and it looks good, Thank you. I will have time later to go through and see how to change my code to use your method
 
Upvote 0

Peter Lewis

Active Member
Licensed User
Longtime User
View attachment 157491 View attachment 157492
This is another example of the CustomListView view with a checkbox per item.
Only one item can be checked.
The clv1_ItemClick subroutine also checks the checkbox of the selected item.
View attachment 157493
And here's the code:
B4X:
Sub Class_Globals
    Private Root As B4XView
    Private xui As XUI
    Public checkbx1 As CheckBox
    Public lblname As Label
    Private clv1 As CustomListView
End Sub
Public Sub Initialize
    B4XPages.GetManager.LogEvents = True
End Sub
Private Sub B4XPage_Created (Root1 As B4XView)
    Root = Root1
    Root.LoadLayout("MainPage")
    fill_clv
End Sub
Private Sub fill_clv
    Dim names As List = Array As String("Peter","Ann","Nick","Sandra","John")
    For i = 0 To 4
        Dim pnl As Pane = set_clv1_item("clvitem_layout",i,names.Get(i))
        clv1.Add(pnl,i)      
    Next
End Sub
Private Sub set_clv1_item(strlayout As String, index As Int, name As String) As Pane
    Dim pn As Pane
    pn.Initialize("")
    pn.LoadLayout(strlayout)
    pn.SetLayoutAnimated(0, 0, 0, 550,60)
    checkbx1.Checked = False
    checkbx1.Tag = index
    lblname.Text = name
    Return pn
End Sub
Private Sub checkbx1_CheckedChange(Checked As Boolean)
    If Checked = False Then Return            ' only change when a checkbox is checked  
    Dim cbxsel As CheckBox = Sender
    Dim tag As Int = cbxsel.Tag
    For i = 0 To clv1.Size -1
        If i <> tag Then
            clv1.GetPanel(i).GetView(0).Checked = False
        End If
    Next
    clv1.GetPanel(tag).GetView(0).Checked = True
End Sub
Private Sub clv1_ItemClick (Index As Int, Value As Object)
    If clv1.GetPanel(Index).GetView(0).Checked = False Then
        clv1.GetPanel(Index).GetView(0).Checked = True
    End If
End Sub
The first test in the checkbx1_CheckedChange subroutine is important (as indicated by @Daestrum ).
The checkbox tag property is used to set the clv1 index and to test for the current item index in the checkbx1_CheckedChange subroutine.
The complete source code and layouts are available in the attachment (testenvironment72.zip).
Works perfectly, Thank you
 
Upvote 0
Top