玩转 Android TableLayout:结构化数据神器,实战全攻略

目录

前言:为什么 TableLayout 是 Android 表格布局的 “最优解”?

一、TableLayout 基础认知:到底什么是 “表格布局”?

1.1 一句话搞懂 TableLayout 的核心作用

1.2 必须澄清的 3 个常见误解

1.3 TableLayout 与其他布局的核心区别

1.4 TableLayout 的典型适用场景

二、TableLayout 核心属性:少而精,直击表格需求

2.1 核心属性 1:表格整体属性(控制列宽与布局)

示例 1:拉伸列(自适应屏幕,占满剩余空间)

示例 2:收缩列(内容过长时自动换行)

示例 3:隐藏列(不显示指定列)

2.2 核心属性 2:TableRow 属性(控制行样式)

示例 4:交替行色(表格美化)

2.3 核心属性 3:单元格属性(控制单个单元格)

示例 5:横向合并单元格(跨列)

示例 6:纵向合并单元格(跨行)

三、TableLayout 实战布局:覆盖高频场景,直接复用

3.1 场景 1:设置页面表单(APP 通用)

3.2 场景 2:商品规格选择表(电商 APP)

3.3 场景 3:个人信息表单(两列多行)

3.4 场景 4:动态表格(Java 代码添加行 / 列)

四、TableLayout 进阶技巧:合并单元格、样式美化与优化

4.1 进阶技巧 1:复杂合并单元格(跨行列组合)

示例 7:跨行列组合合并(复杂表格)

4.2 进阶技巧 2:表格样式美化(边框、阴影、间距)

示例 8:带边框的表格(美化样式)

4.3 进阶技巧 3:TableLayout 性能优化

优化 1:控制表格行数(≤20 行)

优化 2:避免过度合并单元格

优化 3:减少单元格嵌套

优化 4:使用 ScrollView 包裹大数据表格

4.4 进阶技巧 4:与其他布局组合(互补优势)

示例 9:TableLayout + ScrollView + LinearLayout(带操作栏的滚动表格)

五、TableLayout 常见坑点与避坑指南(实战经验总结)

5.1 坑点 1:列数不一致,表格布局错乱

5.2 坑点 2:拉伸 / 收缩列失效,列宽不自适应

5.3 坑点 3:合并单元格后,布局错位

5.4 坑点 4:表格没有边框,样式丑陋

5.5 坑点 5:动态添加行时,性能卡顿

5.6 坑点 6:单元格内容显示不全,被截断

六、总结:TableLayout 核心知识点图谱


class 卑微码农:
    def __init__(self):
        self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
        self.发量 = 100  # 初始发量
        self.咖啡因耐受度 = '极限'
        
    def 修Bug(self, bug):
        try:
            # 试图用玄学解决问题
            if bug.严重程度 == '离谱':
                print("这一定是环境问题!")
            else:
                print("让我看看是谁又没写注释...哦,是我自己。")
        except Exception as e:
            # 如果try块都救不了,那就...
            print("重启一下试试?")
            self.发量 -= 1  # 每解决一个bug,头发-1
 
 
# 实例化一个我
我 = 卑微码农()

前言:为什么 TableLayout 是 Android 表格布局的 “最优解”?

刚做 Android 开发时,我曾用 LinearLayout 嵌套了 5 层才实现一个简单的 3 行 3 列表格 —— 代码冗余到不敢轻易修改,不同屏幕下还容易错位。直到发现 TableLayout,才明白它的核心价值:以 “表格行列” 为核心,零嵌套搞定结构化数据展示,无需手动计算宽高,自动适配屏幕。

TableLayout(表格布局)就像 “Android 自带的 Excel 表格”,通过<TableRow>标签定义行,子 View 直接作为列,支持自动列数、拉伸 / 收缩列、合并单元格等功能。不管是数据报表、设置页面表单、商品规格选择,还是复杂的多列列表,用它实现都只需 1-2 层布局,代码简洁且维护成本低。

但很多开发者觉得 TableLayout “小众”,只会用基础的行列布局,不懂拉伸列、合并单元格、自适应屏幕等核心技巧,导致本该用它的场景却用了复杂嵌套。本文会把 TableLayout 的基础概念、核心属性、实战布局、进阶技巧、样式美化、避坑指南全讲透。全文包含 15 个完整可运行的原创 XML/Java 示例,新手能直接上手,进阶开发者能挖掘它的隐藏潜力。

