遇到这么一个需求:“控件宽度有限,随着输入内容,动态修改字体大小”,如果是你,只如何来实现?又有几种方式?
嗯,就是这么一个简单的需求,让我记录了俩篇blog
- Android进阶之路 - 去除EditText内边距
- Android进阶之路 - EditText输入字体自适应
起初我曾尝试通过监听TextChanged
+ 字体自适应 的方式,来实现 输入字体自适应
,但是效果并不理想 ,所以最终换了别的方式
- 简单、直接、有点low
- AutoAdjustSizeEditText
- AutoAdaptSizeEditText
该篇通过我所使用的几种方式,看看能否帮助大家,具体采用了以下几种方式,先简单介绍一下
监听TextChanged
,在一定规则内直接设置字体大小(字体过度不自然)- AutoAdjustSizeEditText 自定义控件(基本满足场景,不过单行可支持无线输入,可无限滑动)
- AutoAdaptSizeEditText 控件借鉴于前者,稍加修改,用于满足某一场景(设置单行场景,超过固定宽度,则不可继续输入)
AutoAdjustSizeEditText、AutoAdaptSizeEditText
效果与布局引入
效果
布局引入
<?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.AutoAdjustSizeEditTextandroid: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.AutoAdaptSizeEditTextandroid: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>
简单、直接、有点low
这种方式就想我说的使用起来很简单,唯一不足可能在于
首先需要了解输入规则的要求,同时字体适应时会生硬一些
(目前因为框架原因,我先使用了该方式)
因为框架原因,我直接提供伪代码
用于各位借鉴吧
EditText - addTextChangedListener
通过规则,自行定义字体大小
splitties框架 的 EditText.setTextIfDifferent
扩展函数,内部会自行设置焦点位置
AutoAdjustSizeEditText
我看了很多篇关于
EditText 输入字体自适应
Blog,大多好像都脱胎于早期这款 AutoAdjustSizeEditText 自定义控件,我直接将源码跑完后发现基本可以适用于大部分场景,其中有优点有不足(仅个人认为),但依旧不可否认可以从前辈的代码中学习和成长(为表尊重,源码不做任何修改)
适用大部分场景,如果对单行显示长度有限定或许不太满足
自定义属性(之前一直没记录过自定义属性的相关blog,等有时间我必须补充一篇)
<!-- 文本自动调整大小显示自定义属性 --><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());//回收 TypedArrayarray.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));}@Overrideprotected void onTextChanged(CharSequence text, int start,int lengthBefore, int lengthAfter) {// 根据需要调整字体大小adjustTextSize(this);super.onTextChanged(text, start, lengthBefore, lengthAfter);}@Overrideprotected 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
之所以修改原始 AutoAdjustSizeEditText 控件,主要是考虑到我当前场景为单行场景,且宽度固定,如果一直对输入内容不做限制,用户体验上可能不太好(感觉部分朋友应该也会遇到类似场景)
自定义属性(因为我Demo中这俩款自定义控件都用到了自定义属性;而自定义属性不可重复,所以这里命名稍有改变)
<!-- 文本自动调整大小显示自定义属性 --><declare-styleable name="AutoAdaptTextSize"><attr name="minSize" format="dimension" /><attr name="maxSize" format="dimension" /></declare-styleable>
AutoAdaptSizeEditText (感觉改的还行,不过还能优化一些写法,有时间再说吧)
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());//回收 TypedArrayarray.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;}}@Overrideprotected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {// 根据需要调整字体大小autoAdaptTextSize(this);super.onTextChanged(text, start, lengthBefore, lengthAfter);}@Overrideprotected 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);}}