news 2026/9/19 20:07:47

Plaid自定义控件教程:打造拖拽弹性关闭的FrameLayout,5步掌握实现套路

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Plaid自定义控件教程:打造拖拽弹性关闭的FrameLayout,5步掌握实现套路

Plaid自定义控件教程:打造拖拽弹性关闭的FrameLayout,5步掌握实现套路

【免费下载链接】plaidAn Android app which provides design news & inspiration as well as being an example of implementing material design.项目地址: https://gitcode.com/gh_mirrors/pl/plaid

Plaid 是一款经典的 Android 设计资讯应用,也是 Material Design 实践的标杆项目。其中ElasticDragDismissFrameLayout是一个"拖拽弹性关闭"自定义控件:页面跟着手指走、越拉越费劲(弹性阻尼),拉过阈值就自动关闭页面,没拉够则平滑回弹。本文将它拆解为 5 个步骤,帮你掌握这类自定义控件的完整实现套路 💪

🧩 第 1 步:理解原理——嵌套滚动如何驱动弹性拖拽

很多新手做"下拉关闭页面"时会直接拦截触摸事件,结果和内部列表的滚动冲突,体验很糟。Plaid 的方案更优雅:不抢手势,而是监听嵌套滚动(Nested Scroll)中"没被消费掉的偏移量"

核心逻辑在 ElasticDragDismissFrameLayout.java:

  • onStartNestedScroll():只声明对垂直方向的嵌套滚动感兴趣;
  • onNestedScroll():子列表滚到头后,剩余的dyUnconsumed才交给父布局做拖拽位移;
  • onNestedPreScroll():处理用户中途"反向回拉"的手势,避免状态混乱。

💡 套路总结:凡是"包裹可滚动内容、要做二次手势"的控件,优先用 NestedScroll 接口,而不是onInterceptTouchEvent抢事件。

🎛️ 第 2 步:在 XML 中声明控件并配置 4 个属性

控件继承自FrameLayout,所以用法就是"套一层壳"。Plaid 在 Dribbble 作品详情页用它包裹整个内容区,见 activity_dribbble_shot.xml:

<io.plaidapp.core.ui.widget.ElasticDragDismissFrameLayout android:id="@+id/draggable_frame" android:layout_width="match_parent" android:layout_height="match_parent" app:dragDismissDistance="@dimen/drag_dismiss_distance" app:dragDismissScale="0.95"> <!-- 你的页面内容,比如 NestedScrollView --> </io.plaidapp.core.ui.widget.ElasticDragDismissFrameLayout>

四个可配置属性定义在 attrs_elastic_drag_dismiss_frame_layout.xml:

属性含义默认值
dragDismissDistance触发关闭的拖拽距离(像素)无穷大
dragDismissFraction按父布局高度百分比设定距离-1(未启用)
dragDismissScale拖拽时的缩放终点比例,0.95即缩到 95%1(不缩放)
dragElasticity弹性系数,位移放大/衰减倍数0.8

Plaid 把距离设为 112dp(正好是 2 倍 Action Bar 高度),手感恰到好处——这个"基于屏幕尺寸而非固定像素"的取数思路值得学习。

📐 第 3 步:弹性公式与回弹动画(核心数学)

"橡皮筋手感"的关键在 dragScale() 里的一个对数公式:

// 拖拽比例 0~1,越接近阈值,增长越慢 float dragFraction = (float) Math.log10(1 + (Math.abs(totalDrag) / dragDismissDistance));

为什么用log10(1 + x)

  • 拖拽初期:位移≈真实手指位移,跟手灵敏;
  • 接近阈值后:位移按对数递减,怎么拉也拉不到头,产生"拉不动"的橡皮筋阻力感。

配合dragElasticity(0.8)衰减总位移,再按需同步缩放(shouldScale时把缩放轴心设为底部或顶部,见 L205-L211,视觉上就像页面从被拉出的那一端"翘起")。

