IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 自动改变(文字大小)TextSize的TextView——autoSizeTextType -> 正文阅读

[移动开发]自动改变(文字大小)TextSize的TextView——autoSizeTextType

今天要实现一个自动改变TextSize的TextView,以下是我的艰辛历程,不感兴趣的直接跳到结尾获取最终答案

思路: 我的计划是在onSizeChange中获取TextView的宽高,计算当前TextSize占用的高度,再不断减小文字大小直到TextView可以放下,然后将TextSize设置给TextView

说干就干首先我继承了一个textView,然后通过简单的百度我知道了如下API

/**
     * Return the width of the text.
     *
     * @param text  The text to measure. Cannot be null.
     * @return      The width of the text
     */
    public float measureText(String text) {}

/**
     * Return the recommend line spacing based on the current typeface and
     * text size.
     *
     * <p>Note that this is the value for the main typeface, and actual text rendered may need a
     * larger value because fallback fonts may get used in rendering the text.
     *
     * @return  recommend line spacing based on the current typeface and
     *          text size.
     */
    public float getFontSpacing() {
        return getFontMetrics(null);
    }

说人话就是measureText可以测量文本的宽度,getFontSpacing可以获取推荐的行距

然后就有了下面"优雅的"代码

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        String text = getText().toString();
        setTextSize(getAutoTextSize(w,h,getTextSize(),text));
    }

    private float getAutoTextSize(int w, int h, float textSize,String text) {
        Paint paint = new Paint();
        paint.setTextSize(textSize);
        float textW = paint.measureText(text);
        float fontSpacing = paint.getFontSpacing();

        if ((textW / w) > h / fontSpacing) {
            return getAutoTextSize(w, h, --textSize, text);
        } else {
            return textSize;
        }
    }

正当我以为万事大吉的时候,真实的显示情况是這樣的

我来来回回的检查代码,最后在setTextSize的注释找到答案

/**
     * Set the default text size to the given value, interpreted as "scaled
     * pixel" units.  This size is adjusted based on the current density and
     * user font size preference.
     *
     * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
     *
     * @param size The scaled pixel size.
     *
     * @attr ref android.R.styleable#TextView_textSize
     */
    @android.view.RemotableViewMethod
    public void setTextSize(float size) {
        setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
    }

说人话就是android老哥贴心的为我们调用了另一个重载方法并且传递了一个TypedValue,TypedValue是什么呢,下面的注释给了答案

/**
     * Set the default text size to a given unit and value. See {@link
     * TypedValue} for the possible dimension units.
     *
     * <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
     *
     * @param unit The desired dimension unit.
     * @param size The desired size in the given units.
     *
     * @attr ref android.R.styleable#TextView_textSize
     */
    public void setTextSize(int unit, float size) {
        if (!isAutoSizeEnabled()) {
            setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
        }
    }

这里的还给了一个贴心的提示:"注意:如果此 TextView 启用了自动调整大小功能,则此功能无操作。"这个贴士很重要,后面会提到

TypedValue被称作维度单位,说白了就是一个单位.直接调用setTextSize方法的话,默认的单位是sp,那我改一下调用的方法总好了吧

我天,文字小是小了,可是并没有显示全,这时候仔细回想一下我的思路就会发现,这思路太理想化了,我们通过measureText获取的值是一行展示的时候的宽度,然而实际展示的时候TextView会在行末根据最后的字符占用宽度进行判断和换行,所以就很容易出现图中的这种情况.

这种时候作为一个优秀的搬砖工,当然是去找找看有没有轮子-----借鉴一下思路咯

然后惊喜的发现了textView的属性 autoSizeTextType , 原来android已经贴心的增加了这样的API

感觉又相信爱了

不过autoSizeTextType的使用还是有一些需要注意的点哦

1. 在Android 8.0 (API level 26) 以上,才可以使用这个属性哦,不过通过万能的拓展库可以最低支持到Android 4.0(API Level 14)及以上的系统(不过你的应用编译的targetSDKVersion必须在26及以上)。

2. 还记得上面setTextSize的注释吗,里面有一个小贴士哦,如果设置了自动大小功能,setTextSize将不起作用了,textSize的大小会根据textView的宽高自动适配

3. 在使用autoSizeTextType属性的时候宽高属性最好是固定的参数,或者设置maxHeightmaxWidth限制一下控件的宽高

4. 如果只设置了autoSizeTextType属性的话,默认的textSize是12sp~112sp,粒度为1px

知其然也要知其所以然,来看一下他是怎么实现的吧,很简单的哦,你忍一下

/**
     * Performs a binary search to find the largest text size that will still fit within the size
     * available to this view.
     */
    private int findLargestTextSizeWhichFits(RectF availableSpace) {
        final int sizesCount = mAutoSizeTextSizesInPx.length;
        if (sizesCount == 0) {
            throw new IllegalStateException("No available text sizes to choose from.");
        }

        int bestSizeIndex = 0;
        int lowIndex = bestSizeIndex + 1;
        int highIndex = sizesCount - 1;
        int sizeToTryIndex;
        while (lowIndex <= highIndex) {
            sizeToTryIndex = (lowIndex + highIndex) / 2;
            if (suggestedSizeFitsInSpace(mAutoSizeTextSizesInPx[sizeToTryIndex], availableSpace)) {
                bestSizeIndex = lowIndex;
                lowIndex = sizeToTryIndex + 1;
            } else {
                highIndex = sizeToTryIndex - 1;
                bestSizeIndex = highIndex;
            }
        }

        return mAutoSizeTextSizesInPx[bestSizeIndex];
    }

    private boolean suggestedSizeFitsInSpace(int suggestedSizeInPx, RectF availableSpace) {
        final CharSequence text = mTransformed != null
                ? mTransformed
                : getText();
        final int maxLines = getMaxLines();
        if (mTempTextPaint == null) {
            mTempTextPaint = new TextPaint();
        } else {
            mTempTextPaint.reset();
        }
        mTempTextPaint.set(getPaint());
        mTempTextPaint.setTextSize(suggestedSizeInPx);

        final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
                text, 0, text.length(),  mTempTextPaint, Math.round(availableSpace.right));

        layoutBuilder.setAlignment(getLayoutAlignment())
                .setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
                .setIncludePad(getIncludeFontPadding())
                .setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
                .setBreakStrategy(getBreakStrategy())
                .setHyphenationFrequency(getHyphenationFrequency())
                .setJustificationMode(getJustificationMode())
                .setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
                .setTextDirection(getTextDirectionHeuristic());

        final StaticLayout layout = layoutBuilder.build();

        // Lines overflow.
        if (maxLines != -1 && layout.getLineCount() > maxLines) {
            return false;
        }

        // Height overflow.
        if (layout.getHeight() > availableSpace.bottom) {
            return false;
        }

        return true;
    }

可以看到findLargestTextSizeWhichFits方法就是计算TextSize的方法了,大神的代码就是优雅呢,使用了二分算法来找最合适的size

最重要的来咯, suggestedSizeFitsInSpace方法,他是用来判断当前testSize是否可以在textView中完美展示的

可以看到大神使用了StaticLayout,通过StaticLayout的getHeight()方法获取了当前testSize下占用的高度然后和展示区域的底部进行比较

ps: 还不了解StaticLayout的小伙伴,只要知道他是一个继承自Layout的可以实现自动换行的文本的布局

总结: 通过上面的源码学习可以发现,自由缩放功能是使用StaticLayout来计算占用的高度,然后不断的放大或缩小TextSize来实现的.android的源码中真是藏着很多的宝藏呢.

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-28 09:27:50  更:2021-08-28 09:29:36 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 13:41:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码