仿陌陌选项卡:文字大小变化的SlidingScaleTabLayout

2024-06-04 16:48

本文主要是介绍仿陌陌选项卡:文字大小变化的SlidingScaleTabLayout,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

不知不觉博客又一个月没有更新了。最近在看flutter,本来想写一篇flutter相关的内容,仔细想想又觉得内容太多,等年后回来再写一个系列吧。临近年底,在大家工作热情逐渐消退的气氛中,我们聊一点简单的。

最近公司发布了新版本的UI,其中一个效果是模仿陌陌的:
在这里插入图片描述
首先我们简单分析一下效果:

  • 被选中的Tab文字大,其他Tab文字小
  • 被选中的字体加粗

其他的效果就先忽略了,因为我一直使用的是FlycoTabLayout这个框架,动画效果已经满足了,唯一欠缺的就是这个文字大小切换的效果。

FlycoTabLayout框架github地址

正文

有了之前的分析,我们开始做准备工作:

效果1:随着ViewPager的滑动,文字大小也随之变化

这个效果非常简单,ViewPager自带Transform监听:

/*** Created by li.zhipeng on 2019/1/3.* <p>* tab文字大小切换的动画类*/
public class TabScaleTransformer implements ViewPager.PageTransformer {private SlidingScaleTabLayout slidingScaleTabLayout;private PagerAdapter pagerAdapter;private List<IViewPagerTransformer> transformers = null;public TabScaleTransformer(SlidingScaleTabLayout slidingScaleTabLayout, PagerAdapter pagerAdapter) {this.slidingScaleTabLayout = slidingScaleTabLayout;this.pagerAdapter = pagerAdapter;}@Overridepublic void transformPage(@NonNull View view, final float position) {// position的值区间[-1 ,1]// [-1, 0] 表示在左边的选项卡滑动到中心的比例// [0, 1] 表示在右边的选项卡滑动到中心的比例}public List<IViewPagerTransformer> getTransformers() {return transformers;}public void setTransformers(List<IViewPagerTransformer> transformers) {this.transformers = transformers;}
}
// 设置ViewPager的滑动动画
viewPager.setPageTransformer(true, new TabScaleTransformer())

position的含义已经在注释中说明了,因为在transformPage回调中我们得不到具体的位置信息,所以我们只能通过参数view和PagerAdapter,得到Tab的position。PagerAdapter已经有写好的方法供我们调用,那就是:

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {TextView textView = new TextView(SlidingScaleTabLayoutActivity.this);textView.setBackgroundColor(colors[position]);textView.setText(getPageTitle(position));// 设置位置TagtextView.setTag(position);container.addView(textView);return textView;
}@Override
public int getItemPosition(@NonNull Object object) {View view = (View) object;// 获取位置tagreturn (int) view.getTag();
}

其中参数object是一个View类型的对象,就算我们使用的FragmentPagerAdapter,仍然是View类型(Fragment的布局),而不是Fragment。所以我的解决方案是,在添加的View中设置position的Tag,然后通过getItemPosition返回view的Tag即可。

最后我们还得弄清楚setPageTransformer方法的每个参数的作用:

setPageTransformer最多有三个参数:

boolean reverseDrawingOrder:
如果是true,绘制的顺序是从后向前,也就是前面的View会覆盖后面的View。如果是false,绘制的顺序会相反。

PageTransformer transformer:滑动的效果实现类

int pageLayerType:默认开启硬件加速,如果我们的View中包含了SurefaceView,并且没有设置setZOrderOnTop,但是SurfaceView仍然后显示在最上面,所以为了解决这个问题,需要设置View.LAYER_TYPE_NONE。

效果2:文字大小变化效果

说到大小的变化,第一反应肯定是使用Scale:

Scale最大的特点是不用重新测量View,相对于其他方式,效果更高效流畅。

但是很遗憾,这种方案最终还是失败了,直接把遇到的坑分享给大家:

  1. 因为Scale不会重新测量,导致Tab之间的间距不正确。
  2. 缩放需要设置缩放的中心点,通过transformPage方法无法知道当前哪一个是被选中的,除非再引用ViewPager,实现起来相对复杂。

最终还是采用了土办法,直接设置TextSize,肯定没跑了:

@Overridepublic void transformPage(@NonNull View view, final float position) {final TextView currentTab = slidingScaleTabLayout.getTitle(pagerAdapter.getItemPosition(view));if (currentTab == null) {return;}// 必须要在View调用post更新样式,否则可能无效currentTab.post(new Runnable() {@Overridepublic void run() {if (position >= -1 && position <= 1) { // [-1,1]currentTab.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSelectSize - Math.abs((textSelectSize - textUnSelectSize) * position));} else {currentTab.setTextSize(TypedValue.COMPLEX_UNIT_PX, textUnSelectSize);}}});// 回调设置的页面切换效果设置if (transformers != null && transformers.size() > 0) {for (IViewPagerTransformer transformer : transformers) {transformer.transformPage(view, position);}}}

以上就是实现文字大小变化的代码,其中我们调用了View.post设置选中的状态,是因为首次进入页面,我们设置显示的View位置不是0的话,会导致设置的文字大小没有变化,所以通过View.post方法,让View在测量结束后,再次设置文字大小。

然后因为我们占用了viewPager.setPageTransformer这个方法, 所以最好扩展一个API,供使用者设置滑动效果。最后就是改造我们的SlidingScaleTabLayout。

改造SlidingTabLayout

虽然我们可以通过之前的方式实现大部分的效果,但是还得修改SlidingTabLayout的代码,例如SlidingTabLayout中的文字是居中的,但是我们需要的是靠近底部的。所以不如在SlidingTabLayout的基础上,改造出我们的新类:SlidingScaleTabLayout。

1、新增自定义属性

<!-- 选中的文字大小 -->
<attr name="tl_textSelectSize" />
<!-- 未选中的文字大小 -->
<attr name="tl_textUnSelectSize" />
<!-- 上边距 -->
<attr name="tl_tab_marginTop" />
<!-- 下边距 -->
<attr name="tl_tab_marginBottom" />
<!-- tab的位置 -->
<attr name="tl_tab_gravity" format="enum"><enum name="Top" value="0" /><enum name="Bottom" value="1" /><enum name="Center" value="2" />
</attr>

通过代码获取自定义属性:

mTextUnSelectSize = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_textUnSelectSize, sp2px(14));
// 被选中的文字大小,默认额未选中的大小一样
mTextSelectSize = ta.getDimension(R.styleable.SlidingScaleTabLayout_tl_textSelectSize, mTextUnSelectSize);// 得到设置的上下间距和gravity
mTabMarginTop = ta.getDimensionPixelSize(R.styleable.SlidingScaleTabLayout_tl_tab_marginTop, 0);
mTabMarginBottom = ta.getDimensionPixelSize(R.styleable.SlidingScaleTabLayout_tl_tab_marginBottom, 0);
mTabGravity = ta.getInt(R.styleable.SlidingScaleTabLayout_tl_tab_gravity, CENTER);

找到设置Tab参数的方法,把设置的自定义属性添加进入

// 找到所有设置Tab文字大小的地方,修改文字大小的设置
tv_tab_title.setTextSize(TypedValue.COMPLEX_UNIT_PX, position == mCurrentTab ? mTextSelectSize : mTextUnSelectSize);// 找到生成LayoutParams的方法,设置Gravity,marginTop,marginBottom
private void setTabLayoutParams(TextView title) {RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) title.getLayoutParams();params.topMargin = mTabMarginTop;params.bottomMargin = mTabMarginBottom;if (mTabGravity == TOP) {params.addRule(RelativeLayout.ALIGN_PARENT_TOP);} else if (mTabGravity == BOTTOM) {params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);} else {params.addRule(RelativeLayout.CENTER_VERTICAL);}title.setLayoutParams(params);}

设置TabScaleTransformer

SlidingScaleTabLayout有多个setViewPager方法,把我们封装的设置TabScaleTransformer的方法,添加进去:

