Android 布局 - 如何实现固定/冻结的标题和列

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

提问人:Idan 提问时间:8/19/2011 更新时间:1/26/2022 访问量:57878

问:

我想创建一个类似表格的视图,其中包含大量列 (7-10),而标题行始终可见(即使向下滚动),第一列在水平滚动时也始终可见。

尝试将列表视图放在 HorizontalScrollView 中,这让我可以显示一个具有水平和垂直滚动但没有静态列/标题的列表。我试图避免使用多个视图,并在用户滚动时在它们之间同步。

稍后,我将不得不控制视图中的事件,例如行/列单击,因此应使用带有自定义适配器的内容。

有什么想法吗?

android-layout

评论

0赞 Atul Pandey 2/24/2012
stackoverflow.com/questions/5665747/......

答:

32赞 Ilya Saunkin 8/19/2011 #1

我会用 's 填充。TableLayoutTableRow

下面的代码演示了如何实现此目的。

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;
    }

}

标题是不垂直滚动的部分;这就是您需要在列上设置固定宽度的原因。从您不想滚动的第一列开始,您必须为此目的在行上设置固定高度。

下面是布局 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>

刚加载时输出看起来像这样

enter image description here

当向右和底部滚动时,就像这样

enter image description here

评论

1赞 Idan 8/19/2011
很好的答案。谢谢。这个解决方案的处理是 col2-col5 不会与scrollable_part表水平滚动。这意味着,当我水平滚动数据时,不同的字段会出现在相同的标题下方。我正在寻找的水平滚动是当我向左/向右移动时,标题 col2-col5 会随我移动(curse 的第一个除外)。
1赞 hemant 11/17/2012
-Elijah Saounkine,你能指导我一点吗??我想table_header水平滚动。.请帮帮我
1赞 thinzar00 2/12/2016
谢谢,这个答案救了我。只是想补充一下 hemant 的问题,你可以同步 header 和 body scrollviews,代码示例在这里:stackoverflow.com/questions/3948934/...
14赞 Brais Gabin 1/8/2013 #2

你可以检查我制作的这个库:https://github.com/InQBarna/TableFixHeaders

我认为它实现了您正在寻找的小部件。

评论

2赞 krishnakumarp 8/13/2018
此项目不再处于活动状态。
0赞 Aman Gupta - ΔMΔN 9/25/2020
兄弟,我怎样才能修复 2 个标题?
0赞 Noor Hossain 11/2/2020
@krishnakumarp,错误的信息,我今天刚刚运行这个项目,01-11-2020,出色的工作!
0赞 ruif3r 12/7/2020
如何在项目中使用该库?
6赞 Yan 12/21/2013 #3

创建 TableLayout,它将是一个标题,并在其下放置一个 Table 本身,如下所示:

<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>

当您将用数据填充标头时,请添加下一个代码:

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()));
            }
        }
    });

就是这样。

2赞 BNK 7/13/2015 #4

如果要修复第一列,可以尝试以下布局:

<TableLayout
    android:id="@+id/tbl_header"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:background="@drawable/cell_shape_header"
    android:divider="?android:dividerHorizontal"
    android:orientation="horizontal"
    android:showDividers="middle">

    <TableRow
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="0.5dp">

        <TextView
            style="@style/TextViewStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Fragment A" />

    </TableRow>

    <TableRow
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="0.5dp">

        <TextView
            style="@style/TextViewStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Fragment A" />

    </TableRow>

    <TableRow
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:padding="0.5dp">

        <TextView
            style="@style/TextViewStyle"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="Fragment A" />

    </TableRow>
</TableLayout>

<HorizontalScrollView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentRight="true"
    android:layout_toRightOf="@+id/tbl_header">

    <TableLayout
        android:id="@+id/tbl_relesed_wake_locks"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/cell_shape_header"
        android:divider="?android:dividerHorizontal"
        android:orientation="horizontal"
        android:showDividers="middle"
        android:stretchColumns="1,2">

        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="0.5dp">

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

        </TableRow>

        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="0.5dp">

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

        </TableRow>

        <TableRow
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:padding="0.5dp">

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

            <TextView
                style="@style/TextViewStyle"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:text="Fragment 01" />

        </TableRow>
    </TableLayout>
</HorizontalScrollView>
-1赞 Jose V. Rodriguez 3/25/2017 #5

多滚动视图的屏幕截图

这是我使用在嵌套片段中同步的回收器视图的解决方案,可在 github 上找到: https://github.com/simplyAmazin87/MultiScrollView

这是它的要点,首先我们有主要的活动布局:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:background="@color/colorlight"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/tbl_border2"
            android:orientation="horizontal">

            <TextView
                android:text="MultiScroll Table View"
                android:id="@+id/statText"
                android:textSize="24sp"
                android:paddingRight="20dp"
                android:textColor="@color/colorDarkBlue"
                android:gravity="center_horizontal|center_vertical"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>

        <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:tools="http://schemas.android.com/tools"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/main_content"
            tools:context=".activities.MainActivity">

            <!-- TODO: Update blank fragment layout -->

        </FrameLayout>

    </LinearLayout>

