遇到这么一个需求:“控件宽度有限,随着输入内容,动态修改字体大小”,如果是你,只如何来实现?又有几种方式?
嗯,就是这么一个简单的需求,让我记录了俩篇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.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>
简单、直接、有点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());
//回收 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
之所以修改原始 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());
//回收 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);
}
}