Android Question How to Remove a Row on a Table

Eric McDonald

Member
Licensed User
Hello, I am currently using the Table code that I found on the forums a few weeks back and have been liking it so far. But I am currently stuck on how to remove a row. I followed the Sub procedure and implemented that into my main class file but when I run the program, I get the error (Object must first be initialized (List)), which the full error can be found below. I would appreciate any help. Thank you.

Code from Table Class:
B4X:
'Version 1.11

#DesignerProperty: Key:    ZeroSelection, DisplayName: ZeroSelection, FieldType: Boolean, DefaultValue: False, Description: True > when a selected row is pressed it will be unselected False > it remains selected.

Sub Class_Globals
    Private StringUtils1 As StringUtils
    Private SV As ScrollView
    Private Header As Panel
    Private Callback As Object
    Private Event As String
    Private SelectedRow As Int
    Public SelectedRows As List
    Private Data As List
    Private LabelsCache As List
    Private minVisibleRow, maxVisibleRow As Int
    Private visible As Boolean
    Private visibleRows As Map
    Private NumberOfColumns, ColumnWidth As Int
    Public RowHeight, HeaderColor, TableColor, FontColor, HeaderFontColor, SelectedRowColor As Int
    Public FontSize As Float
    Type RowCol (Row As Int, Col As Int)
    Public Alignment As Int
    Public SelectedDrawable(), Drawable1(), Drawable2() As Object
    Dim SelectedRow = -1 As Int
    Private register As Table
    Public btnRemoveRow As Button
                                                                                        'These are the initial table settings
    HeaderColor = Colors.DarkGray
    RowHeight = 62dip
    TableColor = Colors.Black
    FontColor = Colors.White
    HeaderFontColor = Colors.Yellow
    SelectedRowColor = Colors.Black
    FontSize = 25
    Alignment = Gravity.CENTER
 
    Private IsMultiSelect As Boolean = False
    Public mZeroSelection = True As Boolean
End Sub

                                                                                        'Initialize the table
Public Sub Initialize (CallbackModule As Object, EventName As String, vNumberOfColumns As Int)
'    register.Initialize(Me, "Register", 5)
    SV.Initialize2(0, "SV")
    SV.Panel.Color = TableColor
    Callback = CallbackModule
    Event = EventName
    innerClearAll(vNumberOfColumns)
End Sub

                                                                                        'Clears the table
Public Sub ClearAll
    innerClearAll(NumberOfColumns)
End Sub

                                                                                        'Sets the columns widths
'Example: <code>Table1.SetColumnsWidths(Array As Int(100dip, 30dip, 30dip, 100%x - 160dip))</code>
Public Sub SetColumnsWidths(Widths() As Int)
    Dim v As View
    For i = 0 To Widths.Length - 1
        v = Header.GetView(i)
        v.Width = Widths(i) - 1dip
        If i > 0 Then
            v.Left = Header.GetView(i-1).Left + Widths(i-1) + 1dip
        End If
    Next
    Dim lbls() As Label
    For i = 0 To visibleRows.Size - 1
        lbls = visibleRows.GetValueAt(i)
        For lbl = 0 To lbls.Length - 1
            lbls(lbl).SetLayout(Header.GetView(lbl).Left, lbls(lbl).Top, _
                Header.GetView(lbl).Width, RowHeight)
        Next
    Next
End Sub

Public Sub innerClearAll(vNumberOfColumns As Int)
    For i = SV.Panel.NumberOfViews -1 To 0 Step -1
        SV.Panel.RemoveViewAt(i)
    Next
    NumberOfColumns = vNumberOfColumns
    Dim Drawable1(NumberOfColumns) As Object
    Dim Drawable2(NumberOfColumns) As Object
    Dim SelectedDrawable(NumberOfColumns) As Object
    For i = 0 To NumberOfColumns - 1
        Dim cd1, cd2, cd3 As ColorDrawable
        cd1.Initialize(Colors.Black, 0)
        cd2.Initialize(Colors.Black, 0)
        cd3.Initialize(0xFFFF7B00, 0)
        Drawable1(i) = cd1
        Drawable2(i) = cd2
        SelectedDrawable(i) = cd3
    Next
    SV.Panel.Height = 0
    SelectedRow = -1
    minVisibleRow = -1
    maxVisibleRow = 0
    Data.Initialize
    LabelsCache.Initialize
    visibleRows.Initialize
    SV.ScrollPosition = 0
    SV.ScrollPosition = 0
    For i = 1 To 80 'fill the cache to avoid delay on the first touch
        LabelsCache.Add(CreateNewLabels)
    Next
    If visible Then
        SV_ScrollChanged(0)
    End If