建议先收藏,再跟着示例一步步实操,遇到问题可以在评论区交流~


一、TableLayout 基础认知:到底什么是 “表格布局”?

1.1 一句话搞懂 TableLayout 的核心作用

TableLayout 是 Android 专门用于展示结构化数据的布局容器,核心作用是按 “行列” 形式排列子 View—— 用<TableRow>标签定义表格行,每个<TableRow>中的子 View 自动作为一列,支持列宽自适应、拉伸 / 收缩列、合并单元格等表格特有功能,是展示多列数据的最优解。

简单说:TableLayout 就像 “一张空白表格”——<TableLayout>是整个表格的外框,<TableRow>是表格的每一行,TextView、Button 等子 View 是表格的单元格。比如 APP 的设置页面(图标 + 功能名称 + 开关,3 列多行)、电商的商品规格表(尺寸 + 颜色 + 库存,多列多行)、数据报表(日期 + 销量 + 营收,多列数据),用它实现无需手动控制列宽和对齐,表格自动排版。

1.2 必须澄清的 3 个常见误解

  • 误解 1:TableLayout 只能做 “固定列数” 表格?—— 错!支持自动列数(每行列数可不同)、动态列数(Java 代码添加行 / 列),灵活适配不同数据场景;
  • 误解 2:TableLayout 不能自适应屏幕?—— 错!通过stretchColumns(拉伸列)、shrinkColumns(收缩列)属性,能完美适配所有屏幕尺寸,列宽自动调整;
  • 误解 3:TableLayout 不如 RecyclerView+GridLayoutManager 灵活?—— 各有优势!TableLayout 适合静态 / 半静态表格(如设置页、表单),无需写适配器,代码更简洁;RecyclerView 适合动态大数据列表(如商品列表),支持复用和滑动优化。

1.3 TableLayout 与其他布局的核心区别

特性TableLayoutLinearLayoutRelativeLayoutGridLayout
核心定位结构化表格布局(行列排列)线性排列(水平 / 垂直)相对位置定位(无行列)网格布局(固定行列数)
列宽控制自动适配 + 拉伸 / 收缩属性,无需手动计算需手动设置权重或固定宽高需通过相对定位控制宽度需指定列数和列宽比例
嵌套需求1-2 层(TableLayout+TableRow)复杂表格需多层嵌套(≥3 层)复杂表格需多层定位1 层即可,但列数固定
上手难度中等(属性少,逻辑清晰)低(线性逻辑)中等(相对关系复杂)中等(需理解网格规则)
典型场景静态表格、表单、数据报表线性排版(表单、列表项)相对定位(个人中心、商品卡片)固定网格(图标矩阵、键盘)

1.4 TableLayout 的典型适用场景

  • 设置页面表单:图标(列 1)+ 功能名称(列 2)+ 开关 / 箭头(列 3),多行表格;
  • 商品规格选择:尺寸(列 1)+ 颜色(列 2)+ 库存(列 3)+ 选择按钮(列 4);
  • 数据报表展示:日期(列 1)+ 销量(列 2)+ 营收(列 3)+ 增长率(列 4);
  • 个人信息表单:标签(列 1)+ 内容(列 2),多行两列表格;
  • 复杂表单布局:多列多行的填写表单(如订单详情页、报名表单);
  • 动态数据表格:Java 代码动态添加行 / 列,展示后端返回的结构化数据。

二、TableLayout 核心属性:少而精,直击表格需求

TableLayout 的核心属性分为 “表格整体属性”“TableRow 属性”“单元格属性” 三类,每个属性都针对表格的核心需求(列宽控制、对齐、隐藏列等),下面结合示例逐个讲解。

2.1 核心属性 1:表格整体属性(控制列宽与布局)

这类属性作用于<TableLayout>标签,控制整个表格的列宽策略、对齐方式、边框等,是 TableLayout 的核心能力。

属性含义取值核心作用
stretchColumns设置可拉伸的列(占满剩余空间)列索引(0 开始)、*(所有列)、,分隔多列(如 0,2)让指定列拉伸,填充表格剩余空间(自适应屏幕)
shrinkColumns设置可收缩的列(超出时换行)同 stretchColumns列内容过长时,让指定列收缩换行,避免超出屏幕
collapseColumns设置要隐藏的列同 stretchColumns隐藏指定列,不显示在表格中
layout_column(单元格属性)指定单元格所在列列索引让单元格跳过指定列,直接显示在目标列
gravity控制表格内所有单元格的对齐方式left/right/center/center_horizontal/center_vertical让所有单元格统一对齐(如整体居中)
stretchAllColumns是否拉伸所有列(API 17+)true/false简化stretchColumns="*",让所有列平均拉伸
示例 1:拉伸列(自适应屏幕,占满剩余空间)
<!-- 3列表格,第2列拉伸,占满剩余空间 -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="1" <!-- 第2列(索引1)可拉伸 -->
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 第1行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="姓名"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="张三"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="25"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 第2行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="职业"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="Android开发者"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="3年"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:表格宽度占满屏幕,第 2 列(内容列)自动拉伸,占满剩余空间,第 1 列和第 3 列保持包裹内容宽度 —— 完美适配所有屏幕,无需手动设置权重。

示例 2:收缩列(内容过长时自动换行)
<!-- 2列表格,第2列可收缩(内容过长换行) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:shrinkColumns="1" <!-- 第2列可收缩 -->
    android:padding="16dp"
    android:background="#FFFFFF">

    <TableRow
        android:layout_height="wrap_content"
        android:padding="8dp">

        <TextView
            android:text="描述:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="这是一段超长的描述文本,TableLayout的shrinkColumns属性会让它自动换行,避免超出屏幕,无需手动控制宽度"
            android:textSize="16sp" />

    </TableRow>

</TableLayout>

效果:第 2 列的超长文本自动换行,表格高度自适应,不会超出屏幕 —— 用 LinearLayout 实现需要手动设置maxWidth,TableLayout 一句话搞定。

示例 3:隐藏列(不显示指定列)
<!-- 3列表格,隐藏第3列(索引2) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:collapseColumns="2" <!-- 隐藏第3列 -->
    android:stretchColumns="1"
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 第1行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="商品"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="价格"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="库存(隐藏)"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 第2行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="手机"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥1999"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="100"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:表格只显示前 2 列,第 3 列(库存)被隐藏 —— 适合根据用户权限动态控制列的显示 / 隐藏,无需修改布局结构。

2.2 核心属性 2:TableRow 属性(控制行样式)

这类属性作用于<TableRow>标签,控制单行的高度、背景、对齐方式等,让表格行样式更灵活。

属性含义取值核心作用
layout_height行高wrap_content/match_parent/ 固定值(如 48dp)控制单行高度,wrap_content 自适应内容
background行背景颜色(@color/xxx)、图片(@drawable/xxx)设置行背景色或背景图(如交替行色)
gravity行内所有单元格的对齐方式同 TableLayout 的 gravity让当前行的所有单元格统一对齐(覆盖表格整体对齐)
示例 4:交替行色(表格美化)
<!-- 4列表格,行背景交替显示(灰白相间) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="1,2" <!-- 第2、3列拉伸 -->
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 表头(深色背景) -->
    <TableRow
        android:layout_height="56dp"
        android:background="#333333">

        <TextView
            android:text="日期"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="销量"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="营收"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="增长率"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 第1行(白色背景) -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="2024-01-01"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="1000"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥100000"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="+12%"
            android:textSize="16sp"
            android:textColor="#FF6B6B"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 第2行(灰色背景) -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="2024-01-02"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="1200"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥120000"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="+20%"
            android:textSize="16sp"
            android:textColor="#4ECDC4"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:表头深色背景,数据行灰白交替,表格美观清晰 —— 这是数据报表的经典样式,用 TableLayout 实现无需嵌套,代码简洁。

2.3 核心属性 3:单元格属性(控制单个单元格)

这类属性作用于<TableRow>中的子 View(单元格),控制单个单元格的对齐、合并、列宽等,是实现复杂表格的关键。

属性含义取值核心作用
layout_gravity单元格对齐方式left/right/center/top/bottom单个单元格的对齐方式(覆盖行和表格的对齐)
layout_span横向合并单元格(跨列)合并的列数(如 2 表示跨 2 列)让单元格占据多个列的宽度(如表头跨列)
layout_rowSpan纵向合并单元格(跨行)合并的行数(如 2 表示跨 2 行)让单元格占据多个行的高度
minWidth单元格最小宽度固定值(如 80dp)确保单元格不会过窄,内容显示完整
maxWidth单元格最大宽度固定值(如 200dp)限制单元格最大宽度,避免过宽
示例 5:横向合并单元格(跨列)
<!-- 3列表格,表头跨3列,数据行正常显示 -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="0,1,2"
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 表头(跨3列) -->
    <TableRow
        android:layout_height="56dp"
        android:background="#FF6B6B">

        <TextView
            android:text="2024年1月销售报表"
            android:textSize="18sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:layout_span="3" /> <!-- 跨3列 -->

    </TableRow>

    <!-- 列名行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="日期"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="销量"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="营收"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 数据行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="2024-01-01"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="1000"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥100000"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:表头文本跨 3 列显示,居中对齐,数据行列数正常 —— 这是报表类表格的常用布局,用 TableLayout 的layout_span属性一句话实现,无需复杂嵌套。

