/* * Created by LuaView. * Copyright (c) 2017, Alibaba Group. All rights reserved. * * This source code is licensed under the MIT. * For the full copyright and license information,please view the LICENSE file in the root directory of this source tree. */ package com.taobao.luaview.util; import android.annotation.TargetApi; import android.content.Context; import android.content.res.Resources; import android.os.Build; import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.text.method.SingleLineTransformationMethod; import android.text.method.TransformationMethod; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.ViewGroup; import android.widget.TextView; /** * 文字相关的工具类 * * @author song * @date 15/10/22 */ public class TextUtil { private static final String TAG = "TextUtil"; private static final boolean SPEW = false; // Minimum size of the text in pixels private static final int DEFAULT_MIN_TEXT_SIZE = 8; //sp // How precise we want to be when reaching the target textWidth size private static final float DEFAULT_PRECISION = 0.5f; /** * 使文字大小适应frame * * @param view */ public static void adjustTextSize(TextView view) { final Context context = view.getContext(); float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity; TextPaint paint = new TextPaint(); int maxLines = getMaxLines(view); float minTextSize = scaledDensity * DEFAULT_MIN_TEXT_SIZE; float maxTextSize = view.getTextSize(); float precision = DEFAULT_PRECISION; adjustTextSize(view, paint, minTextSize, maxTextSize, maxLines, precision); } /** * 改变外框大小,适应文字 * 如果当前文字只有一行,则缩到外框适应文字大小 * 如果文字有多行,则宽度不变,缩小高度到适应文字大小 * 文字外框的位置不变 * * @param view */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public static void adjustSize(TextView view) { if (view != null && view.getLayoutParams() != null) { /* //更好的实现方式,但是ios那边不支持这种方式 ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; layoutParams.height = ViewGroup.LayoutParams.WRAP_CONTENT; view.setLayoutParams(layoutParams);*/ // 废弃的实现方式,使用WRAP_CONTENT最简单,且这种方式算出来的width有bug // @Deprecated /*int lineCount = Math.min(view.getLineCount(), getMaxLines(view)); int width, height; if (lineCount > 1) {//多行,宽度不变,改变高度 width = view.getWidth(); height = view.getLineHeight() * lineCount + view.getPaddingTop() + view.getPaddingBottom(); } else {//一行,改变宽度&高度 final Paint paint = view.getPaint(); width = (int) paint.measureText(view.getText().toString()) + view.getPaddingLeft() + view.getPaddingRight(); height = view.getLineHeight() + view.getPaddingTop() + view.getPaddingBottom(); }*/ int lineCount = Math.min(view.getLineCount(), getMaxLines(view)); float width, height; if (lineCount > 1) {//多行,宽度不变,改变高度 width = view.getWidth(); height = view.getLineHeight() * lineCount + view.getPaddingTop() + view.getPaddingBottom(); } else {//一行,改变宽度&高度 width = view.getPaddingLeft() + Layout.getDesiredWidth(view.getText(), view.getPaint()) + view.getPaddingRight(); height = view.getLineHeight() + view.getPaddingTop() + view.getPaddingBottom(); } if (view.getLayoutParams() != null) { ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); layoutParams.width = (int) width; layoutParams.height = (int) height; view.setLayoutParams(layoutParams); } } } /** * Re-sizes the textSize of the TextView so that the text fits within the bounds of the View. */ private static void adjustTextSize(TextView view, TextPaint paint, float minTextSize, float maxTextSize, int maxLines, float precision) { if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) { // Don't auto-size since there's no limit on lines. return; } int targetWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight(); if (targetWidth <= 0) { return; } CharSequence text = view.getText(); TransformationMethod method = view.getTransformationMethod(); if (method != null) { text = method.getTransformation(text, view); } Context context = view.getContext(); Resources r = Resources.getSystem(); DisplayMetrics displayMetrics; float size = maxTextSize; float high = size; float low = 0; if (context != null) { r = context.getResources(); } displayMetrics = r.getDisplayMetrics(); paint.set(view.getPaint()); paint.setTextSize(size); if ((maxLines == 1 && paint.measureText(text, 0, text.length()) > targetWidth) || getLineCount(text, paint, size, targetWidth, displayMetrics) > maxLines) { size = getAutofitTextSize(text, paint, targetWidth, maxLines, low, high, precision, displayMetrics); } if (size < minTextSize) { size = minTextSize; } view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); } /** * Recursive binary search to find the best size for the text. */ private static float getAutofitTextSize(CharSequence text, TextPaint paint, float targetWidth, int maxLines, float low, float high, float precision, DisplayMetrics displayMetrics) { float mid = (low + high) / 2.0f; int lineCount = 1; StaticLayout layout = null; paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid, displayMetrics)); if (maxLines != 1) { layout = new StaticLayout(text, paint, (int) targetWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); lineCount = layout.getLineCount(); } if (SPEW) Log.d(TAG, "low=" + low + " high=" + high + " mid=" + mid + " target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount); if (lineCount > maxLines) { // For the case that `text` has more newline characters than `maxLines`. if ((high - low) < precision) { return low; } return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision, displayMetrics); } else if (lineCount < maxLines) { return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision, displayMetrics); } else { float maxLineWidth = 0; if (maxLines == 1) { maxLineWidth = paint.measureText(text, 0, text.length()); } else { for (int i = 0; i < lineCount; i++) { if (layout.getLineWidth(i) > maxLineWidth) { maxLineWidth = layout.getLineWidth(i); } } } if ((high - low) < precision) { return low; } else if (maxLineWidth > targetWidth) { return getAutofitTextSize(text, paint, targetWidth, maxLines, low, mid, precision, displayMetrics); } else if (maxLineWidth < targetWidth) { return getAutofitTextSize(text, paint, targetWidth, maxLines, mid, high, precision, displayMetrics); } else { return mid; } } } private static int getLineCount(CharSequence text, TextPaint paint, float size, float width, DisplayMetrics displayMetrics) { paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size, displayMetrics)); StaticLayout layout = new StaticLayout(text, paint, (int) width, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true); return layout.getLineCount(); } private static int getMaxLines(TextView view) { int maxLines = -1; // No limit (Integer.MAX_VALUE also means no limit) TransformationMethod method = view.getTransformationMethod(); if (method != null && method instanceof SingleLineTransformationMethod) { maxLines = 1; } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { // setMaxLines() and getMaxLines() are only available on android-16+ maxLines = view.getMaxLines(); } return maxLines; } }