Android TableLayout Header row

One approach is by embedding a TableLayout within another TableLayout’s row and putting the header in the preceding row as seen below. Aligning the data and the header requires the layout_width property of the header View objects and the data’s View objects to be set to the same dip values. Also, the layout_weight property of the inner TableLayout’s View objects must match its corresponding header.
Now, in the XML below, I have placed 3 TextViews in the inner TableLayout in a single row to match with the column headers. This is just to show how the alignment can be done. You can populate that data programmatically by inflating a layout and adding it at runtime.

<?xml version="1.0" encoding="utf-8"?>
<TableLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">


  <TableRow>
    <TextView android:text="Name"
        android:layout_width="100dp"        
        android:layout_column="0"
        android:layout_weight="1"/>
    <TextView android:text="Score"
        android:layout_width="30dp"
        android:layout_column="1"
        android:layout_weight="1">
    </TextView>
    <TextView android:text="Level"
        android:layout_width="30dp"
        android:layout_column="2"
        android:layout_weight="1">
    </TextView>
  </TableRow>

    <ScrollView android:layout_height="120dp">      
    <TableLayout android:id="@+id/score_table"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">          
    <TableRow>
        <TextView android:text="Al"
        android:layout_width="100dp"        
        android:layout_column="0"
        android:layout_weight="1">
        </TextView>
        <TextView android:text="1000"
        android:layout_width="30dp"
        android:layout_column="1"
        android:layout_weight="1">
        </TextView>
        <TextView android:text="2"
        android:layout_width="30dp"
        android:layout_column="2"
        android:layout_weight="1">
        </TextView>
    </TableRow>
  </TableLayout>
  </ScrollView>     
</TableLayout>

I actually came up with another decent way of doing this.

Simply build the table normally with the header row as the first row, inside of a vertical orientation LinearLayout. Next, programmatically remove the first row then add it as the first child to the LinearLayout. This worked like a charm.

Edit: This also works without having to specify static column widths.

I know that the question is old, but it was the first one, that Google gave me as I’ve had the same problem. And since I think I’ve found a better solution, I would like to share it.

Idea: put the TableLayout (inside the ScrollView) into RelativeLayout and create an overlay, that would draw the first (header) row over everything else.

Here is layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"

    android:id="@+id/table_wrapper"

    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
>
    <ScrollView

        android:layout_width="wrap_content"
        android:layout_height="wrap_content"

        tools:ignore="UselessParent"
    >
        <TableLayout

            android:id="@+id/table"

            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
        />
    </ScrollView>
</RelativeLayout>

And here is the code:

TableLayout table = (TableLayout)view.findViewById(R.id.table);

final TableRow headerRow = new TableRow(context);
table.addView(headerRow);

table.addView(new TableRow(context));
table.addView(new TableRow(context));
table.addView(new TableRow(context));


RelativeLayout tableWrapper = (RelativeLayout)view.findViewById(R.id.table_wrapper);

View fakeHeaderView = new View(context) {
    @Override
    public void draw(Canvas canvas) {
        headerRow.draw(canvas);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int width = headerRow.getMeasuredWidth();
        int height = headerRow.getMeasuredHeight();

        widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY);
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
};

tableWrapper.addView(fakeHeaderView);