End Sub

Private Sub SV_ScrollChanged(Position As Int)
    Dim currentMin, currentMax As Int
    currentMin = Max(0, Position / RowHeight - 30)
    currentMax = Min(Data.Size - 1, (Position + SV.Height) / RowHeight + 30)
    If minVisibleRow > -1 Then
        If minVisibleRow < currentMin Then
            'need to hide the upper rows
            For I = minVisibleRow To Min(currentMin - 1, maxVisibleRow)
                HideRow(I)
            Next
        Else If minVisibleRow > currentMin Then
            'need to show the upper rows
            For I = currentMin To Min(minVisibleRow - 1, currentMax)
                ShowRow(I)
            Next
        End If
        If maxVisibleRow > currentMax Then
            'need to hide the lower rows
            For I = maxVisibleRow To Max(currentMax + 1, minVisibleRow) Step -1
                HideRow(I)
            Next
        Else If maxVisibleRow < currentMax Then
            'need to show the lower rows
            For I = currentMax To Max(maxVisibleRow + 1, currentMin) Step -1
                ShowRow(I)
            Next
        End If
    End If
    minVisibleRow = currentMin
    maxVisibleRow = currentMax
End Sub

'Adds the tablet to the activity.
Public Sub AddToActivity(Act As Activity, Left As Int, Top As Int, Width As Int, Height As Int)
    visible = True
    Header.Initialize("")
    Header.Color = TableColor
    Act.AddView(Header, Left, Top, Width, RowHeight)
    Act.AddView(SV, Left, Top + RowHeight, Width, Height - RowHeight)
    ColumnWidth = SV.Width / NumberOfColumns
 
    SV_ScrollChanged(0)

End Sub

'Adds a row to the table
'Example:<code>Table1.AddRow(Array As String("aaa", "ccc", "ddd", "eee"))</code>
Public Sub AddRow(Values() As String)
    If Values.Length <> NumberOfColumns Then
        Log("Wrong number of values.")
        Return
    End If
    Data.Add(Values)
    Dim lastRow As Int
    lastRow = Data.Size - 1
    If lastRow < (SV.ScrollPosition + SV.Height) / RowHeight Then
        ShowRow(lastRow)     
    End If
    SV.Panel.Height = Data.Size * RowHeight
End Sub

Private Sub ShowRow(row As Int)
    If visibleRows.ContainsKey(row) Then Return
    'Log("ShowRow: " & row)
    Dim lbls() As Label
    Dim values() As String
    lbls = GetLabels(row)
    values = Data.Get(row)
    visibleRows.Put(row, lbls)
    Dim rowColor() As Object
    If row = SelectedRow Then
        rowColor = SelectedDrawable
    Else If row Mod 2 = 0 Then
        rowColor = Drawable1
    Else
        rowColor = Drawable2
    End If
    For I = 0 To lbls.Length - 1
        SV.Panel.AddView(lbls(I), Header.GetView(I).Left, row * RowHeight, Header.GetView(I).Width, _
            RowHeight - 1dip)
        lbls(I).Text = values(I)
        lbls(I).Background = rowColor(I)
    Next
End Sub

'Private Sub IsRowVisible(Row As Int) As Boolean
'    Return Row < (SV.ScrollPosition + SV.Height) / (RowHeight + 1) And _
'        Row > SV.ScrollPosition / RowHeight
'End Sub

Private Sub HideRow (Row As Int)
    'Log("HideRow: " & row)
    Dim lbls() As Label
    lbls = visibleRows.Get(Row)
    If lbls = Null Then
        Log("HideRow: (null) " & Row)
        Return
    End If
    For I = 0 To lbls.Length - 1 
        lbls(I).RemoveView
    Next
    visibleRows.Remove(Row)
    LabelsCache.Add(lbls)
End Sub

Private Sub GetLabels(Row As Int) As Label()
    Dim lbls() As Label
    If LabelsCache.Size > 0 Then
        'Log("from cache")
        lbls = LabelsCache.Get(LabelsCache.Size - 1)
        LabelsCache.RemoveAt(LabelsCache.Size - 1)
    Else
        lbls = CreateNewLabels     
    End If
    For I = 0 To lbls.Length - 1
        Dim rc As RowCol
        rc = lbls(I).Tag
        rc.Row = Row
    Next
    Return lbls
End Sub

Private Sub CreateNewLabels As Label()
    Dim lbls(NumberOfColumns) As Label
    For I = 0 To NumberOfColumns - 1
        Dim rc As RowCol
        rc.Col = I
        Dim l As Label
        l.Initialize("cell")
        l.Gravity = Alignment
        l.TextSize = FontSize
        l.TextColor = FontColor
        l.Tag = rc
        lbls(I) = l
    Next
    Return lbls
