Android Question [SOLVED] Strange behavior trying to remove multiple items on a xCustomListView

Alejandro Moyano

Member
Licensed User
Hi im trying to remove multiple items with clv.RemoveAt(index) but works a bit odd, sometimes delete 2 items of 3, others 1 of 3.
Am i doing it right?

Screenshot_2019-08-05-15-52-47.png Screenshot_2019-08-05-15-53-01.png

B4X:
Sub Class_Globals
    Private xui As XUI
    Private vActivity As B4XView
    Private vCodProducto As Int
    Private lblNombreProducto, lblConIva, lblSinIVA As Label
    Private txtCantidad As B4XView
    Type ItemPedido (vCodProducto As Int,txtCantidad As B4XView,lblNombreProducto As Label, lblConIva As Label, lblSinIVA As Label)
    Private clvProductos As CustomListView
    Private vSelectedItems As List
    Private vBaseColor, vSelectedColor As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(activity As B4XView)
    vActivity = activity
    vActivity.RemoveAllViews
    vActivity.LoadLayout("frmNuevoPedido")
    vSelectedItems.Initialize
    vBaseColor = 0xFFA8CDAA
    vSelectedColor = 0xFF0686CC
    CargarPedido
End Sub

Private Sub CargarPedido
    For i = 1 To 50
        Dim icp As ItemPedido
        icp.Initialize
        clvProductos.Add(CrearItemProducto(i,"Hola"&i,i*1000,i*1000*1.21,i,icp),icp)
    Next
End Sub

Private Sub CrearItemProducto(codigo As Int,nombre As String,precio_sin_iva As String, precio_con_iva As String, cantidad As Int, icp As ItemPedido) As B4XView
    Dim item As B4XView = xui.CreatePanel("")
    item.SetLayoutAnimated(0,0,0,clvProductos.GetBase.Width,85dip)
    item.LoadLayout("ItemPedido")
    vCodProducto = codigo
    lblNombreProducto.Text = nombre
    lblConIva.Text = precio_con_iva
    lblSinIVA.Text = precio_sin_iva
    txtCantidad.Text = cantidad
    txtCantidad.Color = xui.Color_Transparent
    icp.lblConIva = lblConIva
    icp.lblSinIVA = lblSinIVA
    icp.lblNombreProducto = lblNombreProducto
    icp.vCodProducto = vCodProducto
    Return item
End Sub

Sub cmdMenu_Click
    #if B4A
    StartActivity(Menu)
    #end if
End Sub

Sub cmdExit_Click
    #If B4A
    CallSubDelayed(Main,"CloseMe")
    #End If
End Sub


Sub cmdEditClient_Click
    'mesaje con input
End Sub

Sub cmdFinalizar_Click
    
End Sub

'Delete all selected items
Sub cmdEliminar_Click
    For Each i In vSelectedItems
        Log(i)
        clvProductos.RemoveAt(i)
    Next
    vSelectedItems.Clear
End Sub

Sub cmdAnadir_Click
    
End Sub

'This function handle the painting and selection of items.
Sub clvProductos_ItemClick (Index As Int, Value As Object)
    If vSelectedItems.IndexOf(Index) <> -1 Then
        clvProductos.GetPanel(Index).Color = vBaseColor
        vSelectedItems.RemoveAt(vSelectedItems.IndexOf(Index))
    Else
        vSelectedItems.Add(Index)
        clvProductos.GetPanel(Index).Color = vSelectedColor
    End If
End Sub
 

Jorge M A

Well-Known Member
Licensed User
Longtime User
Am i doing it right?
Probably not.
Maybe you are confusing your label (1,2,3,4...) with the index.
Every time you remove an item, both the size and the indexes of the lists are recalculated, so that if you remove the index 2, which was before the 3, becomes 2.
You have to look for the way to "mark" the selected ones to eliminate, and look for the new index assigned to erase it with its new position, with each removal.
 
Upvote 0

Alejandro Moyano

Member
Licensed User
TXS @Jorge M A seems i don't considerate the recalculated index, i solved doing this:

