Android Code Snippet [B4X] B4XTable - Resize columns based on content

Erel

Administrator
Staff member
Licensed User
This code measures the required width based on the cells text and set it:

B4X:
Sub B4XTable1_DataUpdated
    Dim ShouldRefresh As Boolean
    'NameColumn and NumberColumn are global B4XTableColumns that we want to measure
    For Each column As B4XTableColumn In Array(NameColumn, NumberColumn)
        Dim MaxWidth As Int
        For i = 0 To B4XTable1.VisibleRowIds.Size - 1
            Dim RowId As Long = B4XTable1.VisibleRowIds.Get(i)
            If RowId > 0 Then
                Dim pnl As B4XView = column.CellsLayouts.Get(i + 1)
                Dim lbl As B4XView = pnl.GetView(0)
                Dim txt As String = B4XTable1.GetRow(RowId).Get(column.Id)
                MaxWidth = Max(MaxWidth, cvs.MeasureText(txt, lbl.Font).Width + 10dip)
            End If
        Next
        If MaxWidth > column.ComputedWidth Or MaxWidth < column.ComputedWidth - 20dip Then
            column.Width = MaxWidth
            ShouldRefresh = True
        End If
    Next
    If ShouldRefresh Then
        B4XTable1.Refresh
    End If
End Sub
cvs is a global B4XCanvas. Create it with this code when the app starts:
B4X:
Dim p As B4XView = xui.CreatePanel("")
   p.SetLayoutAnimated(0, 0, 0, 1dip, 1dip)
   cvs.Initialize(p)
 
Last edited:

Mahares

Well Known Member
Licensed User
I am getting this error:
java.lang.ClassCastException: java.lang.String cannot be cast to carthage.lastvisitb4xtable.b4xtable$_b4xtablecolumn
at this line:
B4X:
For Each Column As B4XTableColumn In Array(NameColumn, NumberColumn) 'of SUb B4XTable1_DataUpdated
Here is part of the code:
B4X:
Sub Globals
    Private B4XTable1 As B4XTable
    Private cvs As B4XCanvas
    Private xui As XUI
    Private NameColumn As String
    Private NumberColumn As Int  'it does not like this declaration
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("1")  'has the B4XTable customview
    Dim p As B4XView = xui.CreatePanel("")
    p.SetLayoutAnimated(0, 0, 0, 1dip, 1dip)
    cvs.Initialize(p)
.
.
 

Mahares

Well Known Member
Licensed User
Private NameColumn As B4XTableColumn
I still cannot get it to work. Here is the error. Am I the only dude that does not get it or what:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object anywheresoftware.b4a.objects.collections.List.Get(int)' on a null object reference
B4X:
Sub Globals
Private NameColumn As B4XTableColumn
    Private NumberColumn As Int   'What is this suppoed to be. It says variable is never assigned any value.
Could you please explain better this code snippet and provide a small example on how to automatically adjust columns width.
 

DonManfred

Expert
Licensed User
What is this suppoed to be.
You should tell us. It is from your Code or not?
Here is part of the code:
B4X:
Sub Globals
    Private B4XTable1 As B4XTable
    Private cvs As B4XCanvas
    Private xui As XUI
    Private NameColumn As String
    Private NumberColumn As Int  'it does not like this declaration
End Sub
 

DonManfred

Expert
Licensed User
For Each Column As B4XTableColumn In Array(NameColumn, NumberColumn) 'change the columns that should be measured
Based on this code NumberColumn must be the same type as NameColumn (B4XTableColumn), So in fact
For Each Column As B4XTableColumn In Array(NameColumn, NumberColumn) 'change the columns that should be measured
In the code NameColumn and NumberColumn are measured.

PD;: Sorry, i never worked with B4XTable, i just saw the Release and some threads about. But that´s all.
 

LucaMs

Expert
Licensed User
Private NumberColumn As Int
Given its name, you could think that NumberColumn is a column number.
It must be the column (b4xTableColumn type) you want to resize and you get it as "function result" when you CREATE and add a new column to the table using the method/function AddColumn.