松手后的处理在 onStopNestedScroll():

  • 位移 ≥ 阈值 → 派发关闭回调;
  • 否则 → 用 200ms 的fastOutSlowIn插值器动画回到原位(这正是 Android 官方的"快出慢进"曲线,回弹不生硬)。

💡 套路总结:对数衰减做阻尼 + 系统级插值器做回弹,两件套就是流畅弹性的全部秘密。

🔔 第 4 步:回调监听与系统栏渐隐

控件通过 ElasticDragDismissCallback 抽象类对外暴露两个回调:

  • onDrag(elasticOffset, elasticOffsetPixels, rawOffset, rawOffsetPixels):每帧拖拽都回调,同时给出"弹性后"和"原始"两组偏移量,方便你做渐变、缩放等联动效果;
  • onDragDismissed():确认关闭时触发。

内置的 SystemChromeFader 是一个现成的演示:向下拖时按rawOffset比例渐隐状态栏,向上拖时渐隐底部导航栏,关闭时调用finishAfterTransition()让转场动画自然衔接。子类化它即可定制自己的联动效果,参考 ShotActivity.kt。

⏱️ 第 5 步:把监听器绑定到 Activity 生命周期

最后一步是防内存泄漏的正确姿势,Plaid 的做法(ShotActivity.kt):

override fun onResume() { super.onResume() binding.draggableFrame.addListener(chromeFader) } override fun onPause() { binding.draggableFrame.removeListener(chromeFader) super.onPause() }

只在页面可见时监听,不可见即解绑——回调注册与视图生命周期对齐,是自定义控件集成的通用规范。

📌 五步套路小结

步骤要点
1️⃣ 原理用 NestedScroll 消费剩余滚动量,不抢手势
2️⃣ 声明FrameLayout 子类 +declare-styleable自定义属性
3️⃣ 弹性对数公式做阻尼 + fastOutSlowIn 回弹动画
4️⃣ 回调抽象 Callback 暴露 onDrag / onDragDismissed
5️⃣ 生命周期onResume 注册、onPause 解绑

这个控件在 Plaid 的设计新闻详情页、Dribbble 作品页和 About 页面均有应用,源码集中在 core/src/main/java/io/plaidapp/core/ui/widget/ 目录。照着这 5 步,你就能为自己的页面加上下拉关闭、下拉刷新之外的第三种手势体验 ✨

【免费下载链接】plaidAn Android app which provides design news & inspiration as well as being an example of implementing material design.项目地址: https://gitcode.com/gh_mirrors/pl/plaid

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/19 20:07:43

AzerothCore-WoTLK Docker部署:3条命令起服

AzerothCore-WoTLK Docker部署&#xff1a;3条命令起服 【免费下载链接】azerothcore-wotlk Complete Open Source and Modular solution for MMO 项目地址: https://gitcode.com/GitHub_Trending/az/azerothcore-wotlk 自己搭一套魔兽世界私服&#xff0c;过去光配环境…

作者头像 李华
网站建设 2026/9/19 20:07:18

神经网络代理模型加速火箭发动机结构动力学优化

简介&#xff1a;一份基于神经网络技术的火箭发动机结构动力学优化PDF文档&#xff0c;面向从事液体火箭发动机设计、结构动力学分析及机器学习数据建模的工程师、科研人员和研究生。该论文针对大推力液体火箭发动机研制中低频结构动力学频率优化的关键问题&#xff0c;提出以改…

作者头像 李华
网站建设 2026/9/19 20:04:43

Open Code Review:开源可审计的AI代码评审新范式

1. “open-code-review”不是工具名&#xff0c;而是正在发生的协作范式迁移最近在几个开源项目里做贡献时&#xff0c;我明显感觉到一种变化&#xff1a;PR&#xff08;Pull Request&#xff09;页面底部的评论区&#xff0c;不再只是“LGTM”“1”或者“建议加个空行”这类人…

作者头像 李华
网站建设 2026/9/19 20:04:33

Claude.ai 加 Kling MCP 生成视频,模型通道走 TaoToken 行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/19 20:04:30

API 超时反复重试?TaoToken + Roo Code 这样验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华