Как автоматически настроить размер текста в многострочном TextView в соответствии с максимальными размерами вида?
Я искал способ автоматического подгонки текста внутри textview. Через мой поиск я нашел много решений, таких как:
но, как и многие другие, это не решает мою проблему. Они не работают так, как ожидалось, когда мы используем TextView с многострочным.
в основном моя цель-это один:
Как вы можете видеть, текст изменяется в зависимости от ширины, высоты, а также обращает внимание на разрыв строки, создавая многострочный textview. Также можно изменить шрифт.
одна из моих идей, чтобы решить это что-то вроде этого:
int size = CONSTANT_MAX_SIZE;
TextView tv = (TextView) findViewById(R.id.textview1)
while(Math.abs(tv.getMeasuredHeight()) >= TEXTVIEW_MAX_HEIGHT) {
size--;
tv.setTextSize(size);
tv.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);
i++;
}
CONSTANT_MAX_SIZE-константа, определяющая максимальный размер шрифта (свойство textsize в TextView)
TEXTVIEW_MAX_HEIGHT постоянная это определяет максимальный размер, который может иметь textview.
это вызывалось каждый раз при изменении текста в textview.
TextView xml был чем-то вроде этого:
<TextView
android:id="@+id/textview1"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:singleLine="false"
android:inputType="textMultiLine"
android:text=""
android:textSize="150sp" />
поскольку ширина будет ограничена в XML, необходимо учитывать только высоту представления, так как при необходимости настройка android автоматически создаст многострочную строку.
хотя это потенциальное решение не работает отлично (далеко от него), и это не поддерживает изменение размера вниз (при удалении текста).
любые предложения и/или идеи?
6 ответов
самое близкое решение этой проблемы было основано на методе из paint.
в основном paint имеет метод под названием "breaktext", который:
общественные инт breakText (объект CharSequence текст, начать инт, инт конец, логическое measureForwards, float maxWidth, float[] measuredWidth)
добавлено в API уровня 1
измерьте текст, останавливая раньше если измеренная ширина превышает значение maxwidth. Возвращает количество символов, которые были измерены, и если measuredWidth не является нулевым, возвращает в нем фактическую измеренную ширину.
я объединяю это с краской "getTextBounds", которая:
public void getTextBounds (текст строки, int start, int end, Rect bounds)
добавлено в API уровня 1
возврат в пределах (выделенных вызывающим) наименьший прямоугольник, который охватывает все >символы, с подразумеваемым началом в точке (0,0).
Итак, теперь я могу получить количество символов, которые соответствуют заданной ширине и высоте этих символов.
используя некоторое время, вы можете продолжать перемещать удаляющие символы из строки, которую вы хотите измерить, и получить количество строк (используя while(index
дополнительно вы необходимо добавить переменную высоту для каждых двух строк, представляющих пространство между строками (которое не учитывается в getTextBounds).
в качестве примера кода функция для определения высоты текста с несколькими строками выглядит примерно так:
public int getHeightOfMultiLineText(String text,int textSize, int maxWidth) {
paint = new TextPaint();
paint.setTextSize(textSize);
int index = 0;
int linecount = 0;
while(index < text.length()) {
index += paint.breakText(text,index,text.length,true,maxWidth,null);
linecount++;
}
Rect bounds = new Rect();
paint.getTextBounds("Yy", 0, 2, bounds);
// obtain space between lines
double lineSpacing = Math.max(0,((lineCount - 1) * bounds.height()*0.25));
return (int)Math.floor(lineSpacing + lineCount * bounds.height());
Примечание: переменная maxWidth находится в пикселях
затем вам придется вызвать этот метод через некоторое время, чтобы определить, какой максимальный размер шрифта для этой высоты. Пример кода быть:
textSize = 100;
int maxHeight = 50;
while(getHeightOfMultiLineText(text,textSize,maxWidth) > maxHeight)
textSize--;
к сожалению, это был единственный (насколько я знаю), как я смог добиться от изображения выше.
надеюсь, что это может помочь любому, кто пытается преодолеть это препятствие.
у вас есть довольно хорошее решение на эту проблему, но я взглянул на это с другой точки зрения. Я думаю, что логика проще и понятнее, по крайней мере, для новых разработчиков android. Идея основана на вставке символа "\n " между пробелами большой строки. Затем я устанавливаю отредактированную строку в textView в качестве отображаемого текста. Итак, вот код..
private String insertNewLineCharAtString(String str, int step){
StringBuilder sb = new StringBuilder();
int spaceCounter = 0;
String[] strArray = str.split(" ");
for(int i = 0; i < strArray.length; i++){
if(spaceCounter == step){
sb.append('\n');
sb.append(strArray[i]);
spaceCounter = 0;
}else{
sb.append(" "+strArray[i]);
}
spaceCounter++;
}
return sb.toString();
}
параметры просты. Str-это строка, которую мы будем редактировать и шаг переменной определяет, во сколько пробелов метод вставит символ' /n'. Потом , вы просто называете что-то вроде этого :
myTextView.setText(insertNewLineCharactersAtString(yourDisplayingStr);
надеюсь, это поможет многим из вас , что вы не хотите углубляться с TextPaint, Rects и математическими функциями..
спасибо за ваше решение, вы не супермен! Я реализовал ваш код в производном классе TextView. Код преобразуется в код C# для Xamarin android. Вы можете легко конвертировать в Java, если вам нужно(удалить первый конструктор для Java).
public class AutoFitTextView : TextView
{
public AutoFitTextView(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer)
: base(javaReference, transfer)
{
}
public AutoFitTextView(Context context)
: base(context)
{
}
public AutoFitTextView(Context context, IAttributeSet attrs)
: base(context, attrs)
{
}
public void ResizeFontSize()
{
int textSize = 50;
int maxHeight = this.Height;
while (GetHeightOfMultiLineText(this.Text, textSize, this.Width) > maxHeight)
{
textSize--;
}
float scaleFactor = Context.Resources.DisplayMetrics.ScaledDensity;
float additionalFactor = 1.2f;
TextSize = ((float)(textSize / (additionalFactor * scaleFactor)));
}
private int GetHeightOfMultiLineText(string text, int textSize, int maxWidth)
{
TextPaint paint = new TextPaint();
paint.TextSize = textSize;
int index = 0;
int lineCount = 0;
while (index < text.Length)
{
index += paint.BreakText(text, index, text.Length, true, maxWidth, null);
lineCount++;
}
Rect bounds = new Rect();
paint.GetTextBounds("Yy", 0, 2, bounds);
// obtain space between lines
double lineSpacing = Math.Max(0, ((lineCount - 1) * bounds.Height() * 0.25));
return (int)Math.Floor(lineSpacing + lineCount * bounds.Height());
}
}
вот код AXML
<touchtest.AutoFitTextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/tvLabel2"
android:singleLine="false"
android:inputType="textMultiLine"
android:text="I am here To test this text" />
немного улучшенный ответ phamducgiam. Он показывает текст с уже установленным размером, а не skretchs после показа. Выглядит уродливо в редакторе из-за запуска textSize 100, но работает так, как должно в run. Вот код:
import android.content.Context;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
public class FontFitTextView extends TextView {
public FontFitTextView(Context context) {
super(context);
}
public FontFitTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FontFitTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// start decreasing font size from 100
setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
// calculate dimensions. don't forget about padding
final float maxWidth = getWidth()-(getPaddingLeft()+getPaddingRight());
final float maxHeight = getHeight()-(getPaddingTop()+getPaddingBottom());
if (maxWidth < 1.0f || maxHeight < 1.0f) {
return;
}
CharSequence text = getText();
int lineCount = getLineCount(maxWidth, text);
float height = getHeight(lineCount);
// keep decreasing font size until it fits
while (height > maxHeight) {
final float textSize = getTextSize();
setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
height = getHeight(getLineCount(maxWidth, getText()));
}
// show fitted textView
requestLayout();
}
private float getHeight(int lineCount) {
return lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0);
}
private int getLineCount(float maxWidth, CharSequence text) {
int lineCount = 0;
int index = 0;
final TextPaint paint = getPaint();
while (index < text.length()) {
index += paint.breakText(text, index, text.length(), true, maxWidth, null);
lineCount++;
}
return lineCount;
}
}
С помощью идея от nunofmendes, Я написал производный класс TextView, который автоматически изменяет размер текста и поддерживает несколько строк.
import android.content.Context;
import android.os.Handler;
import android.text.Layout;
import android.text.TextPaint;
import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet;
import android.util.TypedValue;
public class AutoResizeTextView extends TextView {
private Handler measureHandler = new Handler();
private Runnable requestLayout = new Runnable() {
@Override
public void run() {
requestLayout();
}
};
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public AutoResizeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AutoResizeTextView(Context context) {
super(context);
}
@Override
protected void onMeasure(final int widthMeasureSpec,
final int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final float maxWidth = getWidth();
final float maxHeight = getHeight();
if (maxWidth < 1.0f || maxHeight < 1.0f) {
return;
}
int index = 0;
int lineCount = 0;
CharSequence text = getText();
final TextPaint paint = getPaint();
while (index < text.length()) {
index += paint.breakText(text, index, text.length(), true, maxWidth, null);
lineCount++;
}
final float height = lineCount * getLineHeight() + (lineCount > 0 ?
(lineCount - 1) * paint.getFontSpacing() : 0);
if (height > maxHeight) {
final float textSize = getTextSize();
setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
measureHandler.post(requestLayout);
}
}
}
компилируемый, фиксированный код: скопируйте-вставьте этот, а не принятый, потому что вам нужно его исправить:)
public static int getHeightOfMultiLineText(String text, int textSize, int maxWidth) {
TextPaint paint = new TextPaint();
paint.setTextSize(textSize);
int index = 0;
int lineCount = 0;
while (index < text.length()) {
index += paint.breakText(text, index, text.length(), true, maxWidth, null);
lineCount++;
}
Rect bounds = new Rect();
paint.getTextBounds("Yy", 0, 2, bounds);
// obtain space between lines
double lineSpacing = Math.max(0, ((lineCount - 1) * bounds.height() * 0.25));
return (int) Math.floor(lineSpacing + lineCount * bounds.height());
}
нужно использовать, спасибо.
важно: вам нужно учитывать масштабный фактор
int textSize = 50;
int maxHeight = boundsTv.height();
while (getHeightOfMultiLineText(text, textSize, params.width) > maxHeight) {
textSize--;
}
float scaleFactor = mainActivity.getResources().getDisplayMetrics().scaledDensity;
float temp = 1.2f;
tvText.setTextSize((float) (textSize / (temp*scaleFactor)));