ExpandableTextView 源码逐行精讲:400 行代码如何实现完整展开收起交互
ExpandableTextView 源码逐行精讲400 行代码如何实现完整展开收起交互【免费下载链接】ExpandableTextViewAndroid TextView that can expand and collapse.项目地址: https://gitcode.com/gh_mirrors/expand/ExpandableTextViewExpandableTextView 是一款开源的 Android 展开收起文本控件Expandable TextView让开发者用一行setText就能给长文本加上展开 / 收起按钮和流畅的高度动画。全文约 400 行的 ExpandableTextView.java 核心源码中隐藏着几个非常实用的 Android 技巧二次测量判断文本高度、Animation做高度动画、onInterceptTouchEvent防连点。本文将逐段拆解它的实现思路新手也能轻松读懂。1. 效果一览它能做什么从上面的演示动图可以看到它的完整交互文本超过设定行数时自动截断并显示「展开」按钮点击后以 300ms 高度动画展开全文按钮变为「收起」支持在ListView列表项中使用且每项的展开状态互不干扰。整个控件对外暴露的能力只有两行核心 API见 ExpandableTextView.java 第 252~269 行public void setText(Nullable CharSequence text) // 带列表位置参数用于 ListView 场景 public void setText(CharSequence text, SparseBooleanArray collapsedStatus, int position)2. 整体结构一个 LinearLayout 装两个 TextView控件继承自LinearLayout强制竖直方向内部通过merge布局装了两个TextView布局文件expandabletextview/src/main/res/layout/expandabletextview.xml成员变量角色mTv显示正文内容的 TextViewmStateTv显示「展开 / 收起」文字 图标的按钮// 构造时直接 inflate 内部布局强制竖直排列 LayoutInflater.from(context).inflate(R.layout.expandabletextview, this, true); setOrientation(LinearLayout.VERTICAL); setVisibility(GONE); // 默认隐藏setText 后才显示 细节setOrientation被重写传入横向布局会直接抛异常——作者从 API 层面杜绝了误用。3. 自定义属性速查11 个 XML 属性一次配置所有可配置项都定义在expandabletextview/src/main/res/values/attrs.xml通过TypedArray在init()中统一读取并赋默认值me.chensir.expandabletextview.ExpandableTextView app:maxCollapsedLines4 !-- 收起时最多显示几行默认 8 -- app:animDuration300 !-- 动画时长默认 300ms -- app:contentTextSize16sp !-- 正文字号 -- app:contentTextColorcolor/blue !-- 正文颜色 -- app:contentLineSpacingMultiplier1.2 app:expandDrawabledrawable/... !-- 展开按钮图标 -- app:collapseDrawabledrawable/... app:expandText更多 !-- 按钮文案 -- app:collapseText收起 app:expandCollapseTextColorcolor/pink app:DrawableAndTextGravityleft !-- 按钮位置left/center/right -- /源码中「取属性 → 判空 → 用默认值兜底」的写法如R.string.expand_string、R.drawable.ic_expand_more_black_12dp是自定义 View 处理属性的标准范式值得模仿。4. 核心逻辑 ①onMeasure 如何判断文本太长判断是否需要收起作者没有用字符串长度估算而是在onMeasure中做了两次测量源码 206~246 行// 第一次乐观测量——假设文本完全放得下 mStateTv.setVisibility(View.GONE); mTv.setMaxLines(Integer.MAX_VALUE); super.onMeasure(widthMeasureSpec, heightMeasureSpec); // 行数没超过上限 → 不需要按钮直接返回 if (mTv.getLineCount() mMaxCollapsedLines) { return; } // 确实超长记下完整文本高度再收起测量一次 mTextHeightWithMaxLines getRealTextViewHeight(mTv); if (mCollapsed) { mTv.setMaxLines(mMaxCollapsedLines); } mStateTv.setVisibility(View.VISIBLE); super.onMeasure(widthMeasureSpec, heightMeasureSpec); mCollapsedHeight getMeasuredHeight();这个先放开、再收起的测量策略保证了短文本不会出现多余的「展开」按钮mTextHeightWithMaxLines完整高度与mCollapsedHeight收起高度两个动画所需数值都被精确拿到。mRelayout标志位配合setText()确保只有文本变化时才重新执行这套测量避免无谓开销。5. 核心逻辑 ②onClick 中的状态切换点击正文或按钮都会走到onClick源码 137~190 行逻辑清晰分四步mCollapsed !mCollapsed; // 1. 翻转状态 mStateTv.setText(mCollapsed ? mExpandString : mCollapsedString); // 2. 换文案 mStateTv.setCompoundDrawablesWithIntrinsicBounds( mCollapsed ? mExpandDrawable : mCollapseDrawable, null, null, null); // 3. 换图标 // 4. 创建动画收起 → 目标 mCollapsedHeight展开 → 目标 当前高 超出部分 Animation animation mCollapsed ? new ExpandCollapseAnimation(this, getHeight(), mCollapsedHeight) : new ExpandCollapseAnimation(this, getHeight(), getHeight() mTextHeightWithMaxLines - mTv.getHeight());动画结束后的收尾也很讲究public void onAnimationEnd(Animation animation) { clearAnimation(); // 清除动画防止 applyTransformation 被反复调用 mAnimating false; if (mListener ! null) { mListener.onExpandStateChanged(mTv, !mCollapsed); // 回调通知业务层 } }通过setOnExpandStateChangeListener()注册的回调在动画结束后触发业务侧可以安全地基于展开状态做埋点或联动。6. 核心逻辑 ③ExpandCollapseAnimation 的高度插值这是全项目最精华的约 30 行——一个只改高度的Animation内部类源码 364~393 行protected void applyTransformation(float interpolatedTime, Transformation t) { // 在 startHeight 与 endHeight 之间按插值因子线性计算高度 final int newHeight (int) ((mEndHeight - mStartHeight) * interpolatedTime mStartHeight); mTv.setMaxHeight(newHeight - mMarginBetweenTxtAndBottom); // 约束正文可见高度 mTargetView.getLayoutParams().height newHeight; // 同时压住整个容器 mTargetView.requestLayout(); }两点设计巧思setMaxHeight与容器高度双管齐下正文用maxHeight裁剪容器用固定height布局动画过程中布局不跳动、不遮挡下方控件willChangeBounds()返回true明确告诉系统动画会改变视图尺寸让父布局正确响应。注意这里的动画本质是高度渐变 逐帧重布局所以性能关键在requestLayout()的开销。默认 300ms 的时长就是流畅性与开销的平衡点。7. 实战细节三连防连点、列表状态、空文本源码里还有三处容易被忽略的健壮性设计动画期间拦截所有触摸——避免连点导致动画叠加错乱Override public boolean onInterceptTouchEvent(MotionEvent ev) { return mAnimating; // 动画进行中拦截全部触摸事件 }ListView 状态保持——setText(text, collapsedStatus, position)重载把每项的收起状态存进SparseBooleanArrayViewHolder 复用时仍能还原上次的展开/收起状态示例用法见sample/src/main/java/me/chensir/expandabletextview/sample/SampleTextListAdapter.java空文本自动隐藏——setText中对空文本直接setVisibility(GONE)避免空控件占据布局空间。8. 三步快速上手3 行代码接入将expandabletextview模块导入你的 Android 工程布局 XML 中声明控件并配置属性参考sample/src/main/res/layout/activity_main.xml在 Activity 中设置文本即可参考sample/src/main/java/me/chensir/expandabletextview/sample/MainActivity.java((ExpandableTextView) findViewById(R.id.tv)).setText(longText);如果想在自己的项目中完整研读这份源码可以克隆仓库后打开expandabletextview/src/main/java/me/chensir/expandabletextview/ExpandableTextView.java对照本文的四个小节顺序结构 → 属性 → 测量 → 动画阅读效率最高git clone https://link.gitcode.com/i/c916707246c5538c1be63a6a03e32326.git总结400 行代码的精髓技巧位置可复用场景二次 onMeasure 判断内容溢出第 206 行任何内容自适应控件高度插值 Animation第 364 行展开面板、评论区折叠onInterceptTouchEvent 防连点第 193 行一切带动画的按钮SparseBooleanArray 保存列表状态第 258 行ListView 中保持子视图状态ExpandableTextView 虽然已停止维护但它的实现模式至今仍是 Android 自定义 View 教学的优质范本——读懂这 400 行你就掌握了展开收起交互背后的全部核心知识。【免费下载链接】ExpandableTextViewAndroid TextView that can expand and collapse.项目地址: https://gitcode.com/gh_mirrors/expand/ExpandableTextView创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