Code from the b4xTable example, modified to test the snippet:
B4X:
Sub Globals
   Private B4XTable1 As B4XTable
   Private xui As XUI
   Private mNumberColumn As B4XTableColumn ' I renamed it adding the prefix "m"
   Private btnPrev As Button
   Private btnNext As Button
   Private IME As IME
 
   Private mcvs As B4XCanvas ' Added; even here I'm using the prefix "m"
   Private mNameColumn As B4XTableColumn ' Added
End Sub


' Activity_Create
'...
'create the columns
    'B4XTable1.AddColumn("US County", B4XTable1.COLUMN_TYPE_NUMBERS)' Original line
    mNameColumn = B4XTable1.AddColumn("US County", B4XTable1.COLUMN_TYPE_NUMBERS) ' new line
    B4XTable1.AddColumn("Name", B4XTable1.COLUMN_TYPE_TEXT)
    Dim StateColumn As B4XTableColumn = B4XTable1.AddColumn("State", B4XTable1.COLUMN_TYPE_TEXT)
    StateColumn.Width = 80dip
    mNumberColumn = B4XTable1.AddColumn("Interesting Number", B4XTable1.COLUMN_TYPE_NUMBERS)
Then in the snippet:
B4X:
   For Each Column As B4XTableColumn In Array(mNameColumn, mNumberColumn) 'change the columns that should be measured
 

Mahares

Well Known Member
Licensed User
I think I got is figured out using this code. It is working for me now after several hours:
B4X:
Sub Globals
    Private B4XTable1 As B4XTable  
    Private cvs As B4XCanvas
    Private xui As XUI
    Private NameColumn(6) As B4XTableColumn