然后我们有了主片段,其中定义了标题的静态部分,标题水平部分的回收器视图,以及滚动视图中的框架布局,以便我们可以添加要垂直滚动的内容:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:splitMotionEvents="true"
        android:layout_width="match_parent"
        android:background="@color/colorlight"
        android:layout_margin="2dp"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:splitMotionEvents="false"
            android:orientation="horizontal">
            <RelativeLayout
                android:layout_width="80dp"
                android:layout_height="wrap_content">
                <TextView
                    android:layout_width="match_parent"
                    android:textAppearance="?android:attr/textAppearanceLarge"
                    android:gravity="center_horizontal"
                    android:background="@drawable/tbl_border"
                    android:layout_height="wrap_content"
                    android:text="Static1"
                    android:id="@+id/hdr_Col_St1"
                    android:textSize="20dp"
                    android:padding="4dp"
                    android:textColor="@color/colorlight"/>
            </RelativeLayout>
            <RelativeLayout
                android:layout_width="80dp"
                android:layout_height="wrap_content">
                <TextView
                    android:layout_width="match_parent"
                    android:textAppearance="?android:attr/textAppearanceLarge"
                    android:gravity="center_horizontal"
                    android:background="@drawable/tbl_border"
                    android:layout_height="wrap_content"
                    android:text="Static2"
                    android:id="@+id/hdr_Col_St2"
                    android:textSize="20dp"
                    android:padding="4dp"
                    android:textColor="@color/colorlight"/>
            </RelativeLayout>
            <android.support.v7.widget.RecyclerView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@drawable/tbl_border"
                android:id="@+id/hdr_recycler_view"
                ></android.support.v7.widget.RecyclerView>

        </LinearLayout>

        <android.support.v4.widget.NestedScrollView
            android:id="@+id/vertical_scroll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <FrameLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:id="@+id/detail_content">
            </FrameLayout>

        </android.support.v4.widget.NestedScrollView>

    </LinearLayout>

最后,我们有了最终的布局,其中我们定义了表格中可以垂直滚动的部分,以及一个水平方向的回收器视图,该视图将能够双向滚动。此回收器视图必须与为标头定义的回收器视图同步才能正常工作:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="2dp"
        android:splitMotionEvents="false"
        android:orientation="horizontal">
        <RelativeLayout
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:background="@drawable/tbl_border2">

            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:text="A1"
                android:layout_marginTop="5dp"
                android:id="@+id/ColA1"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A2"
                android:gravity="center_horizontal"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA1"
                android:id="@+id/ColA2"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A3"
                android:layout_marginTop="5dp"
                android:gravity="center_horizontal"
                android:layout_below="@id/ColA2"
                android:id="@+id/ColA3"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A4"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA3"
                android:id="@+id/ColA4"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A5"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA4"
                android:id="@+id/ColA5"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A6"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA5"
                android:id="@+id/ColA6"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A7"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA6"
                android:id="@+id/ColA7"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A8"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA7"
                android:id="@+id/ColA8"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A9"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA8"
                android:id="@+id/ColA9"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A10"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA9"
                android:id="@+id/ColA10"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A11"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA10"
                android:id="@+id/ColA11"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A12"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA11"
                android:id="@+id/ColA12"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A13"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA12"
                android:id="@+id/ColA13"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A14"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA13"
                android:id="@+id/ColA14"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="A15"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColA14"
                android:id="@+id/ColA15"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />

        </RelativeLayout>
        <RelativeLayout
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:background="@drawable/tbl_border2">

            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:gravity="center_horizontal"
                android:text="B1"
                android:layout_marginTop="5dp"
                android:id="@+id/ColB1"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B2"
                android:gravity="center_horizontal"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB1"
                android:id="@+id/ColB2"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B3"
                android:layout_marginTop="5dp"
                android:gravity="center_horizontal"
                android:layout_below="@id/ColB2"
                android:id="@+id/ColB3"
                android:textSize="20dp"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B4"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB3"
                android:id="@+id/ColB4"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B5"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB4"
                android:id="@+id/ColB5"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B6"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB5"
                android:id="@+id/ColB6"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B7"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB6"
                android:id="@+id/ColB7"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B8"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB7"
                android:id="@+id/ColB8"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B9"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB8"
                android:id="@+id/ColB9"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B10"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB9"
                android:id="@+id/ColB10"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B11"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB10"
                android:id="@+id/ColB11"
                android:textSize="20dp"
                android:gravity="center_horizontal"
                android:padding="4dp"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B12"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB11"
                android:id="@+id/ColB12"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B13"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB12"
                android:id="@+id/ColB13"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B14"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB13"
                android:id="@+id/ColB14"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
            <TextView
                android:layout_width="match_parent"
                android:textAppearance="?android:attr/textAppearanceLarge"
                android:layout_height="wrap_content"
                android:text="B15"
                android:layout_marginTop="5dp"
                android:layout_below="@id/ColB14"
                android:id="@+id/ColB15"
                android:textSize="20dp"
                android:padding="4dp"
                android:gravity="center_horizontal"
                android:textColor="@color/colorDarkBlue"
                />
        </RelativeLayout>

        <android.support.v7.widget.RecyclerView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/tbl_border2"
            android:id="@+id/dtl_recyler_view"
            ></android.support.v7.widget.RecyclerView>

    </LinearLayout>

评论

0赞 Jose V. Rodriguez 3/26/2017
在那里,你用更详细的描述编辑了我的答案。
0赞 CoolMind 4/4/2017 #6

我找到了两个工作的例子。http://justsimpleinfo.blogspot.com/2015/04/android-scrolling-table-with-fixed.html

https://www.codeofaninja.com/2013/08/android-scroll-table-fixed-header-column.htmlhttps://www.youtube.com/watch?v=VCjlcV20ftE)。 在第二种情况下,将显示警告,因此可以使用 ID 类型的 Android - 预期资源进行修复。

第一个链接中的代码。

MainActivity.java

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Table table = new Table(this);
        setContentView(table);
    }
}

表.java

public class Table extends LinearLayout {


 public static final String PREVIOUS_ARROW = "\u2190";
 public static final String NEXT_ARROW = "\u2192";
 public static final  int HEADER_BACKROUND_COLOR = Color.parseColor("#339999");
 public static final  int BODY_BACKROUND_COLOR = Color.parseColor("#99cccc");
 public static String LEFT_BODY_SCROLLVIEW_TAG = "LEFT_BODY_SCROLLVIEW_TAG";
 public static String RIGHT_BODY_SCROLLVIEW_TAG = "RIGHT_BODY_SCROLLVIEW_TAG";
 /**
  * @IS_TWO_COLUMN_HEADER = set this to true if you want two column header with span.
  */
 public static final  boolean IS_TWO_COLUMN_HEADER = true;

 LinkedHashMap<Object, Object[]> leftHeaders = new LinkedHashMap<Object, Object[]>();
 LinkedHashMap<Object, Object[]> rightHeaders = new LinkedHashMap<Object, Object[]>();

 BodyTable rightTable;
 BodyTable leftTable;
 /**
  * @leftHeaderChildrenWidht = value will be set on adjust header width to match in screen width
  */
 Integer[] leftHeaderChildrenWidth ;
 /**
  * rightHeaderChildrenWidht = value will be set on adjust header width to match in screen width
  */
 Integer[] rightHeaderChildrenWidht ;


 LoadingDialog loadingDialog;

 public Table(Context context) {
  super(context);

  this.headers();
  this.properties();
  this.init();

  this.resizeFirstLvlHeaderHeight();
  this.resizeSecondLvlHeaderHeight();
  this.resizeHeaderSecondLvlWidhtToMatchInScreen();

  this.leftTable.setHeaderChildrenWidth(this.leftHeaderChildrenWidth);
  this.rightTable.setHeaderChildrenWidth(this.rightHeaderChildrenWidht);


  this.createTestData();
  this.loadData();


 }

 public final static String NAME = "Name";
 public final static String GENDER = "Gender";
 public final static String TICKET_SET_SEQUENCE = "Set Sequence";
 public final static String TICKET_NUMBER = "Ticket Number";
 public final static String TICKET_VALID_UNTIL = "    Valid Until    ";
 public final static String COUNTRY_FROM = "  Country From  ";
 public final static String COUNTRY_TO = "  Country To  ";
 public void headers(){
  leftHeaders.put("Passenger Info", new String[]{NAME,GENDER});
  rightHeaders.put("Ticket Info", new String[]{TICKET_VALID_UNTIL,TICKET_NUMBER,TICKET_SET_SEQUENCE});
  rightHeaders.put("Country Info", new String[]{COUNTRY_FROM,COUNTRY_TO});

 }


 List<Passenger> testData = new ArrayList<Table.Passenger>();
 List<Passenger> dataToBeLoad = new ArrayList<Table.Passenger>();
 int pagination = 20;
 int totalPage = 0;
 int pageNumber = 1;

 public void loadData() {



  // TODO Auto-generated method stub
  this.dataToBeLoad = this.getDataToBeLoad();

  leftTable.loadData(dataToBeLoad);
  rightTable.loadData(dataToBeLoad);


  this.resizeBodyChildrenHeight();

 }

 private void createTestData(){
  for(int x = 0 ; x < 102; x++){
   Passenger passenger = new Passenger();
   passenger.name = "Passenger "+x;
   passenger.gender = x%2 == 0 ? 'F':'M';
   passenger.ticketNum = x;
   passenger.setSequence = "Set "+x;
   passenger.validUntil = "May 01, 2015";
   passenger.countryFrom = "Country "+x;
   passenger.countryTo = x%2 == 0 ? "Philippines" :"Country "+x;

   testData.add(passenger);
  }

  this.totalPage = this.totalPage(testData, pagination);
  /*this.dataToBeLoad = this.getDataToBeLoad();*/
 }
 private List<Passenger> getDataToBeLoad(){
  List<Passenger> passengers = new ArrayList<Table.Passenger>();
  int startingIndex = (pageNumber -1) * pagination;

  int totalPassenger = testData.size();
  //dataToBeLoad.clear();

  for(int x = 0 ; x < pagination ; x++){

   int index = startingIndex + x;

   if(index < totalPassenger){

    passengers.add(testData.get(index));

   }else{
    Log.e("no data","no data");
   }

  }



  return passengers;
 }
 private int totalPage(List<Passenger> testData,int pagination){

  int totalPage = testData.size() / pagination;
  totalPage = totalPage + (testData.size() % 20 == 0 ? 0 : 1);

  return totalPage;

 }
 private void properties(){ 
  this.setBackgroundColor(Color.WHITE);
  this.setOrientation(LinearLayout.HORIZONTAL);
 }

