SlidingScaleTabLayout(2):解决标题文字变化抖动的问题

前言

之前我在FlycoTabLayout的基础上写了一个SlidingScaleTabLayout,实现ViewPager切换Tab标题文字大小变化的效果:


在这里插入图片描述

可能是因为我的项目标题比较短,所以当时没有发现文字抖动的问题,在github开源后,有很多的小伙伴跟我反馈这个问题,由于已经离职加入到新的项目中,一拖再拖到今天,真的非常抱歉。

如果你还不了解SlidingScaleTabLayout,可以先阅读:

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

如果你想先了解优化后的用法,可以先查看github地址:

查看FlycoTabLayoutZ新用法

正文

文字抖动的原因

首先我们要知道为什么会出现这个抖动的问题。经过我的研究,发现问题出现在字体上,以下面为例:


示例1

原谅我的ps水平也就这个程度了。我们模拟一个文字变化的效果:

未选中文字大小为13sp,选中的文字大小为17sp;
当切换到50%时,文字应当都为15sp;

从上图可以看到,宽度并没有对齐,说明文字的大小变化并不是等比的,所以在文字大小频繁切换的时候,就出现了抖动的问题,且文字变化幅度越大,问题就越明显。

解决文字抖动的问题

我们已经知道了原因,就可以找到解决问题的办法。在Android上,显示元素主要就只有两种:文字和图片。既然文字我们无法控制,唯一的解决办法就是用图片。

解决的思路大体为:

1、在设置标题文字后,对于TextView生成图片副本;
2、隐藏标题文字,显示图片副本;
3、修改变化文字大小,变为图片大小;
4、如果选中的文字和未选中的文字样式不同(加粗,颜色),需要在选中后,刷新图片副本;

第一步:在设置标题文字后,对于TextView生成图片副本

为了尽量保证不失真,我们选择最大的文字大小生成副本:

 textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Math.max(mTextSelectSize, mTextUnSelectSize));

然后对显示标题的TextView,生成图片副本:

public static Bitmap generateViewCacheBitmap(View view) {
        view.destroyDrawingCache();
        int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
        view.measure(widthMeasureSpec, heightMeasureSpec);
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
        view.layout(0, 0, width, height);
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        // 请注意,必须要生成新的Bitmap
        // ImageView内部有对DrawingCache回收的机制
        return Bitmap.createBitmap(view.getDrawingCache());
}

第二步:隐藏标题文字,显示图片副本

// 显示图片副本
imageView.setImageBitmap(ViewUtils.generateViewCacheBitmap(textView));
// 保存最大宽度,主要是为了ViewPager滑动的时候,根据position计算显示的宽度
imageView.setMaxWidth(imageView.getDrawable().getIntrinsicWidth());
// 隐藏标题
textView.setVisibility(View.GONE);

第三步:修改变化文字大小,变为图片大小

之前改变文字大小的功能写在了TabScaleTransformer中:

private void changeTextSize(final TextView textView, final float position) {
        // 字体大小相同,不需要切换
        if (textSelectSize == textUnSelectSize) return;
        // 必须要在View调用post更新样式,否则可能无效
        textView.post(new Runnable() {
            @Override
            public void run() {
                if (position >= -1 && position <= 1) { // [-1,1]
                    if (textSelectSize > textUnSelectSize) {
                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSelectSize - Math.abs((textSelectSize - textUnSelectSize) * position));
                    } else {
                        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSelectSize + Math.abs((textUnSelectSize - textSelectSize) * position));
                    }
                } else {
                    textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textUnSelectSize);
                }
            }
        });
    }

我们修改为改变图片副本的大小:

private void changeDmgSize(ImageView imageView, float position) {
        // 字体大小相同,不需要切换
        if (textSelectSize == textUnSelectSize) return;
        ViewGroup.LayoutParams params = imageView.getLayoutParams();
        if (position >= -1 && position <= 1) { // [-1,1]
            if (textSelectSize > textUnSelectSize) {
                float scale = 1 - Math.abs((1 - minScale) * position);
                params.width = (int) (imageView.getMaxWidth() * scale);
            } else {
                float scale = minScale + Math.abs((1 - minScale) * position);
                params.width = (int) (imageView.getMaxWidth() * scale);
            }
            imageView.setLayoutParams(params);
        } else {
            int width;
            if (textSelectSize > textUnSelectSize) {
                width = (int) (imageView.getMaxWidth() * minScale);
            } else {
                width = imageView.getMaxWidth();
            }
            if (width != params.width) {
                params.width = width;
                imageView.setLayoutParams(params);
            }
        }
    }

上面的代码都是计算规则,就没什么可说的了。

第四步:文字样式(加粗,颜色),在选中后,刷新图片副本

这一步和第一步几乎是一样的,只不过我们要加一些判断,防止无用的刷新:

// 如果选中的文字颜色和未选中的文字颜色不同,需要刷新
// 如果选中的是粗体,未选中是普通,也要刷新副本
if ((mTextSelectColor != mTextUnSelectColor || mTextBold == TEXT_BOLD_WHEN_SELECT)) {
      tab_title.setVisibility(View.VISIBLE);
      generateTitleDmg(tabView, tab_title);
}

第五步:一点点优化

如果你觉得开启副本的效果没有太大的变化,或者目前已经满足了你的需求,我建议关闭图片副本这个功能,毕竟文字的开销要比图片开销要小得多。

// 新增自定义属性
<!-- 是否开启文字的图片镜像 -->
<attr name="tl_openTextDmg" format="boolean"/>

另外如果选中的文字和未选中的文字大小都是一样的,也没有必要开启图片副本,所以我增加了一些判断:

/**
* 如果文字的大小没有变化,不需要开启镜像,请注意
*/
private boolean isDmgOpen() {
    return openDmg && mTextSelectSize != mTextUnSelectSize;
}

效果图对比

未开启图片副本:


未开启图片副本

开启图片副本:


开启图片副本

总结

以上就是解决文字变化抖动的解决方案,如果在使用中大家遇到了哪些问题,希望大家继续反馈,感谢大家的支持。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容