HackingToolkit信息收集实战教程:Nmap、RED HAWK与ReconSpider的OSINT侦察完全指南

HackingToolkit信息收集实战教程:Nmap、RED HAWK与ReconSpider的OSINT侦察完全指南

HackingToolkit信息收集实战教程:Nmap、RED HAWK与ReconSpider的OSINT侦察完全指南 【免费下载链接】hackingtoolkit ALL IN ONE Hacking Tool For Hackers, Penetration Tester and Cybersecurity. New Version Beginner to Advanced Tool. This Tool is made for …

2026/8/24 9:08:22 阅读更多 →
贝叶斯与动机性推理融合:构建更智能、更适应性的AI决策智能体

贝叶斯与动机性推理融合:构建更智能、更适应性的AI决策智能体

1. 从“理性”到“有偏”:AI智能体推理范式的演进在AI领域,我们常常追求构建“绝对理性”的智能体——一个能像理想中的科学家一样,基于所有可用证据,通过贝叶斯定理不断更新信念,最终无限逼近真相的完美存在。这听起来…

2026/8/24 9:08:22 阅读更多 →
拆解Pyrlang核心架构:Node、Rex、NetKernel与Asyncio事件循环如何协同工作(全图景)

拆解Pyrlang核心架构:Node、Rex、NetKernel与Asyncio事件循环如何协同工作(全图景)