示例 6:纵向合并单元格(跨行)
<!-- 3列表格,第1列的单元格跨行合并 -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="1,2"
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 第1行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="手机"
            android:textSize="16sp"
            android:gravity="center"
            android:layout_rowSpan="2" /> <!-- 跨2行 -->

        <TextView
            android:text="华为Mate 60"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥5999"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 第2行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <!-- 第1列被上一行合并,无需重复添加 -->
        <TextView
            android:text="苹果iPhone 15"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="¥7999"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:第 1 列的 “手机” 单元格跨 2 行显示,与下方两行的第 2、3 列对齐 —— 纵向合并适合分类展示数据,同一分类下的多行共用一个分类标签。


三、TableLayout 实战布局:覆盖高频场景,直接复用

掌握核心属性后,结合实际开发中的高频场景,实现从简单到复杂的布局案例,每个案例都附完整代码和效果说明,可直接复制到项目中使用。

3.1 场景 1:设置页面表单(APP 通用)

<!-- 设置页面:3列多行表格(图标+功能名称+箭头) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:stretchColumns="1" <!-- 第2列(功能名称)拉伸 -->
    android:background="#F5F5F5">

    <!-- 第1行:账号设置 -->
    <TableRow
        android:layout_height="56dp"
        android:background="#FFFFFF"
        android:layout_marginTop="8dp">

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_person"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="账号设置"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_arrow_right"
            android:layout_gravity="center"
            android:layout_marginRight="16dp" />

    </TableRow>

    <!-- 第2行:通知设置 -->
    <TableRow
        android:layout_height="56dp"
        android:background="#FFFFFF">

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_notifications"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="通知设置"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

        <Switch
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_marginRight="16dp"
            android:checked="true" />

    </TableRow>

    <!-- 第3行:隐私设置 -->
    <TableRow
        android:layout_height="56dp"
        android:background="#FFFFFF">

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_lockscreen"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="隐私设置"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_arrow_right"
            android:layout_gravity="center"
            android:layout_marginRight="16dp" />

    </TableRow>

    <!-- 第4行:关于我们 -->
    <TableRow
        android:layout_height="56dp"
        android:background="#FFFFFF">

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_info_details"
            android:layout_gravity="center"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="关于我们"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

        <ImageView
            android:layout_width="24dp"
            android:layout_height="24dp"
            android:src="@android:drawable/ic_menu_arrow_right"
            android:layout_gravity="center"
            android:layout_marginRight="16dp" />

    </TableRow>

</TableLayout>

效果:典型的 APP 设置页面,3 列布局(图标 + 功能名称 + 箭头 / 开关),第 2 列拉伸占满剩余空间,适配所有屏幕 —— 用 LinearLayout 实现需要 4 层嵌套,TableLayout 仅 1 层,代码简洁易维护。

3.2 场景 2:商品规格选择表(电商 APP)

<!-- 商品规格表:4列多行(尺寸+颜色+库存+选择按钮) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="1,2" <!-- 颜色和库存列拉伸 -->
    android:padding="16dp"
    android:background="#FFFFFF">

    <!-- 表头 -->
    <TableRow
        android:layout_height="56dp"
        android:background="#333333">

        <TextView
            android:text="尺寸"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="颜色"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="库存"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="选择"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 规格1 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="S"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="黑色"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="50件"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <RadioButton
            android:layout_gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 规格2 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="M"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="白色"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="30件"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <RadioButton
            android:layout_gravity="center"
            android:padding="8dp" />

    </TableRow>

    <!-- 规格3 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="L"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="蓝色"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <TextView
            android:text="20件"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp" />

        <RadioButton
            android:layout_gravity="center"
            android:padding="8dp" />

    </TableRow>

</TableLayout>

效果:电商 APP 常用的商品规格选择表,4 列布局,颜色和库存列拉伸适配,单选按钮实现规格选择 —— 用 GridLayout 实现需要指定列数,TableLayout 自动适配列数,更灵活。

