'Version 1.14
' Whats new 1.14
' alignement now is set in the Initialize sub
' added removeRow() sub to remove a specifc row from the table - this is EREL's removeRow adopted to 1.13 and to the rest of the changes in 1.14
' added getValues() sub to get the entire row in an array, thought this is usefull
' added updateRow() sub to set an entire row at once, Vs cell by cell, in tables many times, developer work by rows , update an entire row
' added ability to set (and get) the lables to be singleLine or multiLine lables (deafult is singleLine) this feature depend on reflection lib - setting this attribute will clear the table!!!
' added getHeaderPanel sub to return he header panel so developer can get access to the header components , for example to show tooltip, or quickAction on the header location
' added ability to turn on and off multi selection of rows, User can now select one row or any number of rows, developer can use that functunality to , for example change status of many rows at once, or remove many rows with one user action
' added getSelectedRows to return list of selected rows
' added ability to hide/unhide a specific column, so it will be part of the table but hidden from the user, this is usefull to if developer wants to maintain additional data in the table (this is not a complete data model / view model implementation)
' with ScrollView2 instead of ScrollView
' with highlighting of the selected cell
Sub Class_Globals
Private StringUtils1 As StringUtils
Private SV As ScrollView2D
Private Header As Panel
Private Callback As Object
Private Event As String
'Private SelectedRow As Int
Private SelectedRows As List ' selected rows
Private SelectedCol As Int ' ???
Private Data As List
Private LabelsCache As List
Private minVisibleRow, maxVisibleRow As Int
Private visible As Boolean
Public visibleRows As Map
Private NumberOfColumns, ColumnWidth As Int
Public RowHeight, HeaderColor, TableColor, FontColor, HeaderFontColor As Int
Public FontSize As Float
Type RowCol (Row As Int, Col As Int)
Public Alignment As Int
Public SelectedDrawable(), Drawable1(), Drawable2() As Object
Public SelectedCellDrawable As Object
'Table settings
HeaderColor = Colors.Gray
RowHeight = 30dip
TableColor = Colors.LightGray
FontColor = Colors.Black
HeaderFontColor = Colors.White
FontSize = 14
Alignment = Gravity.CENTER 'change to Gravity.LEFT or Gravity.RIGHT for other alignments.
Public singleLine As Boolean = True ' does a lable hold a single line text or multiline , need to be set rigth after call to initialize
Private multiSelect As Boolean = False
Private SavedWidths() As Int' to keep the user set widths for columns
End Sub
' nir adding alignment as parameter to initialize
Public Sub Initialize (CallbackModule As Object, EventName As String, vNumberOfColumns As Int, cellAlignement As Int)
SV.Initialize(0, 0, "SV")
SelectedRows.Initialize
Alignment = cellAlignement
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)
' clone (keep) Widths
Dim SavedWidths(Widths.Length) As Int
For i=0 To Widths.Length-1
SavedWidths(i) = Widths(i)
Next
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
Header.Width = Header.GetView(Widths.Length - 1).Left + Widths(Widths.Length - 1)
SV.Panel.Width = Header.Width
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
Private Sub innerClearAll(vNumberOfColumns As Int)
SelectedRows.Initialize
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.White, 0)
cd2.Initialize(0xFF98F5FF, 0)
cd3.Initialize(0xFF007FFF, 0)
Drawable1(i) = cd1
Drawable2(i) = cd2
SelectedDrawable(i) = cd3
Next
Dim cd4 As ColorDrawable
cd4.Initialize(0xFFFC8EAC, 0)
SelectedCellDrawable = cd4
SV.Panel.Height = 0
'SelectedRow = -1
SelectedCol = -1
minVisibleRow = -1
maxVisibleRow = 0
Data.Initialize
LabelsCache.Initialize
visibleRows.Initialize
SV.VerticalScrollPosition = 0
DoEvents
SV.VerticalScrollPosition = 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, 0)
End If
End Sub
Private Sub SV_ScrollChanged(PosX As Int, PosY As Int)
Dim currentMin, currentMax As Int
currentMin = Max(0, PosY / RowHeight - 30)
currentMax = Min(Data.Size - 1, (PosY + 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
Header.Left = -PosX
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, 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 =" & Values.Length & " col=" & NumberOfColumns)
Return
End If
Data.Add(Values)
Dim lastRow As Int
lastRow = Data.Size - 1
If lastRow < (SV.VerticalScrollPosition + SV.Height) / RowHeight + 1 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 (SelectedRows.indexof(row) <> -1 )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)
If I = SelectedCol AND (SelectedRows.indexof(row) <> -1) Then
lbls(I).Background = SelectedCellDrawable
Else
lbls(I).Background = rowColor(I)
End If
Next
End Sub
Private Sub IsRowVisible(Row As Int) As Boolean
Return Row < (SV.VerticalScrollPosition + SV.Height) / (RowHeight + 1) AND _
Row > SV.VerticalScrollPosition / 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
' added by nir, make each label single line
If (singleLine) Then
Dim ref As Reflector
ref.Target = l
ref.RunMethod2("setSingleLine", True, "java.lang.boolean")
End If
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
Private Sub Cell_Click
Dim rc As RowCol
Dim l As Label
l = Sender
rc = l.Tag
' SelectRow(rc.Row)
SelectRow(rc)
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)
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(rc As RowCol)
Dim prevIndex As Int
Dim prev As Int ' if we select an alreday selected row, prev will be rc.row, else will be -1
prevIndex = SelectedRows.indexof(rc.Row) ' -1 if selecting not a selected row
If (prevIndex <> -1 AND (multiSelect = False)) Then
' do nothing we select row that is selected already
Return
End If
If (prevIndex = -1) Then
If (multiSelect) Then
SelectedRows.add(rc.Row) 'Select the new row
prev = -1
Else ' set selected to the new one
' hide / show all selected rows
'Log ("get at zero: " & SelectedRows)
If (SelectedRows.Size <> 0) Then
prev = SelectedRows.get(0) ' there should be only one here ever!!!, keep the unselected row in prev
SelectedRows.set(0,rc.Row) ' change it to the new one
Else
prev = -1
SelectedRows.Add(rc.Row)
End If
End If
Else ' multi select and found a row (unselect it)
'Log ("multi select and found row")
prev = SelectedRows.get(prevIndex) ' should be RC.row
SelectedRows.RemoveAt(prevIndex) ' deselect the old selected row
End If
'remove the color of previously selected row
If prev > -1 Then
If visibleRows.ContainsKey(rc.Row) Then
HideRow(prev)
ShowRow(prev)
End If
End If
SelectedCol = rc.col
'For col = 0 To NumberOfColumns - 1
If visibleRows.ContainsKey(rc.Row) Then
HideRow(rc.Row)
ShowRow(rc.Row)
End If
'Next
End Sub
'Makes the given row visible.
Public Sub JumpToRow(Row As Int)
SV.VerticalScrollPosition = 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)
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
' --------------------------------------------------------------------------------------------------------
' new functunality added by nir -->
' remove a row
'row is the row number
Public Sub RemoveRow(Row As Int)
If (Row <0 OR row > Data.Size-1) Then Return ' cant remove row outside of the table scope
SV_ScrollChanged(SV.HorizontalScrollPosition,SV.VerticalScrollPosition)
Dim sr As Int ' to keep the previos selected row (in case multiSelect is off)
'sr = -1 ' not the selected row
Dim prevIndex As Int
prevIndex = SelectedRows.IndexOf(Row) ' if the rmeoved one was selected or not/
For i=0 To SelectedRows.Size -1 ' updated selection
Dim keepSel As Int
keepSel = SelectedRows.get(i)
If (keepSel > Row) Then
SelectedRows.Set(i,keepSel-1) ' dec row number in all rows appear after the soon tobe removed removed row
' future optimization: hide and show all rows touched and that within visible range, for now we hide/show all rows in visible scope
End If
Next
If (prevIndex <> -1) Then
'sr = Row ' in case the row was selected keep it in sr
SelectedRows.RemoveAt(prevIndex) ' removed the current row from the selected list
End If
Dim cr As RowCol
Data.RemoveAt(Row)
For i = minVisibleRow To maxVisibleRow ' hide all visible rows
HideRow(i)
Next
'If multiSelect = False Then
' If sr = Row Then ' current selected row was deleted
' sr = -1
' Else If sr > Row Then
' sr = sr - 1
' End If
'End If
maxVisibleRow = Min(maxVisibleRow, Data.Size - 1) ' adjust visible rows
minVisibleRow = Min(minVisibleRow, Data.Size - 1)
For i = minVisibleRow To maxVisibleRow ' show all visible rows (should select the ones needed to be selected as well)
'If (multiSelect OR sr = i) Then HideRow(i) ' in multi select we made too much mess, we need to redraw the whole view (can be optimized if needed!)
ShowRow(i)
Next
SV.Panel.Height = Data.Size * RowHeight
SV_ScrollChanged(SV.HorizontalScrollPosition,Min(SV.VerticalScrollPosition, SV.Panel.Height))
End Sub
' return array of strings hold all the values for a row.
Public Sub getValues(Row As Int ) As String()
Dim rowData() As String = data.Get(row) ' will throw an excpetion if row is not correct
Dim tmp(NumberOfColumns) As String
For i=0 To NumberOfColumns-1 ' copy the array
tmp(i) = rowData(i)
Next
Return tmp
End Sub
' update a row in the table
' row is the row number to update, Values is an array of string at the size of the number of columns
' return true is worked out, false if failed
Public Sub UpdateRow(Row As Int, Values () As String) As Boolean
Dim i As Int
If (Values.Length <> NumberOfColumns OR Row <0 OR Row>Data.Size-1) Then
Return False
End If
For i=0 To Values.Length-1
SetValue(i,Row,Values(i))
Next
Return True
End Sub
' set multi select flag, and clear the selected list (just in case)
' when multiSelect 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 multiSelect is false, click on a row will select it (or reselect it if alreday selected)
Sub setMultiSelect(ms As Boolean)
SelectedRows.Clear
multiSelect = ms
End Sub
' return true is the table us set to multi select
Sub getMultiSelect As Boolean
Return multiSelect
End Sub
' return the header panel
Sub getHeaderPanel As Panel
Return Header
End Sub
' return the selected rows number as a list of int.
Sub getSelectedRows As List
Dim sr As List
sr.Initialize
sr.AddAll(SelectedRows)
Return sr
End Sub
' set column col to length '1' which mean it willbe hidden
Sub hideCol(col As Int)
Dim tmpWidths(SavedWidths.Length) As Int
For i=0 To SavedWidths.Length-1
tmpWidths(i) = SavedWidths(i)
Next
tmpWidths(col) = 1
SetColumnsWidths(tmpWidths)
End Sub
' unhide column col, and give it a new size
Sub unHideCol(col As Int, newSize As Int)
Dim tmpWidths(SavedWidths.Length) As Int
For i=0 To SavedWidths.Length-1
tmpWidths(i) = SavedWidths(i)
Next
tmpWidths(col) = newSize
SetColumnsWidths(tmpWidths)
End Sub