 private void init(){

  this.loadingDialog = new LoadingDialog(this.getContext());
  this.rightTable = new BodyTable(this.getContext(),this, rightHeaders, RIGHT_BODY_SCROLLVIEW_TAG);
  this.leftTable = new BodyTable(this.getContext(),this,leftHeaders, LEFT_BODY_SCROLLVIEW_TAG);



  this.addView(this.leftTable);
  this.addView(this.rightTable);
 }
 private void resizeFirstLvlHeaderHeight(){
  int rightHeaderLinearLayoutChildCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  int rightHeaderFirstLvlHeighestHeight = 0;
  int rightHeaderFirstLvlHighestHeightIndex = 0;

  for(int x = 0 ; x < rightHeaderLinearLayoutChildCount; x++){

   HeaderRow row = (HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x);

   int height = ViewSizeUtils.getViewHeight(row.firtLvlLinearLayout);

   if(rightHeaderFirstLvlHeighestHeight  <= height){

    rightHeaderFirstLvlHeighestHeight = height;
    rightHeaderFirstLvlHighestHeightIndex = x;
   }
  }

  int leftHeaderLinearLayoutChildCount = leftTable.headerHorizontalLinearLayout.getChildCount();

  int leftHeaderFirstLvlHeighestHeight = 0;
  int leftHeaderFirstLvlHighestHeightIndex = 0;

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount; x++){

   HeaderRow row = (HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x);

   int height = ViewSizeUtils.getViewHeight(row.firtLvlLinearLayout);

   if(leftHeaderFirstLvlHeighestHeight  <= height){

    leftHeaderFirstLvlHeighestHeight = height;
    leftHeaderFirstLvlHighestHeightIndex = x;
   }
  }

  boolean isHighestHighInLeft = false;



  if(leftHeaderFirstLvlHeighestHeight < rightHeaderFirstLvlHeighestHeight){
   // apply right header height in left and right except for the index in highest height

   isHighestHighInLeft = false;


  }else{

   isHighestHighInLeft = true;

  }

