The road to Android advancement - EditText input font adaptation

Encountered such a requirement: "The width of the control is limited, and the font size is dynamically modified according to the input content", if it is you, how to achieve it? How many ways are there?

Well, it is such a simple requirement that I recorded two blogs
insert image description here

At first I tried to achieve it by 监听TextChanged+ font adaptation输入字体自适应 , but the effect was not satisfactory, so I finally changed to another way

This article uses several methods I have used to see if I can help you. The following methods are used in detail. Let me briefly introduce them first.

  • 监听TextChanged, set the font size directly within certain rules (the font is too unnatural)
  • AutoAdjustSizeEditText custom control (basically meets the scene, but a single line can support wireless input and infinite sliding)
  • The AutoAdaptSizeEditText control is borrowed from the former and slightly modified to meet a certain scene (set a single-line scene, if it exceeds a fixed width, you cannot continue to input)

AutoAdjustSizeEditText、AutoAdaptSizeEditTextImporting Effects and Layouts

Effect

Please add a picture description

Layout import

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <com.example.edittextdemo.AutoAdjustSizeEditText
        android:layout_width="230dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:hint="AutoAdjustSizeEditText"
        android:textSize="20sp"
        app:maxTextSize="30sp"
        app:minTextSize="15sp" />

    <com.example.edittextdemo.AutoAdaptSizeEditText
        android:layout_width="230dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:hint="AutoAdaptSizeEditText"
        android:singleLine="true"
        android:textSize="20sp"
        app:maxSize="30sp"
        app:minSize="15sp" />
</androidx.appcompat.widget.LinearLayoutCompat>

Simple, direct, a little low

This method is very simple to use as I said, the only disadvantage may be 首先需要了解输入规则的要求,同时字体适应时会生硬一些(currently because of the framework, I use this method first)

Because of the framework, I will directly provide it 伪代码for your reference.

EditText -addTextChangedListener

insert image description here

Define the font size by yourself through rules

insert image description here

The extension function of the splitties frameworkEditText.setTextIfDifferent , which will set the focus position internally

insert image description here


AutoAdjustSizeEditText

I have read a lot of articles about EditText 输入字体自适应blogs, most of which seem to be born out of the early AutoAdjustSizeEditText custom control. After I ran the source code directly, I found that it can basically be applied to most scenarios. There are advantages and disadvantages (only in my opinion), but still It is undeniable that you can learn and grow from the code of the predecessors (to show respect, the source code will not be modified in any way)

Applicable to most scenarios, if there is a limit on the display length of a single line, it may not be satisfactory