End Sub
B4X:
NameColumn(0) = B4XTable1.AddColumn("Name", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(1) = B4XTable1.AddColumn("Customer Id", B4XTable1.COLUMN_TYPE_NUMBERS)
    NameColumn(2) = B4XTable1.AddColumn("Company", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(3) = B4XTable1.AddColumn("Address", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(4) = B4XTable1.AddColumn("Email", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(5) = B4XTable1.AddColumn("Country", B4XTable1.COLUMN_TYPE_TEXT)
B4X:
Sub B4XTable1_DataUpdated
    Dim ShouldRefresh As Boolean
    For Each Column As B4XTableColumn In Array (NameColumn(0), NameColumn(1), NameColumn(2), NameColumn(3), NameColumn(4), NameColumn(5) )
        Dim MaxWidth As Int
        For i = 0 To B4XTable1.VisibleRowIds.Size
            Dim pnl As B4XView = Column.CellsLayouts.Get(i)
            Dim lbl As B4XView = pnl.GetView(0)
            MaxWidth = Max(MaxWidth, cvs.MeasureText(lbl.Text, lbl.Font).Width + 10dip)
        Next
        If MaxWidth > Column.ComputedWidth Or MaxWidth < Column.ComputedWidth - 20dip Then
            Column.Width = MaxWidth
            ShouldRefresh = True
        End If
    Next
    If ShouldRefresh Then
        B4XTable1.Refresh
    End If
End Sub
 

LucaMs

Expert
Licensed User
It is working for me now after several hours
I'm sorry to have written my post not many minutes ago, which should be quite clear.

For Each Column As B4XTableColumn In Array (NameColumn(0), NameColumn(1), NameColumn(2), NameColumn(3), NameColumn(4), NameColumn(5) )
You can change this line to:
B4X:
For Each Column As B4XTableColumn In NameColumn
I would also change the array name from NameColumn to Columns.
 

Mahares

Well Known Member
Licensed User
You can change this line to:
B4X:
For Each Column As B4XTableColumn In NameColumn
Hi Mario:
The reason I wanted to explicitly show the columns is because in the official project, I wanted to skip autowidth for some columns. That is why I did this:
B4X:
For Each Column As B4XTableColumn In Array (NameColumn(0),  NameColumn(2), NameColumn(5) )
not simply use the full array as below:
B4X:
For Each Column As B4XTableColumn In (NameColumn)
I think the main reason I had trouble with Erel's snippet is this:
Array(NameColumn, NumberColumn)
If he wrote: Array(NameColumn1, NameColumn3, NameColumn6, etc.)I would have understood it. But we can't blame him as he answers a ton of posts each day and that is taxing.
 
Last edited:

Erel

Administrator
Staff member
Licensed User
If he wrote: Array(NameColumn1, NameColumn3, NameColumn6, etc.)I would have understood it. But we can't blame him as he answers a ton of posts each day and that is taxing.
The first code snippet did include a comment which was later removed by mistake when I updated the code. I've added it back now.
 

Graeme M itchell

Member
Licensed User
I get an error message on the following line
B4X:
Dim pnl As B4XView = Column.CellsLayouts.Get(i)
[<B4IArray 0x281c5fc90> valueForUndefinedKey:]: this class is not key value coding-compliant for the key CellsLayouts.
 

Guenter Becker

Member
Licensed User
I think I got is figured out using this code. It is working for me now after several hours:
B4X:
Sub Globals
    Private B4XTable1 As B4XTable 
    Private cvs As B4XCanvas
    Private xui As XUI
    Private NameColumn(6) As B4XTableColumn
End Sub
B4X:
NameColumn(0) = B4XTable1.AddColumn("Name", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(1) = B4XTable1.AddColumn("Customer Id", B4XTable1.COLUMN_TYPE_NUMBERS)
    NameColumn(2) = B4XTable1.AddColumn("Company", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(3) = B4XTable1.AddColumn("Address", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(4) = B4XTable1.AddColumn("Email", B4XTable1.COLUMN_TYPE_TEXT)
    NameColumn(5) = B4XTable1.AddColumn("Country", B4XTable1.COLUMN_TYPE_TEXT)
B4X:
Sub B4XTable1_DataUpdated
    Dim ShouldRefresh As Boolean
    For Each Column As B4XTableColumn In Array (NameColumn(0), NameColumn(1), NameColumn(2), NameColumn(3), NameColumn(4), NameColumn(5) )
        Dim MaxWidth As Int
        For i = 0 To B4XTable1.VisibleRowIds.Size
            Dim pnl As B4XView = Column.CellsLayouts.Get(i)
            Dim lbl As B4XView = pnl.GetView(0)
            MaxWidth = Max(MaxWidth, cvs.MeasureText(lbl.Text, lbl.Font).Width + 10dip)
        Next
        If MaxWidth > Column.ComputedWidth Or MaxWidth < Column.ComputedWidth - 20dip Then
            Column.Width = MaxWidth
            ShouldRefresh = True
        End If
    Next
    If ShouldRefresh Then
        B4XTable1.Refresh
    End If
End Sub
I checked your code ande modified it a little bit. Now the header of the column is checked as well. If the column has no value than the width of the column is taken from the width of the header text. The column "DBTablerowid" is not prooved because in my app it is a hidden column if I proof it an error is raised.
Some enhancement:
         If B4XTable1.mCurrentCount > 0 Then
            Dim ShouldRefresh As Boolean
            'NameColumn and NumberColumn are global B4XTableColumns that we want to measure
            'For Each column As B4XTableColumn In Array(NameColumn, NumberColumn)
            For Each column As B4XTableColumn In B4XTable1.columns
                If column.Id <> "DBTablerowid" Then
                    Dim MaxWidth,MaxWidth1 As Int
                    For i = 0 To B4XTable1.VisibleRowIds.Size - 1
                        Dim RowId As Long = B4XTable1.VisibleRowIds.Get(i)
                        If RowId > 0 Then
                            Dim pnl As B4XView = column.CellsLayouts.Get(i + 1)
                            Dim lbl As B4XView = pnl.GetView(0)
                            Dim txt As String = B4XTable1.GetRow(RowId).Get(column.Id)
                            Dim txt1 As String = column.Id
                            MaxWidth = Max(MaxWidth, Modul1.cvs.MeasureText(txt, lbl.Font).Width + 10dip)
                            MaxWidth1 = Max(MaxWidth1, Modul1.cvs.MeasureText(txt1, lbl.Font).Width + 10dip)
                        End If
                    Next
                    If (MaxWidth > column.ComputedWidth Or MaxWidth < column.ComputedWidth - 20dip) _
                        And MaxWidth > = MaxWidth1 Then
                        column.Width = MaxWidth
                        ShouldRefresh = True
                    End If
                End If
Thanks for your work.
 
Top