I would like to create a table-like view that contains a large number of columns (7-10) while the headers row is always visible (even when scrolling down) and the first column also always visible while scrolling horizontally.
Tried to put a list view inside an HorizontalScrollView which let me display a list with horizontal and vertical scrolling but no static column/header. I am trying to avoid using multiple views and sync between them while the user scrolls.
Later on I will have to control events inside the view like row/columns clicks, so something with a custom adapter should be used.
any ideas?
I would go with TableLayout populated by TableRow's.
The following code demonstrates how to achieve that.
package com.test;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Gravity;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TableRow.LayoutParams;
import android.widget.TextView;
public class TableLayoutTest extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.table_layout);
        TableRow.LayoutParams wrapWrapTableRowParams = new TableRow.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        int[] fixedColumnWidths = new int[]{20, 20, 20, 20, 20};
        int[] scrollableColumnWidths = new int[]{20, 20, 20, 30, 30};
        int fixedRowHeight = 50;
        int fixedHeaderHeight = 60;
        TableRow row = new TableRow(this);
        //header (fixed vertically)
        TableLayout header = (TableLayout) findViewById(R.id.table_header);
        row.setLayoutParams(wrapWrapTableRowParams);
        row.setGravity(Gravity.CENTER);
        row.setBackgroundColor(Color.YELLOW);
        row.addView(makeTableRowWithText("col 1", fixedColumnWidths[0], fixedHeaderHeight));
        row.addView(makeTableRowWithText("col 2", fixedColumnWidths[1], fixedHeaderHeight));
        row.addView(makeTableRowWithText("col 3", fixedColumnWidths[2], fixedHeaderHeight));
        row.addView(makeTableRowWithText("col 4", fixedColumnWidths[3], fixedHeaderHeight));
        row.addView(makeTableRowWithText("col 5", fixedColumnWidths[4], fixedHeaderHeight));
        header.addView(row);
        //header (fixed horizontally)
        TableLayout fixedColumn = (TableLayout) findViewById(R.id.fixed_column);
        //rest of the table (within a scroll view)
        TableLayout scrollablePart = (TableLayout) findViewById(R.id.scrollable_part);
        for(int i = 0; i < 10; i++) {
            TextView fixedView = makeTableRowWithText("row number " + i, scrollableColumnWidths[0], fixedRowHeight);
            fixedView.setBackgroundColor(Color.BLUE);
            fixedColumn.addView(fixedView);
            row = new TableRow(this);
            row.setLayoutParams(wrapWrapTableRowParams);
            row.setGravity(Gravity.CENTER);
            row.setBackgroundColor(Color.WHITE);
            row.addView(makeTableRowWithText("value 2", scrollableColumnWidths[1], fixedRowHeight));
            row.addView(makeTableRowWithText("value 3", scrollableColumnWidths[2], fixedRowHeight));
            row.addView(makeTableRowWithText("value 4", scrollableColumnWidths[3], fixedRowHeight));
            row.addView(makeTableRowWithText("value 5", scrollableColumnWidths[4], fixedRowHeight));
            scrollablePart.addView(row);
        }
    }
    //util method
    private TextView recyclableTextView;
    public TextView makeTableRowWithText(String text, int widthInPercentOfScreenWidth, int fixedHeightInPixels) {
        int screenWidth = getResources().getDisplayMetrics().widthPixels;
        recyclableTextView = new TextView(this);
        recyclableTextView.setText(text);
        recyclableTextView.setTextColor(Color.BLACK);
        recyclableTextView.setTextSize(20);
        recyclableTextView.setWidth(widthInPercentOfScreenWidth * screenWidth / 100);
        recyclableTextView.setHeight(fixedHeightInPixels);
        return recyclableTextView;
    }
}
Header is the part that doesn't scroll vertically; that's the reason you need to set fixed width on columns. As of the first column that you don't want to scroll, you'll have to set a fixed height on rows for that purpose.
Here's the layout XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    android:id="@+id/fillable_area">
    <TableLayout
        android:id="@+id/table_header"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"/>
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        <LinearLayout android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center_horizontal"
            android:id="@+id/fillable_area">
            <TableLayout
                android:id="@+id/fixed_column"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"/>
            <HorizontalScrollView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content">
                <TableLayout
                    android:id="@+id/scrollable_part"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"/>
            </HorizontalScrollView>
        </LinearLayout>
    </ScrollView>
</LinearLayout>
And the output looks like this when just loaded

and like this when scrolled to the right and to the bottom

You can check this library that I made: https://github.com/InQBarna/TableFixHeaders
I think that it implements the widget you're looking for.
Create TableLayout that will be a header and under it place a Table itself within a ScrollView like this:
<TableLayout
        android:id="@+id/tbl_header"
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:divider="@drawable/table_divider"
        android:showDividers="middle"
        android:background="@drawable/table_header_backdround"
        />
    <ScrollView android:layout_width="fill_parent"
                android:layout_height="wrap_content">
        <TableLayout
                android:id="@+id/tbl_relesed_wake_locks"
                android:orientation="horizontal"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:divider="@drawable/table_divider"
                android:showDividers="middle"
                android:stretchColumns="1,2"
                android:background="@drawable/table_backdround"
                />
    </ScrollView>
When you will populate your header with data add next code:
table.post(new Runnable() {
        @Override
        public void run() {
            TableRow tableRow = (TableRow)table.getChildAt(0);
            for(int i = 0; i < headerRow.getChildCount(); i++){
                headerRow.getChildAt(i).setLayoutParams(new TableRow.LayoutParams(tableRow.getChildAt(i).getMeasuredWidth(), tableRow.getChildAt(i).getMeasuredHeight()));
            }
        }
    });
That's it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With