拆解Pyrlang核心架构:Node、Rex、NetKernel与Asyncio事件循环如何协同工作(全图景) 【免费下载链接】Pyrlang Erlang node implemented in Python 3.5 (Asyncio-based) 项目地址: https://gitcode.com/gh_mirrors/py/Pyrlang Pyrlang …

2026/8/24 9:07:22 阅读更多 →

最新新闻

TNFR-1/CD120a:TNF信号通路的关键受体与炎症调控核心

TNFR-1/CD120a:TNF信号通路的关键受体与炎症调控核心

简述 本文围绕TNFR-1(CD120a)的分子结构与生物学功能,系统阐述其作为肿瘤坏死因子受体超家族核心成员的分类归属、结构特征及其在炎症信号转导与细胞凋亡调控中的关键作用。一、TNFR-1的分子归属与家族定位肿瘤坏死因子受体-1(TNF…

2026/8/24 10:58:00 阅读更多 →
树突状细胞亚群的分类、标志物与功能特征

树突状细胞亚群的分类、标志物与功能特征

简述 本文围绕树突状细胞的分类体系,系统阐述经典DC(cDC1与cDC2)、浆细胞样DC及单核细胞来源DC在人源和小鼠体系中的表面标志物、细胞因子谱及核心功能特征。一、树突状细胞的发育与功能定位树突状细胞于1973年由Steinman首次发现&#xff0c…

2026/8/24 10:58:00 阅读更多 →
5分钟本地解密音乐文件:Unlock-Music完整指南(免安装、不上传)

5分钟本地解密音乐文件:Unlock-Music完整指南(免安装、不上传)

5分钟本地解密音乐文件:Unlock-Music完整指南(免安装、不上传) 【免费下载链接】unlock-music 在浏览器中解锁加密的音乐文件。原仓库: 1. https://github.com/unlock-music/unlock-music ;2. https://git.unlock-musi…

2026/8/24 10:58:00 阅读更多 →
C++模板与友元结合时链接错误的根源与三种解决方案

C++模板与友元结合时链接错误的根源与三种解决方案

1. 项目概述:当友元遇上模板,一个经典的编译难题在C的进阶之路上,模板和友元是两个绕不开的强大特性。模板让我们能编写与类型无关的通用代码,而友元则打破了类的封装壁垒,允许特定外部函数或类访问其私有成员。当你信…

2026/8/24 10:58:00 阅读更多 →
蓝桥杯备赛实战:从省一到国二的算法竞赛心法与策略复盘

蓝桥杯备赛实战:从省一到国二的算法竞赛心法与策略复盘

1. 从省一到国二:我的蓝桥杯实战复盘与心路历程又到了蓝桥杯赛季,看到不少学弟学妹在备赛,也让我想起了自己当年参赛的经历。我拿的是第十一届蓝桥杯广东省赛区一等奖,最终在全国总决赛中拿到了二等奖。这个成绩不算顶尖&#xff…

2026/8/24 10:57:59 阅读更多 →
从零构建AI漫剧:基于Stable Diffusion与ComfyUI的完整工作流指南

从零构建AI漫剧:基于Stable Diffusion与ComfyUI的完整工作流指南

在数字内容创作领域,AI绘画与视频生成技术的结合,正催生出一种名为“AI漫剧”的新兴内容形式。它并非传统意义上的动画或漫画,而是通过AI工具,将静态的AI绘画串联成具有动态效果和叙事性的短片,常用于短视频平台&#…

2026/8/24 10:56:59 阅读更多 →

日新闻

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践 前端安全依赖分层防护。没有任何单一配置能替代输出编码、权限校验和依赖更新。 把不可信内容当作数据 默认使用框架的转义能力;确需渲染 HTML 时,先在服务端或可信的客户端库中进行白名单过滤。避免把用户输入直接赋给 inne…

2026/8/24 1:08:15 阅读更多 →
Windows登录密码存储机制全解析:从哈希算法到安全加固实战

Windows登录密码存储机制全解析:从哈希算法到安全加固实战

1. 项目概述:Windows登录密码的“黑匣子”每次你按下CtrlAltDel,输入密码,然后看到那个熟悉的桌面,这背后发生了一系列复杂而精密的操作。作为一名长期与Windows系统打交道的从业者,我经常被问到:“我的密码…

2026/8/24 1:08:15 阅读更多 →
AI面试系统安全挑战与解决方案

AI面试系统安全挑战与解决方案

1. 项目概述:AI面试系统的安全挑战去年参与某跨国企业AI面试系统部署时,遇到一个典型案例:候选人在视频面试中无意提到竞争对手产品名称,系统竟自动将该信息关联到企业知识库并生成竞品分析报告。这个看似"智能"的功能&…

2026/8/24 1:08:15 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/24 0:06:02 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/24 0:20:20 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/24 0:14:11 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/23 18:47:06 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/23 12:10:44 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →