Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android layout - How to implement a fixed/freezed header and column

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?

like image 945
Idan Avatar asked Aug 19 '11 08:08

Idan


3 Answers

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

enter image description here

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

enter image description here

like image 85
Ilya Saunkin Avatar answered Nov 17 '22 07:11

Ilya Saunkin


You can check this library that I made: https://github.com/InQBarna/TableFixHeaders

I think that it implements the widget you're looking for.

like image 14
Brais Gabin Avatar answered Nov 17 '22 07:11

Brais Gabin


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.

like image 6
Yan Avatar answered Nov 17 '22 08:11

Yan