B4X:
Sub Class_Globals
    Private xui As XUI
    Private vActivity As B4XView
    Private vCodProducto As Int
    Private lblNombreProducto, lblConIva, lblSinIVA As Label
    Private txtCantidad As B4XView
    Type ItemPedido (vCodProducto As Int,txtCantidad As B4XView,lblNombreProducto As Label, lblConIva As Label, lblSinIVA As Label)
    Private clvProductos As CustomListView
    Private vSelectedProductCodes As List
    Private vBaseColor, vSelectedColor As Int
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize(activity As B4XView)
    vActivity = activity
    vActivity.RemoveAllViews
    vActivity.LoadLayout("frmNuevoPedido")
    vSelectedProductCodes.Initialize
    vBaseColor = 0xFFA8CDAA
    vSelectedColor = 0xFF0686CC
    CargarPedido
End Sub

Private Sub CargarPedido
    For i = 1 To 50
        Dim icp As ItemPedido
        icp.Initialize
        clvProductos.Add(CrearItemProducto(i,"Hola"&i,i*1000,i*1000*1.21,i,icp),icp)
    Next
End Sub

Private Sub CrearItemProducto(codigo As Int,nombre As String,precio_sin_iva As String, precio_con_iva As String, cantidad As Int, icp As ItemPedido) As B4XView
    Dim item As B4XView = xui.CreatePanel("")
    item.SetLayoutAnimated(0,0,0,clvProductos.GetBase.Width,85dip)
    item.LoadLayout("ItemPedido")
    vCodProducto = codigo
    lblNombreProducto.Text = nombre
    lblConIva.Text = precio_con_iva
    lblSinIVA.Text = precio_sin_iva
    txtCantidad.Text = cantidad
    txtCantidad.Color = xui.Color_Transparent
    icp.lblConIva = lblConIva
    icp.lblSinIVA = lblSinIVA
    icp.lblNombreProducto = lblNombreProducto
    icp.vCodProducto = vCodProducto
    Return item
End Sub

Sub cmdMenu_Click
    #if B4A
    StartActivity(Menu)
    #end if
End Sub

Sub cmdExit_Click
    #If B4A
    CallSubDelayed(Main,"CloseMe")
    #End If
End Sub


Sub cmdEditClient_Click
    'mesaje con input
End Sub

Sub cmdFinalizar_Click
    
End Sub

'Delete all selected items
Sub cmdEliminar_Click
    For Each i In vSelectedProductCodes
        DeleteItemByCode(i)
    Next
    vSelectedProductCodes.Clear
End Sub

Sub cmdAnadir_Click
    
End Sub

'Delete an item
Private Sub DeleteItemByCode(code As Int)
    For i = 0 To clvProductos.Size-1
        Dim icp As ItemPedido = clvProductos.GetValue(i)
        If icp.vCodProducto = code Then
            clvProductos.RemoveAt(i)
            Exit
        End If
    Next
End Sub

'This function handle the painting and selection of items.
Sub clvProductos_ItemClick (Index As Int, Value As Object)
    Dim item_pedido As ItemPedido = Value
    If vSelectedProductCodes.IndexOf(item_pedido.vCodProducto) <> -1 Then
        clvProductos.GetPanel(Index).Color = vBaseColor
        vSelectedProductCodes.RemoveAt(item_pedido.vCodProducto)
    Else
        vSelectedProductCodes.Add(item_pedido.vCodProducto)
        clvProductos.GetPanel(Index).Color = vSelectedColor
    End If
End Sub

Plus, it allow me delete a product directly by product code.. the only noise is that is not efficient.. but i figure that the user never will have a list with more than 15 productos.
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
just go backwards through your selected items list (if that list is correctly sorted) and delete the items.

the shifting won't affect your routine since you will look up items for deletion earlier in the list than where the shifting happend.
 
Upvote 0

emexes

Expert
Licensed User
im trying to remove multiple items
Minor thought: while you're in the land of O(n), is it worth searching the whole list just in case there are multiple items with .vCodProducto = code?

Yeah, it'll double your search time (presumably from negligible to negligible) but could save you heaps of debugging time if it ever occurs. Not that I've ever experienced such issues myself... ;-)

Perhaps even better: keep a count of how many items are deleted, and log if anything other than 1.
B4X:
'Delete an item
Private Sub DeleteItemByCode(code As Int)
    For i = 0 To clvProductos.Size-1
        Dim icp As ItemPedido = clvProductos.GetValue(i)
        If icp.vCodProducto = code Then
            clvProductos.RemoveAt(i)
            Exit
        End If
    Next
End Sub
 
Last edited:
Upvote 0
Top