  for(int x = 0 ; x < rightHeaderLinearLayoutChildCount; x++){

   LinearLayout firstLvlLinearLayout = ((HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x)).firtLvlLinearLayout;


   if(isHighestHighInLeft){

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,leftHeaderFirstLvlHeighestHeight);
    params.weight = 1;

    firstLvlLinearLayout.setLayoutParams(params);

   }else{

    if(rightHeaderFirstLvlHeighestHeight  != rightHeaderFirstLvlHighestHeightIndex){
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,rightHeaderFirstLvlHeighestHeight);
     params.weight = 1;

     firstLvlLinearLayout.setLayoutParams(params);

    }

   }


  }

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount; x++){

   LinearLayout firstLvlLinearLayout = ((HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x)).firtLvlLinearLayout;


   if(isHighestHighInLeft){

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,leftHeaderFirstLvlHeighestHeight);
    params.weight = 1;

    firstLvlLinearLayout.setLayoutParams(params);

   }else{

    if(leftHeaderFirstLvlHeighestHeight  != leftHeaderFirstLvlHighestHeightIndex){
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,rightHeaderFirstLvlHeighestHeight);
     params.weight = 1;

     firstLvlLinearLayout.setLayoutParams(params);

    }

   }


  }

 }

 private void resizeSecondLvlHeaderHeight(){
  int rightHeaderLinearLayoutChildCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  int rightHeaderFirstLvlHeighestHeight = 0;
  int rightHeaderFirstLvlHighestHeightIndex = 0;

  for(int x = 0 ; x < rightHeaderLinearLayoutChildCount; x++){

   HeaderRow row = (HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x);

   int height = ViewSizeUtils.getViewHeight(row.secondLvlLinearLayout);

   if(rightHeaderFirstLvlHeighestHeight  <= height){

    rightHeaderFirstLvlHeighestHeight = height;
    rightHeaderFirstLvlHighestHeightIndex = x;
   }
  }

  int leftHeaderLinearLayoutChildCount = leftTable.headerHorizontalLinearLayout.getChildCount();

  int leftHeaderFirstLvlHeighestHeight = 0;
  int leftHeaderFirstLvlHighestHeightIndex = 0;

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount; x++){

   HeaderRow row = (HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x);

   int height = ViewSizeUtils.getViewHeight(row.secondLvlLinearLayout);

   if(leftHeaderFirstLvlHeighestHeight  <= height){

    leftHeaderFirstLvlHeighestHeight = height;
    leftHeaderFirstLvlHighestHeightIndex = x;
   }
  }

  boolean isHighestHighInLeft = false;



  if(leftHeaderFirstLvlHeighestHeight < rightHeaderFirstLvlHeighestHeight){
   // apply right header height in left and right except for the index in highest height

   isHighestHighInLeft = false;


  }else{

   isHighestHighInLeft = true;

  }


  for(int x = 0 ; x < rightHeaderLinearLayoutChildCount; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;


   if(isHighestHighInLeft){

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,leftHeaderFirstLvlHeighestHeight);
    params.weight = 1;

    secondLvlLinearLayout.setLayoutParams(params);

   }else{

    if(rightHeaderFirstLvlHeighestHeight  != rightHeaderFirstLvlHighestHeightIndex){
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,rightHeaderFirstLvlHeighestHeight);
     params.weight = 1;

     secondLvlLinearLayout.setLayoutParams(params);

    }

   }


  }

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;


   if(isHighestHighInLeft){

    LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,leftHeaderFirstLvlHeighestHeight);
    params.weight = 1;

    secondLvlLinearLayout.setLayoutParams(params);

   }else{

    if(leftHeaderFirstLvlHeighestHeight  != leftHeaderFirstLvlHighestHeightIndex){
     LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,rightHeaderFirstLvlHeighestHeight);
     params.weight = 1;

     secondLvlLinearLayout.setLayoutParams(params);

    }

   }


  }

 }

 private void resizeHeaderSecondLvlWidhtToMatchInScreen(){
  int screenWidth = ScreenUtils.getScreenWidth(this.getContext());
  int leftHeaderChildrenTotalWidth = this.leftSecondLvlHeaderChildrenTotalWidth();
  int rightHeaderChildrenTotalWidth = this.rightHeaderChildrenTotalWidth();
  int leftHeaderSecondLvlChildrenCount = this.leftSecondLvlHeaderChildrenCount();
  int rightHeaderSecondLvlChildrenCount = this.rightSecondLvlHeaderChildrenCount();
  float availableWidth = screenWidth - (leftHeaderChildrenTotalWidth + rightHeaderChildrenTotalWidth);

  if(availableWidth <=0){
   // set the header width
   this.leftHeaderChildrenWidth = this.getLeftHeaderChildrenWidth();
   this.rightHeaderChildrenWidht = this.getRightHeaderChildrenWidth();

   return;
  }

  int widthForEachHeaderChild = (int) Math.ceil(availableWidth / (leftHeaderSecondLvlChildrenCount + rightHeaderSecondLvlChildrenCount));


  this.addWidthForEachHeaderLeftAndRightChild(widthForEachHeaderChild);
  // set the header width
  this.leftHeaderChildrenWidth = this.getLeftHeaderChildrenWidth();
  this.rightHeaderChildrenWidht = this.getRightHeaderChildrenWidth();

 }
 /**
  * get children count in left header
  * @return
  */
 private int leftSecondLvlHeaderChildrenCount(){
  int totalChildren = 0;
  int leftHeaderLinearLayoutChildCount = leftTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount ; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;
   totalChildren += secondLvlLinearLayout.getChildCount();


  }

  return totalChildren;
 }
 /**
  * get children count in right header
  * @return
  */
 private int rightSecondLvlHeaderChildrenCount(){
  int totalChildren = 0;
  int leftHeaderLinearLayoutChildCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount ; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;
   totalChildren += secondLvlLinearLayout.getChildCount();


  }

  return totalChildren;
 }
 /**
  * Compute total header width in left header
  * @return
  */
 private int leftSecondLvlHeaderChildrenTotalWidth(){
  int totalWidth = 0;
  int leftHeaderLinearLayoutChildCount = leftTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount ; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;
   int leftColumnChildrenCount = secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < leftColumnChildrenCount ; y++){
    View view  = secondLvlLinearLayout.getChildAt(y);
    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view) : params.width;

    totalWidth += width;

   }

  }

  return totalWidth;
 }
 /**
  * Compute total right header children width
  * @return
  */
 private int rightHeaderChildrenTotalWidth(){
  int totalWidth = 0;
  int leftHeaderLinearLayoutChildCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < leftHeaderLinearLayoutChildCount ; x++){

   LinearLayout secondLvlLinearLayout = ((HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x)).secondLvlLinearLayout;
   int leftColumnChildrenCount = secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < leftColumnChildrenCount ; y++){
    View view  = secondLvlLinearLayout.getChildAt(y);
    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view) : params.width;

    totalWidth += width;

   }

  }

  return totalWidth;
 }
 /**
  * Add width in left and right children width if needed to match screen width.
  * @param widthToBeAdded
  */
 private void addWidthForEachHeaderLeftAndRightChild(int widthToBeAdded){

  int leftHeaderColumnCount = leftTable.headerHorizontalLinearLayout.getChildCount();
  int rightHeaderColumnCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < leftHeaderColumnCount ; x++){

   HeaderRow tableRow =  (HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x);
   int headerRowChildCount = tableRow.secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < headerRowChildCount ; y++){

    View view = tableRow.secondLvlLinearLayout.getChildAt(y);

    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view) + widthToBeAdded : params.width +widthToBeAdded;
    params.width = width;
   }


  }

  for(int x = 0 ; x < rightHeaderColumnCount ; x++){

   HeaderRow tableRow =  (HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x);
   int headerRowChildCount = tableRow.secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < headerRowChildCount ; y++){

    View view = tableRow.secondLvlLinearLayout.getChildAt(y);

    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view) + widthToBeAdded : params.width +widthToBeAdded;
    params.width = width;
   }


  }

 }
 /**
  * Get each width of left header child
  * @return
  */
 private Integer[] getLeftHeaderChildrenWidth(){

  List<Integer> headerChildrenWidth = new ArrayList<Integer>();

  int leftHeaderColumnCount = leftTable.headerHorizontalLinearLayout.getChildCount();


  for(int x = 0 ; x < leftHeaderColumnCount ; x++){

   HeaderRow tableRow =  (HeaderRow) leftTable.headerHorizontalLinearLayout.getChildAt(x);
   int headerRowChildCount = tableRow.secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < headerRowChildCount ; y++){

    View view = tableRow.secondLvlLinearLayout.getChildAt(y);

    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view): params.width ;

    headerChildrenWidth.add(width);

   }


  }


  return headerChildrenWidth.toArray(new Integer[headerChildrenWidth.size()]);
 }
 /**
  * Get each width of right header child
  * @return
  */
 private Integer[] getRightHeaderChildrenWidth(){

  List<Integer> headerChildrenWidth = new ArrayList<Integer>();

  int rightHeaderColumnCount = rightTable.headerHorizontalLinearLayout.getChildCount();

  for(int x = 0 ; x < rightHeaderColumnCount ; x++){

   HeaderRow tableRow =  (HeaderRow) rightTable.headerHorizontalLinearLayout.getChildAt(x);
   int headerRowChildCount = tableRow.secondLvlLinearLayout.getChildCount();

   for(int y = 0 ; y < headerRowChildCount ; y++){

    View view = tableRow.secondLvlLinearLayout.getChildAt(y);

    LinearLayout.LayoutParams params = (LayoutParams) view.getLayoutParams();

    int width = params.width <=0 ? ViewSizeUtils.getViewWidth(view) : params.width ;

    headerChildrenWidth.add(width);
   }


  }

  return headerChildrenWidth.toArray(new Integer[headerChildrenWidth.size()]);
 }
 /**
  * Resize each body column to match each other
  */
 private  void resizeBodyChildrenHeight(){

  int leftHeaderFirstLvlHighestHeight = 0;


  for(LinearLayout lin : leftTable.bodyLinearLayoutTempMem){

   int childCount = lin.getChildCount();

   for(int x = 0 ; x < childCount; x++){
    int width = ViewSizeUtils.getViewHeight(lin.getChildAt(x));
    if(leftHeaderFirstLvlHighestHeight < width){
     leftHeaderFirstLvlHighestHeight = width;

    }
   }


  }

  int rightHeaderFirstLvlHighestHeight = 0;
  //int rightHeaderFirstLvlHighestHeightIndex = 0;
  for(LinearLayout lin : rightTable.bodyLinearLayoutTempMem){

   int childCount = lin.getChildCount();

   for(int x = 0 ; x < childCount; x++){
    int width = ViewSizeUtils.getViewHeight(lin.getChildAt(x));
    if(rightHeaderFirstLvlHighestHeight < width){
     rightHeaderFirstLvlHighestHeight = width;
     //rightHeaderFirstLvlHighestHeightIndex = x;
    }
   }


  }

  boolean isHighestHighInLeft = leftHeaderFirstLvlHighestHeight > rightHeaderFirstLvlHighestHeight;


  for(LinearLayout lin : leftTable.bodyLinearLayoutTempMem){

   int childCount = lin.getChildCount();

   for(int x = 0 ; x < childCount; x++){
    LinearLayout.LayoutParams params = (LayoutParams) lin.getChildAt(x).getLayoutParams();
    params.height = isHighestHighInLeft ? leftHeaderFirstLvlHighestHeight : rightHeaderFirstLvlHighestHeight;

   }


  }

  for(LinearLayout lin : rightTable.bodyLinearLayoutTempMem){

   int childCount = lin.getChildCount();

   for(int x = 0 ; x < childCount; x++){
    LinearLayout.LayoutParams params = (LayoutParams) lin.getChildAt(x).getLayoutParams();
    params.height = isHighestHighInLeft ? leftHeaderFirstLvlHighestHeight : rightHeaderFirstLvlHighestHeight;

   }


  }

 }
 /**
  * 
  * @author lau
  *
  */
 class LoadingDialog extends Dialog{

   LoadingDialog(Context context) {
   super(context);
   this.setCancelable(false);
   this.requestWindowFeature(Window.FEATURE_NO_TITLE);
   this.init(context);
  }

   private void init(Context context){
    TextView textView = new TextView(context);
    textView.setText("Please wait loading data..");

    this.setContentView(textView);

   }
 }
 class Passenger{
  String name;
  char gender;
  int ticketNum;
  String validUntil;
  String setSequence;
  String countryFrom;
  String countryTo;
 }
}