3.3 场景 3:个人信息表单(两列多行)

<!-- 个人信息表单:2列多行(标签+内容) -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="1" <!-- 内容列拉伸 -->
    android:padding="20dp"
    android:background="#FFFFFF">

    <!-- 姓名 -->
    <TableRow
        android:layout_height="56dp">

        <TextView
            android:text="姓名:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="张三"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

    </TableRow>

    <!-- 性别 -->
    <TableRow
        android:layout_height="56dp">

        <TextView
            android:text="性别:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="男"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

    </TableRow>

    <!-- 年龄 -->
    <TableRow
        android:layout_height="56dp">

        <TextView
            android:text="年龄:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="25岁"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

    </TableRow>

    <!-- 手机号 -->
    <TableRow
        android:layout_height="56dp">

        <TextView
            android:text="手机号:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="16dp" />

        <TextView
            android:text="138****1234"
            android:textSize="16sp"
            android:layout_gravity="center_vertical" />

    </TableRow>

    <!-- 地址 -->
    <TableRow
        android:layout_height="wrap_content">

        <TextView
            android:text="地址:"
            android:textSize="16sp"
            android:textStyle="bold"
            android:layout_gravity="top"
            android:layout_marginRight="16dp"
            android:layout_marginTop="8dp" />

        <TextView
            android:text="北京市朝阳区建国路88号某小区1号楼1单元101室"
            android:textSize="16sp"
            android:layout_gravity="top"
            android:layout_marginTop="8dp"
            android:maxLines="2"
            android:ellipsize="end" />

    </TableRow>

</TableLayout>

效果:个人信息展示表单,2 列布局(标签 + 内容),内容列拉伸占满剩余空间,地址列支持换行 —— 用 LinearLayout 实现需要 5 层嵌套,TableLayout 仅 1 层,维护成本极低。

3.4 场景 4:动态表格(Java 代码添加行 / 列)

public class DynamicTableActivity extends AppCompatActivity {
    private TableLayout mTableLayout;
    private String[][] mData = {
            {"2024-01-01", "1000", "¥100000", "+12%"},
            {"2024-01-02", "1200", "¥120000", "+20%"},
            {"2024-01-03", "1500", "¥150000", "+25%"},
            {"2024-01-04", "1300", "¥130000", "-13%"}
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dynamic_table);

        mTableLayout = findViewById(R.id.table_layout);
        // 设置表格属性:拉伸所有列
        mTableLayout.setStretchAllColumns(true);
        // 添加表头
        addTableHeader();
        // 添加数据行
        addDataRows();
    }

    // 添加表头
    private void addTableHeader() {
        TableRow headerRow = new TableRow(this);
        headerRow.setLayoutParams(new TableLayout.LayoutParams(
                TableLayout.LayoutParams.MATCH_PARENT,
                dp2px(56)
        ));
        headerRow.setBackgroundColor(Color.parseColor("#333333"));

        // 表头文本数组
        String[] headers = {"日期", "销量", "营收", "增长率"};
        for (String header : headers) {
            TextView textView = new TextView(this);
            textView.setText(header);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
            textView.setTextColor(Color.WHITE);
            textView.setGravity(Gravity.CENTER);
            textView.setPadding(dp2px(8), dp2px(8), dp2px(8), dp2px(8));
            headerRow.addView(textView);
        }

        mTableLayout.addView(headerRow);
    }

    // 添加数据行
    private void addDataRows() {
        for (int i = 0; i < mData.length; i++) {
            TableRow dataRow = new TableRow(this);
            dataRow.setLayoutParams(new TableLayout.LayoutParams(
                    TableLayout.LayoutParams.MATCH_PARENT,
                    dp2px(48)
            ));
            // 交替行色
            dataRow.setBackgroundColor(i % 2 == 0 ? Color.parseColor("#F5F5F5") : Color.WHITE);

            String[] rowData = mData[i];
            for (int j = 0; j < rowData.length; j++) {
                TextView textView = new TextView(this);
                textView.setText(rowData[j]);
                textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);
                textView.setGravity(Gravity.CENTER);
                textView.setPadding(dp2px(8), dp2px(8), dp2px(8), dp2px(8));

                // 增长率列设置颜色(正增长绿色,负增长红色)
                if (j == 3) {
                    if (rowData[j].contains("+")) {
                        textView.setTextColor(Color.parseColor("#4ECDC4"));
                    } else if (rowData[j].contains("-")) {
                        textView.setTextColor(Color.parseColor("#FF6B6B"));
                    }
                }

                dataRow.addView(textView);
            }

            mTableLayout.addView(dataRow);
        }
    }

    // dp转px工具方法
    private int dp2px(int dp) {
        return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
    }
}