End Sub

'Set the headers values
'Example:<code>Table1.SetHeader(Array As String("Col1", "Col2", "Col3"))</code>
Public Sub SetHeader(Values() As String)
    For I = Header.NumberOfViews - 1 To 0 Step -1
        Header.RemoveViewAt(I)
    Next
    For I = 0 To NumberOfColumns - 1
        Dim l As Label
        l.Initialize("header")
        l.Gravity = Gravity.CENTER
        l.TextSize = FontSize
        l.Color = HeaderColor
        l.TextColor = HeaderFontColor
        l.Text = Values(I)
        l.Tag = I
        Header.AddView(l, ColumnWidth * I, 0, ColumnWidth - 1dip, RowHeight)
    Next
End Sub

Public Sub Cell_Click
    Dim rc As RowCol
    Dim l As Label
    l = Sender
    rc = l.Tag
    SelectRow(rc.Row)
    If SubExists(Callback, Event & "_CellClick") Then
        CallSub3(Callback, Event & "_CellClick", rc.Col, rc.Row)
    End If
End Sub

Private Sub Header_Click
    Dim l As Label
    Dim col As Int
    l = Sender
    col = l.Tag
    If SubExists(Callback, Event & "_HeaderClick") Then
        CallSub2(Callback, Event & "_HeaderClick", col)
    End If
End Sub

'Gets the value of the given cell.
Public Sub GetValue(Col As Int, Row As Int) As Int
    Dim values() As String
    values = Data.Get(Row)
    Return values(Col)
End Sub
'Sets the value of the given cell.
Public Sub SetValue(Col As Int, Row As Int, Value As String)
    Dim values() As String
    values = Data.Get(Row)
    values(Col) = Value
    If visibleRows.ContainsKey(Row) Then
        Dim lbls() As Label
        lbls = visibleRows.Get(Row)
        lbls(Col).Text = Value
    End If
End Sub

Private Sub SelectRow(Row As Int)
    Dim previousRow As Int
    previousRow = SelectedRow
    SelectedRow = Row
    'removes the color of the previously selected row
    If previousRow > -1 Then
        If visibleRows.ContainsKey(previousRow) Then
            HideRow(previousRow)
            ShowRow(previousRow)
        End If
    End If
    'Adds the color to the newly selected row
    For Col = 0 To NumberOfColumns - 1
        If visibleRows.ContainsKey(SelectedRow) Then
            HideRow(SelectedRow)
            ShowRow(SelectedRow)
        End If
    Next
End Sub

'Makes the given row visible.
Public Sub JumpToRow(Row As Int)
    SV.ScrollPosition = Row * RowHeight
End Sub

'Clears the previous table and loads the CSV file to the table.
'You should first add the Table to the activity before calling this method.
Public Sub LoadTableFromCSV(Dir As String, Filename As String, HeadersExist As Boolean)
 
    Dim List1 As List
    Dim h() As String
    If HeadersExist Then
        Dim headers As List
        List1 = StringUtils1.LoadCSV2(Dir, Filename, ",", headers)
        Dim h(headers.Size) As String
        For i = 0 To headers.Size - 1
            h(i) = headers.Get(i)
        Next
    Else
        List1 = StringUtils1.LoadCSV(Dir, Filename, ",")
        Dim firstRow() As String
        firstRow = List1.Get(0)
        Dim h(firstRow.Length) As String
        For i = 0 To firstRow.Length - 1
            h(i) = "Col" & (i + 1)
        Next
    End If
    innerClearAll(h.Length)
    ColumnWidth = SV.Width / NumberOfColumns
    SetHeader(h)
    For i = 0 To List1.Size - 1
        Dim row() As String
        row = List1.Get(i)
        AddRow(row)
    Next
End Sub

'Saves the table to a CSV file.
Public Sub SaveTableToCSV(Dir As String, Filename As String)
    Dim headers(NumberOfColumns) As String
    For i = 0 To headers.Length - 1
        Dim l As Label
        l = Header.GetView(i)
        headers(i) = l.Text
    Next
    StringUtils1.SaveCSV2(Dir, Filename, ",", Data, headers)
End Sub