依此类推,对不起,答案限制在 30 000 个字符以内。

2赞 Aman Srivastava 12/22/2018 #7

Fixed header tableview

Fixed header tableview

GitHub 链接

最近我遇到了一种情况,我需要前两行和第一列冻结。我得到了这个库,我根据我的要求修改了它并修复了一些错误。现在工作顺利。

评论

0赞 Noor Hossain 11/2/2020
您可以将文本重力设置为中心吗?我被困在这里。
1赞 Ant Waters 1/26/2022 #8

十年过去了,这仍然非常困难! Jetpack Compose 现在有一个 DataTable 控件,但该控件尚不适用于 Android:Jetpack Compose:数据表

我一直在开发一个 Kotlin 应用程序供我自己的学习,并希望显示一个数据表。我从这个线程中汲取了想法,以及链接的滚动:同步 ScrollView 滚动位置 - android

完整代码可在 GitHub 上找到:CovidStatistics

该表如下面的屏幕截图所示。您可以看到数据表具有网格线,并在两个方向上滚动。此外,它还在某些单元格上显示图标。

我的解决方案将以下内容添加到此线程中上面介绍的内容中:

  • 数据单元格是在布局文件中定义的,而不是在代码中定义的,这样可以更轻松地在单元格中显示复杂的布局。图标随机显示为一个示例。
  • 将数据添加到表中后会自动设置列宽,而不必事先选择。