配套 XML 布局(activity_dynamic_table.xml)

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:padding="16dp"
    android:background="#F5F5F5">

    <TableLayout
        android:id="@+id/table_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FFFFFF" />

</ScrollView>

效果:Java 代码动态添加表头和 4 行数据,交替行色,增长率列根据正负值设置不同颜色,表格可滚动 —— 适合展示后端返回的动态数据,无需提前写 XML 布局,灵活性极高。


四、TableLayout 进阶技巧:合并单元格、样式美化与优化

基础布局满足静态需求,实际开发中还需要实现复杂表格(如合并单元格)、美化表格样式、优化性能,下面讲解进阶技巧。

4.1 进阶技巧 1:复杂合并单元格(跨行列组合)

合并单元格是 TableLayout 的进阶功能,支持横向 + 纵向组合合并,实现复杂的表格结构(如不规则表格)。

示例 7:跨行列组合合并(复杂表格)
<!-- 复杂表格:横向合并+纵向合并组合 -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="*"
    android:padding="16dp"
    android:background="#FFFFFF"
    android:collapseColumns="3"> <!-- 隐藏第4列,用于调整布局 -->

    <!-- 第1行:横向合并2列 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="产品分类"
            android:textSize="16sp"
            android:gravity="center"
            android:layout_span="2" /> <!-- 跨2列 -->

        <TextView
            android:text="销量"
            android:textSize="16sp"
            android:gravity="center" />

        <TextView /> <!-- 隐藏列,占位 -->

    </TableRow>

    <!-- 第2行:纵向合并2行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="手机"
            android:textSize="16sp"
            android:gravity="center"
            android:layout_rowSpan="2" /> <!-- 跨2行 -->

        <TextView
            android:text="华为"
            android:textSize="16sp"
            android:gravity="center" />

        <TextView
            android:text="5000"
            android:textSize="16sp"
            android:gravity="center" />

        <TextView /> <!-- 隐藏列,占位 -->

    </TableRow>

    <!-- 第3行 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#F5F5F5">

        <TextView
            android:text="苹果"
            android:textSize="16sp"
            android:gravity="center" />

        <TextView
            android:text="3000"
            android:textSize="16sp"
            android:gravity="center" />

        <TextView /> <!-- 隐藏列,占位 -->

    </TableRow>

    <!-- 第4行:横向合并3列 -->
    <TableRow
        android:layout_height="48dp"
        android:background="#FFFFFF">

        <TextView
            android:text="总计"
            android:textSize="16sp"
            android:textStyle="bold"
            android:gravity="center" />

        <TextView
            android:text="8000"
            android:textSize="16sp"
            android:textStyle="bold"
            android:gravity="center"
            android:layout_span="2" /> <!-- 跨2列 -->

        <TextView /> <!-- 隐藏列,占位 -->

    </TableRow>

</TableLayout>

效果:表格包含横向合并(产品分类跨 2 列)、纵向合并(手机跨 2 行),实现复杂的不规则表格 —— 用其他布局几乎无法实现,TableLayout 通过layout_spanlayout_rowSpan组合轻松搞定。

4.2 进阶技巧 2:表格样式美化(边框、阴影、间距)

默认的 TableLayout 没有边框,通过自定义背景和属性,可实现带边框、阴影、间距的美观表格。

示例 8:带边框的表格(美化样式)
<!-- 带边框的表格:通过shape背景实现单元格边框 -->
<TableLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:stretchColumns="*"
    android:padding="16dp"
    android:background="#F5F5F5">

    <!-- 表头 -->
    <TableRow
        android:layout_height="56dp">

        <TextView
            android:text="姓名"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_header_border" />

        <TextView
            android:text="年龄"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_header_border" />

        <TextView
            android:text="职业"
            android:textSize="16sp"
            android:textColor="#FFFFFF"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_header_border" />

    </TableRow>

    <!-- 数据行1 -->
    <TableRow
        android:layout_height="48dp">

        <TextView
            android:text="张三"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

        <TextView
            android:text="25"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

        <TextView
            android:text="Android开发者"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

    </TableRow>

    <!-- 数据行2 -->
    <TableRow
        android:layout_height="48dp">

        <TextView
            android:text="李四"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

        <TextView
            android:text="30"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

        <TextView
            android:text="产品经理"
            android:textSize="16sp"
            android:gravity="center"
            android:padding="8dp"
            android:background="@drawable/table_cell_border" />

    </TableRow>

