美文网首页Android开发技术分享Android开发Android开发
SlidingScaleTabLayout(2):解决标题文字变

SlidingScaleTabLayout(2):解决标题文字变

作者: 珠穆朗玛小王子 | 来源:发表于2020-01-09 10:52 被阅读0次

    前言

    之前我在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;
    }
    

    效果图对比

    未开启图片副本:


    未开启图片副本

    开启图片副本:


    开启图片副本

    总结

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

    相关文章

      网友评论

        本文标题:SlidingScaleTabLayout(2):解决标题文字变

        本文链接:https://www.haomeiwen.com/subject/yocyactx.html