Android Question CustomListView.RemoveAt does not remove all selected rows

mfstuart

Active Member
Licensed User
Longtime User
I'm testing a part of my app to allow the user to LongClick on a CLV. The code in turn would add the Index to a List.
The user can then select another CLV row and it does the same, adds the Index to a List.

In the CLV LongClick code, my ActionBars (Panels) are manipulated to hide the "main bar" and show the "delete bar". They are panels with other views on them, like Labels and Buttons.

On the "delete bar" there is a Button - btnDelete. When pressed it cycles thru the List of Indexes and runs the CLV.RemoveAt(Index) code.
Here's the issue: the code only removes the first Index in the List and not the others. That is of course if the user selected more than one row in the CLV.
The zipped project is attached.

The btnDelete code is not working as desired.
Does the CLV.RemoveAt(Index) re-index the CLV indexes?

This code is for the "Default" type of B4A project:

Activity_Create:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Main")
    SelectedItems.Initialize
    Load_Data
End Sub

Load_Data:
Sub Load_Data
    clvItems.Clear
    For i = 0 To 20
        clvItems.AddTextItem("Item " & i,i)
    Next
End Sub

LongClick:
Private Sub clvItems_ItemLongClick(Index As Int, Value As Object)
    pnlActionBar.Visible = False
    pnlDeleteBar.Visible = True
    Dim p As Panel = clvItems.GetPanel(Index)
    p.Color = xui.Color_LightGray
    SelectedItems.Add(Index)
    lblDeleteTitle.Text = "Delete " & SelectedItems.Size
End Sub

btnDelete_Click:
Private Sub btnDelete_Click
    Dim ItemCount As Int = SelectedItems.Size        '<=== SelectedItems is a List
    Log("ItemCount: " & ItemCount)
    
    If ItemCount > 0 Then
        For i = 0 To ItemCount - 1
            Dim idx As Int = SelectedItems.Get(i)
            Log(idx)
            clvItems.RemoveAt(idx)    '<=== should remove all items in List, BUT DOES NOT!
        Next
        ToastMessageShow(ItemCount & " items deleted.", False)
    Else
        ToastMessageShow("No Items selected!", False)
    End If
End Sub
 

Attachments

  • LongTouch_Delete_Demo.zip
    10.6 KB · Views: 66

Sagenut

Expert
Licensed User
Longtime User
Try with
B4X:
Private Sub btnDelete_Click
    SelectedItems.Sort(False)
    Dim ItemCount As Int = SelectedItems.Size        '<=== SelectedItems is a List
    Log("ItemCount: " & ItemCount)
 
    If ItemCount > 0 Then
        For i = 0 to ItemCount - 1
            Dim idx As Int = SelectedItems.Get(i)
            Log(idx)
            clvItems.RemoveAt(idx)
        Next
        ToastMessageShow(ItemCount & " items deleted.", False)
    Else
        ToastMessageShow("No Items selected!", False)
    End If
End Sub
 
Last edited:
Upvote 0

mfstuart

Active Member
Licensed User
Longtime User
Thank you @Sagenut. That worked exactly as I'd expect it to.
Your change:
B4X:
For i = (ItemCount - 1) To 0 Step -1        '<== your correction, which deletes all selected rows that I have in a List.
    Dim idx As Int = SelectedItems.Get(i)
    'Log(idx)
    clvItems.RemoveAt(idx)
Next
 
Upvote 0

mfstuart

Active Member
Licensed User
Longtime User
@Sagenut,
I just noticed that your reply does not show your changes that I received in the email I received from the Forum.
Why did you change @Sagenut code. It works the way he had it. using this: For i = 0 to ItemCount - 1
That's the way I originally had my code, if you look in my #1 post.

Like I said in my reply, that's what I received in my email from Sagenut. And that kinda works, if you select the rows going down the list.
But if you select rows further down, then some from the top, the ones further down are not removed.
 
Upvote 0

Sagenut

Expert
Licensed User
Longtime User
Why did you change @Sagenut code. It works the way he had it. using this: For i = 0 to ItemCount - 1
I changed my initial code proposed thinking that still was not seen.
My fault.
 
Last edited:
Upvote 0

mfstuart

Active Member
Licensed User
Longtime User
So what is the final sub: Sub BtnDelete_Click look like?
I think B4A is re-indexing the CLV after issuing the .RemoveAt command.
How do I know? The Index value is not the same as at the time of initially loading the CLV.

@Erel - is this the case?

Never mind. I see that they are re-indexed.
Add this handler to see the Index when clicked after a delete:
B4X:
Private Sub clvItems_ItemClick (Index As Int, Value As Object)
    Log("Current Index: " & Index & "," & Value)
End Sub
It's different from the original Index value.

So how do you handle multi-row delete?
I'm working on it.
#See next post for a solution.

Thanx,
Mark Stuart
 
Last edited:
Upvote 0

mfstuart

