重拾安卓:自定义View之表格封装实现

本文介绍如何在Android中实现自定义表格View,包括两层RecyclerView嵌套,实现行滚动和平分列宽的功能。通过组合现有控件,创建自定义StudentWorkTableView,对外提供设置数据的方法。讲解了从需求分析、代码实现到使用的全过程,涉及布局文件、适配器及样式设置。
摘要由CSDN通过智能技术生成

今天开始更新【重拾安卓】系列文章。

因业务需要又要做一个 Android 原生的项目,记录下时隔几年之后再开发安卓的那些事。讲的不会太基础,基本上是自定义View封装,复杂功能的实现等等,有需要的小伙伴可以关注~


安卓对表格的支持不是太友好,前端很快能实现的简单表格,安卓写的话要费很大精力。

拿到需求之后,稍微复杂点的功能在 github 上搜一下有没有好用的第三方框架,无疑是最节省时间的。表格还真有几个不错的框架,star 最多的是 smartTable ,的确很强大,只需设置数据就能自动生成表格。

但考虑各种因素还是决定自己撸一个表格,一是后端返回的数据结构还没定,二是需求并不是太复杂,只是个简单表格,三是找找手感~

一、需求分析及实现原理

最终效果:

实现目标:

  1. 行数不固定,超出父容器可以上下滚动
  2. 列数不固定,不管有多少列,都平分父容器宽度,每列的宽度一致
  3. 表头设置灰色背景,单元格是白色背景

实现原理:

两层 RecyclerView 嵌套,最外层是垂直方向的 RecyclerView,每一行是一个 item。每行又包含一个内层 RecyclerView,每行的每个单元格是内层 RecyclerViewitem

二、代码实现

为了方便重用,我们把这个课表封装成自定义 View,并对外暴露一个方法设置数据。

Android 自定义 View 有三种方式:组合、扩展、重写。我们这里用的是组合的方式,即把已有的控件组合起来形成符合需求的自定义控件。

2.1 自定义View 主文件 StudentWorkTableView

新建一个 Java 类 StudentWorkTableView 并继承 LinearLayout ,实现它的构造方法,就创建了一个自定义 View。

为什么继承 LinearLayout ?其实继承其他的 RelativeLayoutConstraintLayout 都可以,一般是你的 xml 最外层用的是什么布局,就继承什么。

构造方法要实现三个,因为不同的创建方式走的构造方法不一样,所以都要求实现。

构造方法小技巧:把前两个参数少的构造方法里的 super 改成 this,并填充默认值变成三个参数,就会都调用三个参数的构造方法了,业务逻辑只需写在最后一个构造方法里即可。

这个 View 很简单,先在构造方法里绑定 xml 布局,再执行初始化方法初始数据,然后在 onLayout 中计算每个单元格的宽度,最后对外暴露一个方法设置数据。自定义 View 基本都是这个套路。

注意这里用到了第三方框架 ButterKnife ,简化了 findViewById ,不熟悉的同学可以查查相关资料。

代码注释写的比较详细,就不多说了直接看代码。

public class StudentWorkTableView extends LinearLayout {
   

    @BindView(R.id.recycler_view_week_table)
    RecyclerView recyclerView;

    private Context mContext;

    private List<TableListModel> mList;
    private int mCellWidth;
    private StudentWorkTableAdapter mTableAdapter;

    public StudentWorkTableView(Context context) {
   
        this(context, null, 0);
    }

    public StudentWorkTableView(Context context, @Nullable AttributeSet attrs) {
   
        this(context, attrs, 0);
    }

    public StudentWorkTableView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
   
        super(context, attrs, defStyleAttr);
        View view = View.inflate(context, R.layout.view_student_work_table, this);
        ButterKnife.bind(view, this);
        mContext = context;
    }

    /**
     * 对外暴露的方法,设置表格的数据
     *
     * @param list
     */
    public void setData(List<TableListModel> list) {
   
        mList = list;
        init();
    }

    /**
     * 初始化方法
     */
    private void init() {
   
        LinearLayoutManager lm = new LinearLayoutManager(mContext, LinearLayoutManager.VERTICAL, false);
        recyclerView.setLayoutManager(lm);
        recyclerView.setItemAnimator(new DefaultItemAnimator());
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
   
        super.onLayout(changed, l, t, r, b);
        // onLayout 时 View 的宽高已经确定了,可以拿到比较准确的值
        int width = getWidth();
        // 计算每列即每个单元格的宽度。用 View 总宽度除以列数就得到了每个单元格的宽度
        mCellWidth = width / mList.get(0).getTableList().size();
        if (mTableAdapter == null) {
   
            //把单元格宽度传给 Adapter,在 Adapter 中对单元格重设宽度
            mTableAdapter = new StudentWorkTableAdapter(mContext, mCellWidth, R.layout.item_student_work_table_view, mList);
            recyclerView.setAdapter(mTableAdapter);
        }
    }

}

2.2 布局文件 view_student_work_table.xml

对应的布局文件 view_student_work_table.xml

布局很简单,只有一个 RecyclerView

<?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="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view_week_table"
        
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值