Public Sub RemoveRow(Row As Int)
    SV_ScrollChanged(SV.ScrollPosition)
    Dim sr As Int = SelectedRow
    SelectRow(-1)
    Data.RemoveAt(Row)
    If Data.Size = 0 Then
        ClearAll
        Return
    End If
    For i = minVisibleRow To maxVisibleRow
        HideRow(i)
    Next
    maxVisibleRow = Min(maxVisibleRow, Data.Size - 1)
    minVisibleRow = Min(minVisibleRow, Data.Size - 1)
    For i = minVisibleRow To maxVisibleRow
        ShowRow(i)
    Next
    If sr = Row Then
        sr = -1
    Else If sr > Row Then
        sr = sr - 1
    End If
    SelectRow(sr)
    SV.Panel.Height = Data.Size * RowHeight
    SV_ScrollChanged(Min(SV.ScrollPosition, SV.Panel.Height))
End Sub

Sub Register_CellClick(col As Int, row As Int)
    If SelectedRow >= 0 And SelectedRow = row Then
        register.clearSelection 'this removes the selection and removes the selected row color
        SelectedRow = -1
    Else
        SelectedRow = row
    End If
End Sub

Public Sub clearSelection
    'SV_ScrollChanged(SV.HorizontalScrollPosition,SV.VerticalScrollPosition) ' this strange call will set min/max visible area
'    register.Initialize(Me, "Register", 5)
    SelectedRows.Clear
    RefreshTable
End Sub

' refresh / redraw the visible part of the table
Public Sub RefreshTable
    For i = minVisibleRow To maxVisibleRow ' hide all visible rows
        HideRow(i)
        ShowRow(i)
    Next
End Sub

' return true if the table is set to multi select
Public Sub getMultiSelect As Boolean
    Return IsMultiSelect
End Sub

' set multi select flag, and clear the selected list (just in case)
' when IsMultiSelect is true, click on a not selected row will add that row to the selected list of rows, and click on an selected row will unselect it
' when IsMultiSelect is false, click on a row will select it (or reselect it if alreday selected)
Public Sub setMultiSelect(ms As Boolean)
    clearSelection
    IsMultiSelect = ms
End Sub

'sets or gets the ZeroSelection property
'With ZeroSelection = True, when press on a selectd row this one will be unselected.
'Default value = False, a selected row remains selected when you press on it.
Public Sub setZeroSelection(ZeroSelection As Boolean)
    mZeroSelection = ZeroSelection
End Sub

Error from Log:
B4X:
main_btnremoverow_click (java line: 551)
java.lang.RuntimeException: Object should first be initialized (List).
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
    at anywheresoftware.b4a.objects.collections.List.Sort(List.java:144)
    at b4a.example.main._btnremoverow_click(main.java:551)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:180)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
    at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:80)
    at android.view.View.performClick(View.java:6257)
    at android.widget.TextView.performClick(TextView.java:11149)
    at android.view.View$PerformClick.run(View.java:23705)
    at android.os.Handler.handleCallback(Handler.java:751)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:154)
    at android.app.ActivityThread.main(ActivityThread.java:6780)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1496)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1386)
java.lang.RuntimeException: Object should first be initialized (List).

Implemented Code From Main Class:
B4X:
Sub btnRemoveRow_Click
    Private lst As List
    Private i, row As Int
    lst = register.SelectedRows
    lst.Sort(True)
    For i = lst.Size - 1 To 0 Step -1
        row = lst.Get(i)
        register.RemoveRow(row)
    Next
End Sub
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

Eric McDonald

Member
Licensed User
We are developing a program that based off around the original one and it would take more effort to convert everything to the newer table unless it would be easier? But I will look into using the B4XTable. Thank you for the quick response.
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
You have two solutions:
1. Add the Table in the code like you do currently.
You need to add this line to initialize the Table:
register.InitializeTable(5, Gravity.CENTER, False)
Like below.
B4X:
register.Initialize(Me, "Register")                     'This creates the table with the name "Register" with 5 rows
register.AddToActivity(Activity, 0, 76%y, 100%x, 24%y)   'Sets the position of the table
register.InitializeTable(5, Gravity.CENTER, False)
register.SortBitmapWidth = 16dip

2. Add the Table in the Designer like in the attached project. Flexible Table is a CustomView

How did you zip your project?
You should use the Export As Zip in the Designer, this will make the zip file much smaller with only the needed files.

upload_2019-12-23_20-28-17.png


Some comments:
Your project doesn't look good on phones, because of your layouts.

upload_2019-12-23_20-46-45.png


You put views which are partly outsides the screen area.
In the Admin layout you use a different variant.
800 X 1280 in homepage and 320 x 480 in Admin !?

How do you intend to save the data?
Might be interesting tu use a database.

It would be worth reading the B4X Visual Designer booklet.
 

Attachments

  • RedDogNew.zip
    183 KB · Views: 110
Upvote 0
Top