Android最近新增了支持根据视图大小和最小/最大文本大小调整TextView文本大小的功能。
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
不幸的是,它们不支持EditText,那么还有其他替代方案吗?
Android最近新增了支持根据视图大小和最小/最大文本大小调整TextView文本大小的功能。
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
不幸的是,它们不支持EditText,那么还有其他替代方案吗?
我和你一样遇到了问题,EditText是TextView的子类,但不支持自动调整字体大小???
我通过某种方式解决了这个问题。起初,我查看了TextView的代码,并将其复制并作为扩展(在Kotlin中)实现到EditTextView上,但是...有大量的方法,所以最终我放弃了这个选项。
我的做法是使用一个不可见的TextView(是的,我知道这是一个完全的hack,我对此感到不太满意,但Google应该为此感到羞耻)。
这是我的xmls:
<TextView android:id="@+id/invisibleTextView"
android:layout_height="0dp"
android:layout_width="match_parent"
android:focusable="false"
app:autoSizeTextType="uniform"
app:autoSizeMinTextSize="@dimen/text_min"
app:autoSizeMaxTextSize="@dimen/text_max"
app:autoSizeStepGranularity="@dimen/text_step"
android:textAlignment="center"
app:layout_constraintLeft_toLeftOf="@id/main"
app:layout_constraintRight_toRightOf="@id/main"
app:layout_constraintTop_toBottomOf="@id/textCount"
app:layout_constraintBottom_toBottomOf="@id/main"
android:visibility="invisible"
tool:text="This is a Resizable Textview" />
<EditText android:id="@+id/resizableEditText"
android:layout_height="0dp"
android:layout_width="match_parent"
android:textAlignment="center"
app:layout_constraintLeft_toLeftOf="@id/main"
app:layout_constraintRight_toRightOf="@id/main"
app:layout_constraintTop_toBottomOf="@id/textCount"
app:layout_constraintBottom_toBottomOf="@id/main"
android:maxLength="@integer/max_text_length"
tool:text="This is a Resizable EditTextView" />
注意: 两个视图的宽度/高度必须相同。
然后在我的代码中,我使用这个TextView的自动计算值来应用于我的EditTextView。
private fun setupAutoresize() {
invisibleTextView.setText("a", TextView.BufferType.EDITABLE) //calculate the right size for one character
resizableEditText.textSize = autosizeText(invisibleTextView.textSize)
resizableEditText.setHint(R.string.text_hint)
resizableEditText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(editable: Editable?) {
resizableEditText.textSize = autosizeText(invisibleTextView.textSize)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
textCount.text = currentCharacters.toString()
val text = if (s?.isEmpty() ?: true) getString(R.string.text_hint) else s.toString()
invisibleTextView.setText(text, TextView.BufferType.EDITABLE)
}
})
}
private fun autosizeText(size: Float): Float {
return size / (resources.displayMetrics.density + MARGIN_FACTOR /*0.2f*/)
}
这个库基于:
AutoFitTextView https://github.com/ViksaaSkool/AutoFitEditText 请尝试使用它
android:autoSizeTextType="uniform"
通过在EditText上使用setOnTextChangedListener,您可以将TextView的文本设置为EditText中的任何内容。然后TextView的文本大小将自动调整。然后您需要将EditText的文本大小设置为与TextView相同。这是布局:<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" tools: context=".MainActivity">
<TextView
android:id="@+id/test"
android:layout_width="match_parent" android:layout_height="match_parent" android:padding="0dp"
android:layout_margin="0dp"
android: autoSizeTextType="uniform"
android: autosizeMaxTextSize="500sp" android: autosizestepGranularity="1sp"/>
<EditText
android:id="@+id/edit"
android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="0dp"/>
android:padding="0dp"
</RelativeLayout>
而且代码:
public void code(){
edit.setMovement Method (null);
edit.addTextChangedListener(new Textwatcher() {
@Override
public void onTextChanged (CharSequence s, int start, int before, int count) { test.setText (edit.getText().tostring()); edit.setTextSize(pixel2dip(test.getTextSize()));
}
public static float pixel2dip(float a) {
int b = (int) (a);
int c = (int) (b / Resources.getSystem().getDisplayMetrics ().scaledDensity); return (float) (c);
});
}
这对我有效。在xml中添加:
<EditText
android:id="@+id/editTextMsg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="0.1"
android:background="@drawable/edit_corner_white"
android:gravity="start|top"
android:inputType="textMultiLine"
android:minLines="1"
android:lines="1"
android:maxLines="20"
android:textSize="18dp"
android:padding="10dp"
android:hint="@string/napisz_cos"
android:imeOptions="actionSend"
/>
然后在活动代码中:
editTextMsg.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
int div = editTextMsg.getText().toString().length() % 25; //max 25chars in line
if(div == 0 ) {
String tx = editTextMsg.getText().toString().trim() + "\n";
editTextMsg.setText(tx);
int pxH = (int)( editTextMsg.getLineCount() * 24 * getResources().getDisplayMetrics().density);
editTextMsg.setHeight (pxH );
//set cursor to end of text..
editTextMsg.setSelection(editTextMsg.length());
}
}
});
这里有一个针对单行EditText视图的选项,它在每次更改后计算文本宽度并根据需要缩放textScale,遵循最大和最小大小。
也许有人可以改进它,使其适用于多行EditText。
// set up auto-text-size
val maxTextScale = binding.editText.textSize
val minTextScale = 0.2 * maxTextScale // ensure text doesn't get too small.
binding.editText.addTextChangedListener(object : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
val paint = TextPaint(binding.editText.paint)
val desiredTextWidth = StaticLayout.getDesiredWidth(s, paint)
// added so that the text isn't slightly too big
val ensureWiggleRoom = 0.95F
val scaleFactor = binding.editText.width / desiredTextWidth
val candidateTextSize = truncate(binding.editText.textSize * scaleFactor * ensureWiggleRoom)
if (candidateTextSize > minTextScale && candidateTextSize < maxTextScale) {
binding.editText.setTextSize(TypedValue.COMPLEX_UNIT_PX, candidateTextSize)
}
}
override fun afterTextChanged(s: Editable?) {}
})
/**
* Wrapper class for {@link EditText}.
* It helps to achieve auto size behaviour which exists in {@link AppCompatTextView}.
* The main idea of getting target text size is measuring {@link AppCompatTextView} and then
* extracting from it text size and then applying extracted text size to target {@link EditText}.
*/
public class AutoSizeEditText extends FrameLayout {
private static final String TEST_SYMBOL = "T";
private static final boolean TEST = false;
/**
* Vertical margin which is applied by default in {@link EditText} in
* comparison to {@link AppCompatTextView}
*/
private static final float VERTICAL_MARGIN = convertDpToPixel(4);
/**
* {@link TextMeasure} which helps to get target text size for {@link #wrappedEditTex}
* via its auto size behaviour.
*/
@NonNull
private final TextMeasure textMeasurer;
/**
* {@link AppCompatEditText} we want to show to the user
*/
@NonNull
private final EditText wrappedEditTex;
public AutoSizeEditText(Context context) {
this(context, null);
}
public AutoSizeEditText(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AutoSizeEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// don't clip children
setClipChildren(false);
setClipToOutline(false);
setClipToPadding(false);
// using AttributeSet of TextView in order to apply it our text views
wrappedEditTex = createWrappedEditText(context, attrs);
textMeasurer = createTextMeasure(context, attrs, wrappedEditTex);
addView(wrappedEditTex, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
addView(textMeasurer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
@NonNull
private TextMeasure createTextMeasure(Context context, AttributeSet attrs, EditText editText) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.AutoSizeEditText);
final int minSize = (int) typedArray.getDimension(R.styleable.AutoSizeEditText_autoSizeMinTextSize, convertDpToPixel(10));
final int maxSize = (int) typedArray.getDimension(R.styleable.AutoSizeEditText_autoSizeMaxTextSize, convertDpToPixel(18));
final int step = (int) typedArray.getDimension(R.styleable.AutoSizeEditText_autoSizeStepGranularity, convertDpToPixel(1));
typedArray.recycle();
TextMeasure textMeasurer = new TextMeasure(context);
final Editable text = editText.getText();
final CharSequence hint = editText.getHint();
if (!TextUtils.isEmpty(text)) {
textMeasurer.setText(text);
} else if (!TextUtils.isEmpty(hint)) {
textMeasurer.setText(hint);
} else {
textMeasurer.setText(TEST_SYMBOL);
}
TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(
textMeasurer, minSize, maxSize, step, TypedValue.COMPLEX_UNIT_PX);
textMeasurer.setVisibility(View.INVISIBLE);
textMeasurer.setPadding(0, 0, 0, 0);
if (TEST) {
textMeasurer.setTextColor(Color.RED);
final ColorDrawable background = new ColorDrawable(Color.YELLOW);
background.setAlpha(50);
textMeasurer.setBackground(background);
textMeasurer.setAlpha(0.2f);
textMeasurer.setVisibility(View.VISIBLE);
}
return textMeasurer;
}
/**
* Creating {@link EditText} which user will use and see
*
* @param attrs {@link AttributeSet} which comes from most likely from xml, which can be user for {@link EditText}
* if attributes of {@link TextView} were declared in xml
* @return created {@link EditText}
*/
@NonNull
protected EditText createWrappedEditText(Context context, AttributeSet attrs) {
return new AppCompatEditText(context, attrs);
}
@NonNull
public EditText getWrappedEditTex() {
return wrappedEditTex;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
wrappedEditTex.measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
final int targetHeight = (int) (height
- VERTICAL_MARGIN * 2
- wrappedEditTex.getPaddingTop()
- wrappedEditTex.getPaddingBottom());
final int targetWidth = (width
- wrappedEditTex.getTotalPaddingStart()
- wrappedEditTex.getTotalPaddingEnd());
textMeasurer.measure(
MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(targetHeight, MeasureSpec.EXACTLY)
);
setMeasuredDimension(width, height);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
final int layoutHeight = getMeasuredHeight();
final int layoutWidth = getMeasuredWidth();
wrappedEditTex.layout(0, 0, layoutWidth, layoutHeight);
if (changed) {
final int measuredHeight = textMeasurer.getMeasuredHeight();
final int measuredWidth = textMeasurer.getMeasuredWidth();
final int topCoordinate = (int) (wrappedEditTex.getPaddingTop() + VERTICAL_MARGIN);
final int leftCoordinate = wrappedEditTex.getTotalPaddingStart();
textMeasurer.layout(
leftCoordinate,
topCoordinate,
measuredWidth + leftCoordinate,
topCoordinate + measuredHeight);
wrappedEditTex.setTextSize(TypedValue.COMPLEX_UNIT_PX, textMeasurer.getTextSize());
}
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return wrappedEditTex.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return wrappedEditTex.onTouchEvent(event);
}
/**
* Adjust text size due to the fact we want hint to be always visible
*
* @param hint Hint for {@link #wrappedEditTex}
*/
public void setHint(CharSequence hint) {
wrappedEditTex.setHint(hint);
textMeasurer.setText(hint);
}
/**
* Adjust text size for TypeFace, because it can change calculations
*
* @param typeface for {@link #wrappedEditTex}
*/
public void setTypeface(Typeface typeface) {
wrappedEditTex.setTypeface(typeface);
textMeasurer.setTypeface(typeface);
}
public void setTextColor(Integer textColor) {
wrappedEditTex.setTextColor(textColor);
}
public void setHintTextColor(Integer hintTextColor) {
wrappedEditTex.setHintTextColor(hintTextColor);
}
public void setText(CharSequence text) {
wrappedEditTex.setText(text);
}
private static class TextMeasure extends AppCompatTextView {
public TextMeasure(Context context) {
super(context);
}
@Override
public void setInputType(int type) {
}
@Override
public void setRawInputType(int type) {
}
@Override
public int getInputType() {
return EditorInfo.TYPE_NULL;
}
@Override
public int getMaxLines() {
return 1;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return true;
}
@Override
public int getMinLines() {
return 1;
}
}
}
以下是使用我的组件的示例:
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<com.vladislavkarpman.autosizeedittext.AutoSizeEditText
android:layout_width="300dp"
android:layout_height="100dp"
app:autoSizeMaxTextSize="50dp"
app:autoSizeMinTextSize="4dp"
app:autoSizeStepGranularity="1dp"
app:autoSizeTextType="uniform"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>