Android实现网易严选标签栏滑动效果
发布时间 - 2026-01-11 02:24:03 点击率:次标签栏是一个非常常见的控件,似乎也是一个比较简单的控件,但如果在标签下方加个下划线的话,就还是可以玩出挺多花来的。
网易严选的标签栏就做的很不错,里面隐藏着诸多细节:
- 手动滑动页面,下划线会跟着滑动。
- 选择一个标签后,下划线会有滑动过去的动画。
- 选择最左端或最右端的标签,标签栏会进行滑动,使得标签向中间靠拢(如果可以滑的话)。
仔细分析下,需要在简单标签栏的基础上实现以下逻辑:
- 画出下划线。
- 监听手动滑动页面事件,实时更新下划线位置。
- 切换标签时,开始下划线滑动的动画,并判断是否要同时滑动标签栏。
我做了一个样例程序,其中的较难点在于计算下划线的位置,和下划线的动画效果。
// 根据当前选定的tab,得到indicator应该移动到的位置
private Pair<Float, Float> getIndicatorTargetLeftRight(int position, float positionOffset) {
View tab = tabsContainer.getChildAt(position);
Pair<Float, Float> indicator = getIndicatorLeftRight(tab);
float targetLeft = indicator.first;
float targetRight = indicator.second;
// 如果positionOffset不为0,indicator正处于两个tab之间,需进行加权计算得到它的位置
if (positionOffset > 0f && position < tabCount - 1) {
View nextTab = tabsContainer.getChildAt(position + 1);
Pair<Float, Float> indicatorForNextTab = getIndicatorLeftRight(nextTab);
float left = indicatorForNextTab.first;
float right = indicatorForNextTab.second;
targetLeft = (positionOffset * left + (1f - positionOffset) * targetLeft);
targetRight = (positionOffset * right + (1f - positionOffset) * targetRight);
}
return new Pair<>(targetLeft, targetRight);
}
private Pair<Float, Float> getIndicatorLeftRight(View tab) {
float left = tab.getLeft();
float right = tab.getRight();
if (indicatorMode == IndicatorMode.WRAP && tab instanceof TextView) {
TextView tabTextView = (TextView) tab;
paint.setTextSize(tabTextView.getTextSize());
float textLength = paint.measureText(tabTextView.getText().toString());
float middle = (left + right) / 2f;
left = middle - textLength / 2f;
right = middle + textLength / 2f;
}
return new Pair<>(left, right);
}
上面是计算下划线位置的代码,通过传入在onPageScrolled()中获得的position和positionOffset,计算下划线是在某一个标签下,或者某两个标签之间的位置。需要注意的是,由于各标签的长度可能不一,所以下划线的长度在滑动中也可能发生变化,所以需分别计算下划线的left和right。
private boolean isAnimateRunning = false;
private static final String TARGET_LEFT = "targetLeft";
private static final String TARGET_RIGHT = "targetRight";
private void startIndicatorAnimate(final float targetLeft, final float targetRight) {
// 在indicator超出屏幕范围时,让其从屏幕边界处开始移动
float move = 0;
if (indicatorCurrentRight < getScrollX()) {
move = getScrollX() - indicatorCurrentRight;
} else if (indicatorCurrentLeft > getScrollX() + DimenUtil.getScreenWidth(getContext())) {
move = getScrollX() + DimenUtil.getScreenWidth(getContext()) - indicatorCurrentLeft;
}
indicatorCurrentLeft += move;
indicatorCurrentRight += move;
PropertyValuesHolder valuesHolderLeft = PropertyValuesHolder.ofFloat(
TARGET_LEFT, indicatorCurrentLeft, targetLeft);
PropertyValuesHolder valuesHolderRight = PropertyValuesHolder.ofFloat(
TARGET_RIGHT, indicatorCurrentRight, targetRight);
ValueAnimator animator = ValueAnimator.ofPropertyValuesHolder(valuesHolderLeft, valuesHolderRight)
.setDuration(SCROLL_DURATION);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
if (indicatorCurrentLeft != targetLeft) {
indicatorCurrentLeft = (float) animation.getAnimatedValue(TARGET_LEFT);
}
if (indicatorCurrentRight != targetRight) {
indicatorCurrentRight = (float) animation.getAnimatedValue(TARGET_RIGHT);
}
if (indicatorCurrentLeft == targetLeft && indicatorCurrentRight == targetRight) {
isAnimateRunning = false;
}
invalidate();
}
});
animator.start();
isAnimateRunning = true;
}
这是切换标签时下划线运行滑动动画的代码,使用ValueAnimator实现,并且对下划线超出边界的情况做了特殊处理,以防止滑动距离过大时,滑动速度过快。
更多的细节,请见https://github.com/wlkdb/page_sliding
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# Android
# 标签栏
# 滑动
# Android Navigation TabBar控件实现多彩标签栏
# android配合viewpager实现可滑动的标签栏示例分享
# Android View教程之自定义验证码输入框效果
# Android自定义View实现课程表表格
# Android自定义view实现圆的扩散效果
# Android自定义view实现标签栏功能(只支持固定两个标签)
# 下划线
# 的是
# 这是
# 左端
# 是在
# 会有
# 基础上
# 很不错
# 网易
# 不为
# 中也
# 过大
# 过快
# 正处于
# 是一个非常
# 时下
# 画出
# 就做
# 需要注意
# 让其
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
JavaScript模板引擎Template.js使用详解
零基础网站服务器架设实战:轻量应用与域名解析配置指南
谷歌Google入口永久地址_Google搜索引擎官网首页永久入口
laravel怎么为API路由添加签名中间件保护_laravel API路由签名中间件保护方法
浅谈redis在项目中的应用
Laravel任务队列怎么用_Laravel Queues异步处理任务提升应用性能
Android自定义控件实现温度旋转按钮效果
Laravel如何记录日志_Laravel Logging系统配置与自定义日志通道
如何在沈阳梯子盘古建站优化SEO排名与功能模块?
Laravel怎么实现观察者模式Observer_Laravel模型事件监听与解耦开发【指南】
b2c电商网站制作流程,b2c水平综合的电商平台?
浅析上传头像示例及其注意事项
实现点击下箭头变上箭头来回切换的两种方法【推荐】
Laravel Facade的原理是什么_深入理解Laravel门面及其工作机制
Laravel如何处理文件上传_Laravel Storage门面实现文件存储与管理
如何撰写建站申请书?关键要点有哪些?
手机网站制作平台,手机靓号代理商怎么制作属于自己的手机靓号网站?
悟空浏览器如何设置小说背景色_悟空浏览器背景色设置【方法】
如何快速选择适合个人网站的云服务器配置?
Laravel用户密码怎么加密_Laravel Hash门面使用教程
HTML 中如何正确使用模板变量为元素的 name 属性赋值
阿里云网站搭建费用解析:服务器价格与建站成本优化指南
如何在阿里云虚拟机上搭建网站?步骤解析与避坑指南
Win11怎么开启自动HDR画质_Windows11显示设置HDR选项
微信小程序 require机制详解及实例代码
佛山企业网站制作公司有哪些,沟通100网上服务官网?
Laravel如何实现数据库事务?(DB Facade示例)
如何选择可靠的免备案建站服务器?
laravel怎么在请求结束后执行任务(Terminable Middleware)_laravel Terminable Middleware请求结束任务执行方法
Laravel如何使用Spatie Media Library_Laravel图片上传管理与缩略图生成【步骤】
如何用5美元大硬盘VPS安全高效搭建个人网站?
Laravel怎么定时执行任务_Laravel任务调度器Schedule配置与Cron设置【教程】
如何在服务器上三步完成建站并提升流量?
Laravel如何与Pusher实现实时通信?(WebSocket示例)
JavaScript如何实现路由_前端路由原理是什么
香港服务器选型指南:免备案配置与高效建站方案解析
Laravel如何使用软删除(Soft Deletes)功能_Eloquent软删除与数据恢复方法
html5源代码发行怎么设置权限_访问权限控制方法与实践【指南】
如何获取上海专业网站定制建站电话?
如何在阿里云高效完成企业建站全流程?
Laravel如何使用缓存系统提升性能_Laravel缓存驱动和应用优化方案
如何用IIS7快速搭建并优化网站站点?
Laravel API路由如何设计_Laravel构建RESTful API的路由最佳实践
Laravel如何设置自定义的日志文件名_Laravel根据日期或用户ID生成动态日志【技巧】
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
Laravel如何实现API速率限制?(Rate Limiting教程)
Android okhttputils现在进度显示实例代码
Laravel Docker环境搭建教程_Laravel Sail使用指南
微信小程序 五星评分(包括半颗星评分)实例代码
香港网站服务器数量如何影响SEO优化效果?