private void initTransformer() {// 如果选中状态的文字大小和未选中状态的文字大小是不同的,开启缩放if (mTextUnSelectSize != mTextSelectSize) {defaultTransformer = new TabScaleTransformer(this, mViewPager.getAdapter(), mTextSelectSize, mTextUnSelectSize);this.mViewPager.setPageTransformer(true, defaultTransformer);}}// 新增设置Transformer的api
public void setTransformers(List<IViewPagerTransformer> transformers) {this.defaultTransformer.setTransformers(transformers);
}public List<IViewPagerTransformer> getTransformers() {return defaultTransformer.getTransformers();
}   

赶紧看一下我们的效果:
在这里插入图片描述

总结

能看到这里的绝对是真爱了,我已经把代码开源在github上方便大家使用,有什么问题欢迎留言。

FlycoTabLayoutZ github地址

补充

很多朋友都咨询了相同的问题,在这里补充一下:

问题1:怎么和Fragment一起配合适用?
我刚刚更新了Demo,提供一种可参考的思路,主要是在Fragment的根布局下添加位置Tag,然后在从getItemPosition取出来。

问题2:文字切换抖的厉害,有什么办法解决吗?
github已经更新,具体原因和解决方案可以查看
SlidingScaleTabLayout(2):解决标题文字变化抖动的问题

这篇关于仿陌陌选项卡:文字大小变化的SlidingScaleTabLayout的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1030566

相关文章

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

html css jquery选项卡 代码练习小项目

在学习 html 和 css jquery 结合使用的时候 做好是能尝试做一些简单的小功能,来提高自己的 逻辑能力,熟悉代码的编写语法 下面分享一段代码 使用html css jquery选项卡 代码练习 <div class="box"><dl class="tab"><dd class="active">手机</dd><dd>家电</dd><dd>服装</dd><dd>数码</dd><dd

c++的静态变化!

静态成员   对于非静态成员,一个类的每个对象都自己存有一个副本,每个对象根据自己拥有的非静态的数据成员来区别于其他对象。而静态成员则解决了同一个类的多个对象之间数据和函数的共享问题。   静态数据成员   静态数据成员的作用是:实现同一类的不同对象之间的数据共享。   #include<IOSTREAM>   using namespace std;   class Po

选项卡制作问题--折磨了我一整天,记录下来

看老曹的html+css课程,学习html+css基础,讲到制作选项卡,以京东商城的选项卡为例,效果如下: 看着他做出来很简单,结果自己做花了2个多小时才有个样子,效果是这样的: 内部代码如下 <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-

2409atl,atl3.0到7.0的变化

原文 本文不介绍新的ATL7服务器类和属性.这不是一个完整的列表,只是我目前找到的. 串 串转换宏有一些限制.它从栈中分配内存,可能会在大串时溢出栈.函数退出时,释放该串,而不是在函数内引入的域内释放. 它使用过时OLE2ANSI定义.如果你查看MSDN上对_alloca(来转换)的注意,它说它在SEH或C++EH中使用时有限制. 如,看一下ATL3的串转换宏: USES_CONVER

包拯断案 | 数据库从库GTID在变化 为何没有数据写入@还故障一个真相

提问:作为DBA运维的你是否遇到过这些烦恼 1、数据库从库复制链路如何正确配置表过滤信息? 2、数据库从库的GTID在变化,实际却没有数据写入,究竟是什么原因? 心中有章,遇事不慌 作为DBA的你,遇到问题无从下手,除了在问题面前徘徊,还能如何选择?如果你一次或多次遇到该问题还是 无法解决,又很懊恼,该如何排忧呢?关注公众号,关注《包拯断案》专栏,让小编为你排忧解难~ #包拯秘籍#

2020年SEO行业发展变化和趋势分析!

一、搜索引擎算法发展轨迹 第一阶段:人工目录(1997年-2001年“雅虎早期搜索模式”); 第二阶段:文本分析(2001年-2004年“以关键词和背景颜色一样,堆积大量关键词,就会有非常好的排名; 第三阶段:链接分析(2004年-2009年“以反向链接为核心算法的阶段”),这时行业内有句话是内容为王,外链为皇; 第四阶段:智能分析(2009年-现在“以满足用户人性化需求的用户浏览行为分析

保姆级教学:OC监听网络状态变化 Reachability监听网络变化 ios网络变化

本文主要讲解了,在oc开发中,怎么去使用代码进行网络监听,十分的通俗易懂。 首先,在xcode工程中导入SystemConfiguration框架。 然后导入Reachability.h文件。 Reachability文件 点击下载,也可以按照如下内容创建对应的文件。 Reachability.m //文件名Reachability.m/*Copyright (C) 2016 App

Vue2升级Vue3 --- 第三方库变化(基于gogocode)

本次升级基于gogocode快捷转换gogocode升级体验,汇总的部分问题可能与gogocode有关,但仍可作为vue2升级vue3过程中报错问题的解决参考 第三方库变化 1.vue-contextmenujs(右键菜单) // 原第三方库不支持vue3,更换为支持vue3的库"@imengyu/vue3-context-menu": "^1.0.8"// 更换库后组件样式与以前不

有temp表包含A,B两列,使用SQL,对B列进行处理,形成C列,按A列顺序,B列值不变,则C列累计技术,B列值变化,则C列重新开始计数

有temp表,使用SQL,对B列进行处理,形成C列,按A列顺序,B列值不变,则C列累计技术,B列值变化,则C列重新开始计数 建表语句如下 CREATE TABLE temp(A STRING ,B STRING );INSERT INTO TABLE temp VALUES('2010','1'),('2011','1'),('2012','1'),('2013','0'),('2014',