Android实现网易严选标签栏滑动效果
时间:2022-07-28
本文章向大家介绍Android实现网易严选标签栏滑动效果,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
标签栏是一个非常常见的控件,似乎也是一个比较简单的控件,但如果在标签下方加个下划线的话,就还是可以玩出挺多花来的。
网易严选的标签栏就做的很不错,里面隐藏着诸多细节:
- 手动滑动页面,下划线会跟着滑动。
- 选择一个标签后,下划线会有滑动过去的动画。
- 选择最左端或最右端的标签,标签栏会进行滑动,使得标签向中间靠拢(如果可以滑的话)。
仔细分析下,需要在简单标签栏的基础上实现以下逻辑:
- 画出下划线。
- 监听手动滑动页面事件,实时更新下划线位置。
- 切换标签时,开始下划线滑动的动画,并判断是否要同时滑动标签栏。
我做了一个样例程序,其中的较难点在于计算下划线的位置,和下划线的动画效果。
// 根据当前选定的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
以上就是本文的全部内容,希望对大家的学习有所帮助。
- 快速添加永久存储到到Minishift / CDK 3
- 张小龙发布2018微信全新计划(内附演讲全文)
- 使用JClouds在Java中获取和发布云服务器
- 利用ASP.NET SiteMap生成与Bootstrap"兼容"菜单
- 埃隆·马斯克强烈推荐的5本书,看完之后他开始改变世界
- 算法:AOE网(Activity On edge Network)与关键路径简介
- ASP.NET Core的配置(4):多样性的配置来源[中篇]
- ASP.NET MVC的Razor引擎:RazorViewEngine
- 算法:求解AOE网的关键路径
- 编程小技巧:多态原理
- ASP.NET Core的配置(4):多样性的配置来源[下篇]
- 数据结构 图
- ASP.NET Core的配置(5):配置的同步[ 实例篇]
- TensorFlow 深度学习笔记 卷积神经网络
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- redis主从配置+集群搭建
- 【Ceph RGW】找一个用Go写的s3cmd
- 技术分享 | 如何优雅地在 Windows 上从 MySQL 5.6 升级到 5.7
- SpringCloud分布式配置中心
- Sharding-Proxy的基本功能使用
- React-Redux 对Todolist修改
- 快排解决寻找数组中的第K个最大元素
- Docker六脉神剑(一) Mac极速体验
- React-Router 5.0 制作导航栏+页面参数传递
- Vue3.0快速入门(速查)
- 憧憬博客Nginx到Tengine的迁移
- SpringCloud微服务构建浅析
- 宋宝华:Linux设备与驱动的手动解绑与手动绑定
- ELK7.x日志系统搭建 1. elk基础搭建
- 腾讯云直播开发日记 (二)附近直播-直播礼物-直播回放