我希望使用 RecyclerViews,而不是在代码中创建行和列。我在这方面取得了一半,但只是无法让水平滚动条与 RecyclerView 一起使用。

Data Table with gridlines, icons and scrolling in both directions

以下是表片段的布局代码:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center_horizontal"
    android:id="@+id/main_area"
    android:layout_marginTop="0in"
    android:layout_marginBottom="0in"
    android:layout_marginLeft="0in"
    android:layout_marginRight="0in"
    android:background="@android:color/holo_green_dark">

    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:tooltipText="Loading data"
        android:visibility="visible" />

    <LinearLayout android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:id="@+id/header_area"
        >

        <!-- Top left cell, in a table of its own-->
        <TableLayout
            android:id="@+id/top_left_cell"
            android:layout_width="wrap_content"
            android:layout_height="fill_parent"
            app:layout_constraintStart_toStartOf="parent"
            android:paddingLeft="0in"
            android:paddingRight="0in"
            android:paddingTop="0in"
            android:paddingBottom="0in"
            />

        <!-- Column header horizontal scroll-->
        <com.ant_waters.covidstatistics.ui.ObservableHorizontalScrollView
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:id="@+id/column_header_scroll"
            >

            <!-- Column Headers-->
            <TableLayout
                android:id="@+id/table_header"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                >
            </TableLayout>
        </com.ant_waters.covidstatistics.ui.ObservableHorizontalScrollView>
    </LinearLayout>

    <!-- Data area vertical scroll-->
    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        >
        <LinearLayout android:orientation="horizontal"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="center_horizontal"
            android:id="@+id/fillable_area"
            >

            <!-- Data row headers-->
            <TableLayout
                android:id="@+id/fixed_column"
                android:layout_width="wrap_content"
                android:layout_height="fill_parent"
                app:layout_constraintStart_toStartOf="@+id/table_header"
                android:paddingLeft="0in"
                android:paddingRight="0in"
                android:paddingTop="0in"
                android:paddingBottom="0in"
                />

            <!-- Data rows horizontal scroll-->
            <com.ant_waters.covidstatistics.ui.ObservableHorizontalScrollView
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                android:id="@+id/data_horizontal_scroll"
                >

                <!-- Data rows-->
                <TableLayout
                    android:id="@+id/scrollable_part"
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:paddingLeft="0in"
                    android:paddingRight="0in"
                    android:paddingTop="0in"
                    android:paddingBottom="0in"
                    />
            </com.ant_waters.covidstatistics.ui.ObservableHorizontalScrollView>
        </LinearLayout>
    </ScrollView>
</LinearLayout>

以下是其他关键部分的布局代码:

Header_cell:

<!--This defines the layout for a header cell in the data table-->

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:id="@+id/cell_linear_layout"
    android:layout_marginTop="0in"
    android:layout_marginBottom="0in"
    android:layout_marginLeft="0in"
    android:layout_marginRight="0in"
    >
    <!--    android:gravity="center_horizontal"-->
    <!--    android:background="@android:color/holo_green_dark"-->

    <TextView
        android:id="@+id/cell_text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginEnd="2dp"
        android:layout_marginBottom="2dp"
        android:textColor="@color/black"
        android:textSize="20.0sp"
        android:layout_gravity="center_horizontal|center_vertical"
        android:paddingStart="15sp"
        android:paddingEnd="15sp"
        android:paddingTop="5sp"
        android:paddingBottom="5sp"
        android:background="#4b8ba1"
        ></TextView>
</LinearLayout>