</TableLayout>

配套边框背景文件

  • table_header_border.xml(表头边框):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#333333" />
    <stroke android:width="1dp" android:color="#FFFFFF" /> <!-- 白色边框 -->
</shape>
  • table_cell_border.xml(单元格边框):
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#FFFFFF" />
    <stroke android:width="1dp" android:color="#E0E0E0" /> <!-- 灰色边框 -->
</shape>

效果:表格每个单元格都有边框,表头深色背景 + 白色边框,数据行白色背景 + 灰色边框,美观清晰 —— 这是企业级 APP 常用的表格样式,用 TableLayout+shape 背景轻松实现。

4.3 进阶技巧 3:TableLayout 性能优化

TableLayout 本身性能优秀,但在处理大数据或复杂表格时,需注意以下优化点,避免卡顿:

优化 1:控制表格行数(≤20 行)

TableLayout 的单元格没有复用机制,行数过多(如超过 50 行)会导致内存占用增加,绘制耗时变长。建议:

  • 静态表格行数控制在 20 行以内;
  • 动态大数据表格(如 100 行 +)改用 RecyclerView+GridLayoutManager,实现单元格复用。
优化 2:避免过度合并单元格

过多的跨行列合并会增加表格的测量和绘制耗时,建议:

  • 非必要不合并单元格,尽量保持规则表格;
  • 复杂合并表格的行数控制在 10 行以内。
优化 3:减少单元格嵌套

单元格内避免嵌套复杂布局(如 LinearLayout+TextView+ImageView),建议:

  • 单个单元格仅放一个 View(如 TextView、ImageView);
  • 复杂单元格内容用自定义 View 实现,而非嵌套布局。
优化 4:使用 ScrollView 包裹大数据表格

当表格行数超过屏幕高度时,用 ScrollView 包裹 TableLayout,实现滚动效果,避免表格超出屏幕:

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

    <TableLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!-- 表格内容 -->
    </TableLayout>

</ScrollView>

4.4 进阶技巧 4:与其他布局组合(互补优势)

TableLayout 适合表格布局,与 ScrollView、LinearLayout 组合,可实现更复杂的需求(如滚动表格、表格 + 按钮栏)。

示例 9:TableLayout + ScrollView + LinearLayout(带操作栏的滚动表格)
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#F5F5F5">

    <!-- 操作栏(水平布局) -->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="56dp"
        android:orientation="horizontal"
        android:background="#FFFFFF"
        android:gravity="center_vertical"
        android:paddingHorizontal="16dp">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="导出"
            android:background="#FF6B6B"
            android:textColor="#FFFFFF" />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:text="刷新"
            android:layout_marginLeft="16dp"
            android:background="#4ECDC4"
            android:textColor="#FFFFFF" />

    </LinearLayout>

    <!-- 滚动表格 -->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:padding="16dp">

        <TableLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:stretchColumns="*"
            android:background="#FFFFFF">

            <!-- 表格内容(复用示例4的报表表格) -->

        </TableLayout>

    </ScrollView>

</LinearLayout>

效果:顶部是操作栏(导出 + 刷新按钮),下方是可滚动的表格,兼顾了表格展示和操作功能 —— 三者组合,既发挥了 TableLayout 的表格优势,又满足了复杂布局的需求。


五、TableLayout 常见坑点与避坑指南(实战经验总结)

5.1 坑点 1:列数不一致,表格布局错乱

  • 现象:不同行的列数不同,导致表格列宽混乱、单元格错位;
  • 原因:TableLayout 的列数由列数最多的行决定,其他行列数不足时,会自动补空白列,导致布局错乱;
  • 解决方案:
    1. 确保所有行的列数一致,列数不足时添加空白 View 占位;
    2. 如需不同列数,用layout_span合并列,而非直接减少子 View 数量;
    3. 隐藏列用collapseColumns,而非直接删除单元格。

5.2 坑点 2:拉伸 / 收缩列失效,列宽不自适应

  • 现象:设置stretchColumnsshrinkColumns后,列宽未按预期拉伸 / 收缩;
  • 原因:
    1. 列索引设置错误(从 0 开始,而非 1);
    2. 单元格 View 的宽高设置为match_parent,限制了拉伸 / 收缩;
    3. 表格宽度未设为match_parent,没有剩余空间可拉伸;
  • 解决方案:
    1. 列索引从 0 开始(如第 2 列索引为 1),多列用逗号分隔(如 0,2);
    2. 单元格 View 的宽高设为wrap_content,避免限制尺寸;
    3. 表格layout_width设为match_parent,确保有剩余空间。