Active Member
Licensed User
Longtime User
I think I've found a solution when removing multiple rows from a CLV.
If you are interested in the whole solution - as I've made more changes than the Delete button - download the Project attached to this post.
Here's the btnDelete_Click code:
B4X:
Sub btnDelete_Click
    Dim rowCount As Int = clvItems.Size
    
    For i = (rowCount - 1) To 0 Step -1
        Dim p As Panel = clvItems.GetPanel(i)
        Dim cns As Canvas
        cns.Initialize(p)
        
        'Get the color set in the ItemLongClick event.
        'If it is set in the ItemLongClick it will not = 0.
        'The color will = 0 by default.
        Dim clr As Int = cns.Bitmap.GetPixel(0,0)
        'Delete the row because it was selected by the user.
        If clr <> 0 Then
            clvItems.RemoveAt(i)
            'You have to resize the rowCount variable after the removal of each row,
            'otherwise the OutOfBounds error will occur.
            rowCount = clvItems.Size
        End If
    Next
End Sub
 

Attachments

  • LongTouch_Delete_Demo.zip
    11.8 KB · Views: 61
Upvote 0

Sagenut

Expert
Licensed User
Longtime User
So how do you handle multi-row delete?
Did you tried the code in my post (after the modify)?
It should handle the multi row delete.
It's just your original Sub with only the addition of
B4X:
SelectedItems.Sort(False)
at the beginning.
This will sort the List, populated by the indexes numbers, in descending order from bigger to smaller, to make the item delete start from the bottom of the CLV.
This should avoid errors even if you selected the item to delete in random order.
 
Upvote 0

Sagenut

Expert
Licensed User
Longtime User
So what is the final sub: Sub BtnDelete_Click look like?
The one proposed by me it's the one at post #2.
My fault to modify it without giving advice.
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
Your sub in post #2 works. However, if you selected items to delete then click the delete button, it works properly. But, if you seleclect more items to delete after the first delete and click the delete button, it deletes a lot more items than what you selected. Therefore, you can correct it by adding this line at the end of the sub: SelectedItems.Clear. So your sub can be something like this:
B4X:
Private Sub btnDelete_Click
    SelectedItems.Sort(False)
    Dim ItemCount As Int = SelectedItems.Size        '<=== SelectedItems is a List
    Log("ItemCount: " & ItemCount)
    If ItemCount > 0 Then
        For i =  0 To ItemCount - 1
            clvItems.RemoveAt(SelectedItems.Get(i))
        Next
        ToastMessageShow(ItemCount & " items deleted.", False)
    Else
        ToastMessageShow("No Items selected!", False)
    End If    
    SelectedItems.Clear
End Sub
 
Upvote 0

Sagenut

Expert
Licensed User
Longtime User
I thought that the List was reinitialized or cleared somewhere else.
Good point.
 
Upvote 0

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
This is a standard problem, that we all encounter at one time or another

If you delete an item from a list all the other items "move up"

In a list of 6 items, if you delete item 4, what was item 5 becomes item 4.

The solutions is always to delete items from a list in reverse order.

Post 9 showed this.

See this StackOverflow message.

B4X:
'This code wil always produce problems as the iterator will get confused as to which item it is on

For i =  0 To ItemCount - 1
     clvItems.RemoveAt(SelectedItems.Get(i))
 Next

Should be:
for i = itemcount-1 to 0 step -1
     clvItems.RemoveAt(SelectedItems.Get(i))
 Next
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
Should be: for i = itemcount-1 to 0 step -1 clvItems.RemoveAt(SelectedItems.Get(i)) Next
Here is why I disagree with you in this case Andrew:
1. The OP sorts the list in descending order at the top of the Btndelete _click sub.
2. The OP is removing items from the Customlistview not from the list. The list is not affected.
3. Try your code in the OP's project, you will see it will not work properly.
3. If you can put your assertion in a small project, we will test it. What say you.
 
Upvote 0

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
Ha!. That's what you get for just reading the comments and not trying the code.

The answer is to "simulate" the iterating through the list in reverse.

so
B4X:
Private Sub btnDelete_Click
    Dim ItemCount As Int = SelectedItems.Size        '<=== SelectedItems is a List
    Log("ItemCount: " & ItemCount)
    SelectedItems.Sort(False)    <===== This is the new line
    If ItemCount > 0 Then
        For i = 0 To ItemCount - 1
            Dim idx As Int = SelectedItems.Get(i)
            Log(idx)
            clvItems.RemoveAt(idx)
        Next
        ToastMessageShow(ItemCount & " items deleted.", False)

    Else
        ToastMessageShow("No Items selected!", False)
    End If
End Sub

Just tried this on a phone and it appear to work.
 
Upvote 0

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
Hit send too soon. There is a bug in the code:

B4X:
Private Sub btnDelete_Click
    Dim ItemCount As Int = SelectedItems.Size        '<=== SelectedItems is a List
    Log("ItemCount: " & ItemCount)
    SelectedItems.Sort(False)   
    If ItemCount > 0 Then
        For i = 0 To ItemCount - 1
            Dim idx As Int = SelectedItems.Get(i)
            Log(idx)
            clvItems.RemoveAt(idx)
        Next
        ToastMessageShow(ItemCount & " items deleted.", False)
        selecteditems.clear   <=== Clear the list or theses indexes will be deleted again
    Else
        ToastMessageShow("No Items selected!", False)
    End If
End Sub
 
Upvote 0

Mahares

Expert
Licensed User
Longtime User
I think I've found a solution
Now that you got your project looking almost like the way you want it, I would recommend you look into adding one more feature that may enhance it:
When you long click an item or several items , but before you actually clcik the delete button, you realize that you long clicked an item inadventently. You have no way to UNselect it from the list. You have to exit out with the other button and get back in to start the selection all over again.
 
Upvote 0
Top