Custom attributes (I haven't recorded related blogs about custom attributes before, and I must add one when I have time)

    <!-- 文本自动调整大小显示自定义属性 -->
    <declare-styleable name="AutoAdjustTextSize">
        <attr name="minTextSize" format="dimension" />
        <attr name="maxTextSize" format="dimension" />
    </declare-styleable>

AutoAdjustSizeEditText

package com.example.edittextdemo;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.widget.EditText;
import android.widget.TextView;


/**
 * 自动调整字体文本输入框
 * 
 * @author 蒋庆意
 * @date 2015-11-4
 * @time 上午11:02:32
 */
@SuppressLint("AppCompatCustomView")
public class AutoAdjustSizeEditText extends EditText {
    
    
    /**
     * 默认文字字体大小最小值(单位:像素)
     */
    private static final float DEFAULT_TEXT_SIZE_MIN = 20;

    /**
     * 默认文字字体大小最大值(单位:像素)(貌似用不上)
     */
    @SuppressWarnings("unused")
    private static final float DEFAULT_TEXT_SIZE_MAX = 60;

    /**
     * 画笔(用来测量已输入文字的长度)
     */
    private Paint paint;

    /**
     * 文字字体大小最小值
     */
    private float minTextSize = 0;

    /**
     * 文字字体大小最大值
     */
    private float maxTextSize = 0;

    /**
     * 判断输入文本字体是否变小过
     */
    private boolean hasScaleSmall = false;

    public AutoAdjustSizeEditTextBefore(Context context) {
    
    
        super(context);
        paint = new Paint();
    }

    public AutoAdjustSizeEditTextBefore(Context context, AttributeSet attrs) {
    
    
        super(context, attrs);
        paint = new Paint();
        //读取自定义属性, 获取设置的字体大小范围
        if (null != attrs) {
    
    
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.AutoAdjustTextSize);
            if (null != array) {
    
    
                minTextSize = array.getDimension(R.styleable.AutoAdjustTextSize_minTextSize, DEFAULT_TEXT_SIZE_MIN);
                //如果未设置字体最大值,则使用当前字体大小作为最大值
                maxTextSize = array.getDimension(R.styleable.AutoAdjustTextSize_maxTextSize, this.getTextSize());
                //回收 TypedArray
                array.recycle();
            }
        }
        //未设置字体最小值,则使用默认最小值
        if (0 == minTextSize) {
    
    
            minTextSize = DEFAULT_TEXT_SIZE_MIN;
        }
        //未设置字体最大值,则使用当前字体大小作为最大值
        if (0 == maxTextSize) {
    
    
            //            maxTextSize = DEFAULT_TEXT_SIZE_MAX;
            maxTextSize = this.getTextSize();
        }
        //如果设置的值不正确(例如minTextSize>maxTextSize),则互换
        if (minTextSize > maxTextSize) {
    
    
            float minSize = maxTextSize;
            maxTextSize = minTextSize;
            minTextSize = minSize;
        }
        Log.d("AutoScaleSizeEditText",
                "minTextSize=" + String.valueOf(minTextSize));
        Log.d("AutoScaleSizeEditText",
                "maxTextSize=" + String.valueOf(maxTextSize));
    }

    @Override
    protected void onTextChanged(CharSequence text, int start,
                                 int lengthBefore, int lengthAfter) {
    
    
        // 根据需要调整字体大小
        adjustTextSize(this);
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    
    
        // 根据需要调整字体大小
        if (w != oldw) {
    
    
            adjustTextSize(this);
        }
        super.onSizeChanged(w, h, oldw, oldh);
    }

    /**
     * 调整文本的显示
     */
    private void adjustTextSize(TextView textView) {
    
    
        if (null == textView) {
    
    
            //参数错误,不与处理
            return;
        }
        //已输入文本
        String text = textView.getText().toString();
        //已输入文本长度
        int textWidth = textView.getWidth();
        if (null == text || text.isEmpty() || textWidth <= 0) {
    
    
            return;
        }
        //获取输入框总的可输入的文本长度
        float maxInputWidth = textView.getWidth() - textView.getPaddingLeft() - textView.getPaddingRight();
        //获取当前文本字体大小
        float currentTextSize = textView.getTextSize();
        Log.d("AutoScaleSizeEditText","currentTextSize=" + String.valueOf(currentTextSize));
        //设置画笔的字体大小
        paint.setTextSize(currentTextSize);
        /*
         * 循环减小字体大小
         * 当  1、文本字体小于最大值
         *     2、可输入文本长度小于已输入文本长度
         * 时
         */
        while ((currentTextSize > minTextSize) && (maxInputWidth < paint.measureText(text))) {
    
    
            hasScaleSmall = true;
            Log.d("AutoScaleSizeEditText","TextSizeChange=" + String.valueOf(currentTextSize));
            --currentTextSize;
            if (currentTextSize < minTextSize) {
    
    
                currentTextSize = minTextSize;
                break;
            }
            //设置画笔字体大小
            paint.setTextSize(currentTextSize);
        }
        /*
         * 循环增大字体大小
         * 当  1、文本字体小于默认值
         *     2、可输入文本长度大于已输入文本长度
         * 时
         */
        while (hasScaleSmall && (currentTextSize < maxTextSize)
                && (maxInputWidth > paint.measureText(text))) {
    
    
            Log.d("AutoScaleSizeEditText",
                    "TextSizeChangeSmall=" + String.valueOf(currentTextSize));
            ++currentTextSize;
            if (currentTextSize > maxTextSize) {
    
    
                currentTextSize = maxTextSize;
                break;
            }
            //设置画笔字体大小
            paint.setTextSize(currentTextSize);
        }
        //设置文本字体(单位为像素px)
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, currentTextSize);
        Log.d("AutoScaleSizeEditText",
                "currentTextSize2=" + String.valueOf(currentTextSize));
    }
}

AutoAdaptSizeEditText

The reason for modifying the original AutoAdjustSizeEditText control is mainly because my current scene is a single-line scene with a fixed width. If there is no limit to the input content, the user experience may not be very good (I feel that some friends should also encounter similar scenes)

Custom properties (because both custom controls in my Demo use custom properties; and custom properties cannot be repeated, so the naming is slightly changed here)

    <!-- 文本自动调整大小显示自定义属性 -->
    <declare-styleable name="AutoAdaptTextSize">
        <attr name="minSize" format="dimension" />
        <attr name="maxSize" format="dimension" />
    </declare-styleable>

AutoAdaptSizeEditText (I feel that the change is okay, but it can also optimize some writing methods, let’s talk about it when I have time)

package com.example.edittextdemo;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.widget.EditText;
import android.widget.TextView;


/**
 * 自动调整字体文本输入框,限制单行输入宽度
 *
 * @author ly
 * @date 2023
 */
@SuppressLint("AppCompatCustomView")
public class AutoAdaptSizeEditText extends EditText {
    
    
    /**
     * 默认文字字体大小最小值(单位:像素)
     */
    private static final float DEFAULT_TEXT_SIZE_MIN = 20;

    /**
     * 默认文字字体大小最大值(单位:像素)(貌似用不上)
     */
    @SuppressWarnings("unused")
    private static final float DEFAULT_TEXT_SIZE_MAX = 60;

    /**
     * 画笔(用来测量已输入文字的长度)
     */
    private Paint paint;

    /**
     * 文字字体大小最小值
     */
    private float minTextSize = 0;

    /**
     * 文字字体大小最大值
     */
    private float maxTextSize = 0;

    /**
     * 判断输入文本字体是否变小过
     */
    private boolean hasScaleSmall = false;