5.3 坑点 3:合并单元格后,布局错位

  • 现象:设置layout_spanlayout_rowSpan后,单元格位置错位、高度 / 宽度异常;
  • 原因:
    1. 合并列数 / 行数超过表格实际列数 / 行数;
    2. 合并后的单元格未设置gravity,导致内容对齐异常;
    3. 被合并的行 / 列未添加空白 View 占位;
  • 解决方案:
    1. 合并列数不超过表格最大列数,合并行数不超过表格总行数;
    2. 合并后的单元格设置gravity(如center),确保内容对齐;
    3. 纵向合并时,被合并的行需删除对应列的 View;横向合并时,无需额外操作。

5.4 坑点 4:表格没有边框,样式丑陋

  • 现象:默认 TableLayout 没有边框,单元格之间没有分隔,视觉效果差;
  • 原因:TableLayout 默认不显示边框,需手动设置单元格背景或表格背景;
  • 解决方案:
    1. 给每个单元格设置带边框的 shape 背景(如示例 8);
    2. 给 TableLayout 设置背景色,给 TableRow 设置间距,模拟边框;
    3. 使用第三方表格库(如 SmartTable),支持更丰富的边框样式。

5.5 坑点 5:动态添加行时,性能卡顿

  • 现象:Java 代码动态添加大量行(如 50 行 +),表格加载缓慢、滑动卡顿;
  • 原因:TableLayout 没有单元格复用机制,每行每列都是独立 View,大量 View 导致内存占用高、绘制耗时;
  • 解决方案:
    1. 动态表格行数控制在 20 行以内,超过则改用 RecyclerView+GridLayoutManager;
    2. 动态添加行时,批量添加(如先创建所有 TableRow,再一次性添加到 TableLayout),减少刷新次数;
    3. 避免给动态单元格设置复杂背景或嵌套布局,简化 View 结构。

5.6 坑点 6:单元格内容显示不全,被截断

  • 现象:单元格文本过长或 View 尺寸过大,导致内容被截断;
  • 原因:
    1. 未设置shrinkColumns,文本过长时未换行;
    2. 单元格 View 的maxWidth设置过小,限制了内容显示;
    3. 表格padding或单元格padding过大,挤压内容空间;
  • 解决方案:
    1. 给文本列设置shrinkColumns,让文本自动换行;
    2. 合理设置maxWidth,避免过小;
    3. 调整padding值,给内容留出足够空间;
    4. 文本设置ellipsize="end",超出时显示省略号。

六、总结:TableLayout 核心知识点图谱

到这里,TableLayout 的核心内容已经全部讲完,我们用一张图谱梳理重点:

TableLayout核心知识点
├── 基础认知:行列结构化布局、核心特性、与其他布局的区别、典型场景
├── 核心属性:
│   - 表格整体属性:stretchColumns(拉伸列)、shrinkColumns(收缩列)、collapseColumns(隐藏列)
│   - TableRow属性:layout_height(行高)、background(行背景)、gravity(行对齐)
│   - 单元格属性:layout_gravity(单元格对齐)、layout_span(横向合并)、layout_rowSpan(纵向合并)
├── 实战布局:设置页面、商品规格表、个人信息表单、动态表格
├── 进阶技巧:
│   - 复杂合并:横向+纵向组合合并
│   - 样式美化:边框、阴影、交替行色
│   - 性能优化:控制行数、减少嵌套、批量添加行
│   - 混合布局:与ScrollView/LinearLayout组合
├── 避坑指南:列数不一致、拉伸失效、合并错位、性能卡顿、内容截断

其实 TableLayout 的学习关键是 “理解行列逻辑 + 掌握列宽控制和合并技巧”—— 它不是 “万能布局”,但在静态表格、表单、数据报表等场景下,是最简洁、最高效的选择。很多开发者忽略它的价值,用复杂嵌套布局实现本可简单搞定的需求,反而增加了代码冗余和维护成本。

把文中的示例逐个敲一遍,结合 Android Studio 的布局预览实时查看效果,很快就能熟练掌握。如果遇到具体问题,比如 “合并单元格错位”“动态表格卡顿”,可以在评论区留言,我会第一时间回复~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值