包含图标的数据单元格:

    <!--This defines the layout for a data cell that includes a warning icon in the data table-->

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cell_linear_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <!--    This layer is needed to set the margin, as that doesn't seem to work at the top level -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="2dp"
        android:layout_marginTop="2dp"
        android:layout_marginEnd="2dp"
        android:layout_marginBottom="2dp"
        android:background="@android:color/white"
        android:orientation="horizontal">

        <ImageView
            android:layout_width="20dp"
            android:layout_height="20dp"
            android:layout_gravity="center_vertical"
            android:src="@drawable/ic_baseline_warning_24" />

        <TextView
            android:id="@+id/cell_text_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal|center_vertical"
            android:paddingStart="15sp"
            android:paddingTop="5sp"
            android:paddingEnd="15sp"
            android:paddingBottom="5sp"
            android:textColor="@color/black"
            android:textSize="20.0sp"></TextView>
    </LinearLayout>
</LinearLayout>

标题单元格背景:

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@android:color/darker_gray" />
</shape>

在构建表格时,它会为所有要显示的单元格填充二维视图数组。

fun displayTheDataTable(inflater: LayoutInflater)
{
    if (MainViewModel.DataInitialised.value==enDataLoaded.All) {
        // Display the table by creating Views for cells, headers etc.
        val allCells = displayDataTable(inflater)

        // Add a callback to set the column widths at the end (when onGlobalLayout is called)
        val content: View = _binding!!.mainArea
        content.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
            override fun onGlobalLayout() {
                //Remove the observer so we don't get this callback for EVERY layout pass
                content.viewTreeObserver.removeGlobalOnLayoutListener(this)

                //Resize the columns to match the maximum width
                setColumnWidths(allCells, fun (v: View, w: Int) {
                    val tv = v.findViewById<View>(com.ant_waters.covidstatistics.R.id.cell_text_view) as TextView
                    var lp  = LayoutParams(w, LayoutParams.WRAP_CONTENT)
                    v.layoutParams = lp

                    tv.setGravity(Gravity.CENTER)
                })
            }
        })
    }
}

这用于设置末尾的列宽:

fun setColumnWidths(allCells: Array<Array<View?>>, setItemWidth: (v: View, w: Int) -> Unit)
{
    val numColumns: Int = allCells[0].size
    val colWidths = Array<Int>(numColumns, {0})
    for (r in 0..allCells.size-1)
    {
        if ((allCells[r] == null) || (allCells[r][0] == null)) { continue }    // Row was skipped
        for (c in 0..numColumns-1)
        {
            val vw : View = allCells[r][c]!!
            if (vw.width > colWidths[c]) { colWidths[c] = vw.width}
        }
    }
    for (r in 0..allCells.size-1)
    {
        if ((allCells[r] == null) || (allCells[r][0] == null)) { continue }    // Row was skipped
        for (c in 0..numColumns-1)
        {
            val vw = allCells[r][c]!! as LinearLayout
            setItemWidth(vw, colWidths[c])
        }
    }
}

以下是从布局文件中定义的“模板”构建单个单元格的方法:

fun createHeaderCellFromTemplate(inflater: LayoutInflater, text: String?): View {
    val cellView: View = inflater.inflate(com.ant_waters.covidstatistics.R.layout.header_cell, null)
    val tv = cellView.findViewById<View>(com.ant_waters.covidstatistics.R.id.cell_text_view) as TextView
    tv.text = text

    return cellView
}
fun <TRowHdr>createRowHeaderCellFromTemplate(inflater: LayoutInflater, rowHdr: TRowHdr): View {
    val cellView: View = inflater.inflate(com.ant_waters.covidstatistics.R.layout.header_cell, null)
    val tv = cellView.findViewById<View>(com.ant_waters.covidstatistics.R.id.cell_text_view) as TextView

    if  (rowHdr is Date)
    {
        val dt = rowHdr as Date
        var formatter = SimpleDateFormat("dd/MM/yy")
        tv.text = formatter.format(dt)
    }
    else {
        tv.text = rowHdr.toString()
    }

    return cellView
}

fun <Tval>createDataCellFromTemplate(inflater: LayoutInflater, theVal: Tval,
                                     showWarning: Boolean, countryName: String
): View {
    var templateId = com.ant_waters.covidstatistics.R.layout.data_cell

    var text = theVal.toString()
    if (theVal is Double) {
        val df1 = DecimalFormat("#")
        val df2 = DecimalFormat("#.0")
        text = getProportionalDisplayText(text.toDouble(), df1, df2)
    }

    if (showWarning) { templateId = com.ant_waters.covidstatistics.R.layout.warning_data_cell }
    val cellView: View = inflater.inflate(templateId, null)

    val tv = cellView.findViewById<View>(com.ant_waters.covidstatistics.R.id.cell_text_view) as TextView
    tv.text = text


    if (DataManager.CountriesByName.containsKey(countryName)) {
        cellView.setOnClickListener(View.OnClickListener {
            val cpf = CountryPopupFragment()

            // Supply country as an argument.
            val args = Bundle()

            args.putString("geoId", DataManager.CountriesByName[countryName]!!.geoId)
            cpf.setArguments(args)

            cpf.show(requireActivity()?.getSupportFragmentManager(), "countrypopup_from_datatable")
        })
    }

    return cellView
}

有关代码的其余部分,请参阅 Github 存储库。

评论

0赞 Hakanai 12/26/2022
请注意,您链接到此答案的文章包含一堆指向不存在的 API 的链接,因此我不确定它是否合法。