    /**
     * 可输出文本的最大长度
     */
    private int length = 0;
    /**
     * 可编辑状态
     */
    private boolean editState = false;


    public AutoAdaptTextSize(Context context) {
    
    
        super(context);
        paint = new Paint();
    }

    public AutoAdaptTextSize(Context context, AttributeSet attrs) {
    
    
        super(context, attrs);
        init(context, attrs);
    }

    public void init(Context context, AttributeSet attrs) {
    
    
        paint = new Paint();
        editState = true;
        //读取自定义属性, 获取设置的字体大小范围
        if (null != attrs) {
    
    
            TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.AutoAdaptTextSize);
            if (null != array) {
    
    
                minTextSize = array.getDimension(R.styleable.AutoAdaptTextSize_minSize, DEFAULT_TEXT_SIZE_MIN);
                //如果未设置字体最大值,则使用当前字体大小作为最大值
                maxTextSize = array.getDimension(R.styleable.AutoAdaptTextSize_maxSize, this.getTextSize());
                //回收 TypedArray
                array.recycle();
            }
        }
        //未设置字体最小值,则使用默认最小值
        if (0 == minTextSize) {
    
    
            minTextSize = DEFAULT_TEXT_SIZE_MIN;
        }
        //未设置字体最大值,则使用当前字体大小作为最大值
        if (0 == maxTextSize) {
    
    
            //            maxTextSize = DEFAULT_TEXT_SIZE_MAX;
            maxTextSize = this.getTextSize();
        }
        //如果设置的值不正确(例如minTextSize>maxTextSize),则互换
        if (minTextSize > maxTextSize) {
    
    
            float minSize = maxTextSize;
            maxTextSize = minTextSize;
            minTextSize = minSize;
        }
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
    
    
        // 根据需要调整字体大小
        autoAdaptTextSize(this);
        super.onTextChanged(text, start, lengthBefore, lengthAfter);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    
    
        // 对比前后输入前后字体大小
        if (w != oldw) {
    
    
            autoAdaptTextSize(this);
        }
        super.onSizeChanged(w, h, oldw, oldh);
    }

    /**
     * 调整文本的显示
     */
    private void autoAdaptTextSize(TextView textView) {
    
    
        if (null == textView) {
    
    
            //参数错误,不与处理
            return;
        }
        //已输入文本
        String text = textView.getText().toString();
        //已输入文本长度
        int textWidth = textView.getWidth();
        if (text.isEmpty() || textWidth <= 0) {
    
    
            return;
        }
        //获取输入框总的可输入的文本长度
        float maxInputWidth = textView.getWidth() - textView.getPaddingLeft() - textView.getPaddingRight();
        //获取当前文本字体大小
        float currentTextSize = textView.getTextSize();
        Log.d("AutoScaleSizeEditText", "currentTextSize=" + String.valueOf(currentTextSize));
        //设置画笔的字体大小
        paint.setTextSize(currentTextSize);
        /*
         * 循环减小字体大小,条件如下
         * 1、文本字体小于最大值
         * 2、可输入文本长度小于已输入文本长度
         */
        while ((currentTextSize > minTextSize) && (paint.measureText(text) > maxInputWidth)) {
    
    
            Log.e("tag", "paint.measureText(text)=" + paint.measureText(text) + "maxInputWidth:" + maxInputWidth);
            hasScaleSmall = true;
            --currentTextSize;
            if (currentTextSize < minTextSize) {
    
    
                currentTextSize = minTextSize;
                break;
            }
            //设置画笔字体大小
            paint.setTextSize(currentTextSize);
        }
        /*
         * 循环增大字体大小,条件如下
         * 1、文本字体小于默认值
         * 2、可输入文本长度大于已输入文本长度
         */
        while (hasScaleSmall && (currentTextSize < maxTextSize) && (maxInputWidth > paint.measureText(text))) {
    
    
            ++currentTextSize;
            if (currentTextSize > maxTextSize) {
    
    
                currentTextSize = maxTextSize;
                break;
            }
            //设置画笔字体大小
            paint.setTextSize(currentTextSize);
        }

        /*
         * 限制输入,条件如下
         * 1、当前字体大小已经为我们设置的最小字体(兼容最小值)
         * 2、所有字体的宽度对比控件的最大宽度
         */
        Log.e("tag", "当前字体Size=" + currentTextSize + "最小字体Size:" + minTextSize);
        Log.e("tag", "字体宽度=" + paint.measureText(text) + "控件宽度:" + maxInputWidth);
        if (currentTextSize <= minTextSize && paint.measureText(text) > maxInputWidth) {
    
    
            Log.e("tag", "超过预设值,不支持继续输入");
            if (editState) {
    
    
                editState = false;
                length = text.length();
            }
            if (text.length() > length) {
    
    
                this.setText(text.substring(0, length));
                this.setSelection(length); //光标位于尾部
            }
            //最大可输出入字符数,限制输入的关键点
            this.setEms(length);
        } else {
    
    
            editState = true;
        }
        //设置文本字体(单位为像素px)
        textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, currentTextSize);
    }

}

Guess you like

Origin blog.csdn.net/qq_20451879/article/details/132408782