Как масштабировать/изменять размер текста в соответствии с TextView?
Я пытаюсь создать метод для изменения многострочного текста в TextView
, чтобы он соответствовал границам (как размерам X, так и Y) TextView
.
В настоящее время у меня есть что-то, но все, что он делает, изменяет размер текста таким образом, что только первая буква/символ текста заполняет размеры TextView
(т.е. только первая буква доступна для просмотра, и она огромна), Мне нужно, чтобы он соответствовал всем строкам текста в пределах TextView.
Вот что я до сих пор:
public static void autoScaleTextViewTextToHeight(TextView tv)
{
final float initSize = tv.getTextSize();
//get the width of the view back image (unscaled)....
float minViewHeight;
if(tv.getBackground()!=null)
{
minViewHeight = tv.getBackground().getIntrinsicHeight();
}
else
{
minViewHeight = 10f;//some min.
}
final float maxViewHeight = tv.getHeight() - (tv.getPaddingBottom()+tv.getPaddingTop())-12;// -12 just to be sure
final String s = tv.getText().toString();
//System.out.println(""+tv.getPaddingTop()+"/"+tv.getPaddingBottom());
if(minViewHeight >0 && maxViewHeight >2)
{
Rect currentBounds = new Rect();
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+initSize);
//System.out.println(""+maxViewHeight);
//System.out.println(""+(currentBounds.height()));
float resultingSize = 1;
while(currentBounds.height() < maxViewHeight)
{
resultingSize ++;
tv.setTextSize(resultingSize);
tv.getPaint().getTextBounds(s, 0, s.length(), currentBounds);
//System.out.println(""+(currentBounds.height()+tv.getPaddingBottom()+tv.getPaddingTop()));
//System.out.println("Resulting: "+resultingSize);
}
if(currentBounds.height()>=maxViewHeight)
{
//just to be sure, reduce the value
tv.setTextSize(resultingSize-1);
}
}
}
Я думаю, что проблема заключается в использовании tv.getPaint().getTextBounds(...)
. Он всегда возвращает небольшие числа для текстовых границ... малых относительно значений tv.getWidth()
и tv.getHeight()
... даже если размер текста намного больше ширины или высоты TextView
.
Ответы
Ответ 1
Библиотека AutofitTextView от MavenCentral отлично справляется с этим. Источник размещен на Github (1k + stars) в https://github.com/grantland/android-autofittextview
Добавьте в свой app/build.gradle
следующее:
repositories {
mavenCentral()
}
dependencies {
compile 'me.grantland:autofittextview:0.2.+'
}
Включить любой вид, расширяющий TextView в коде:
AutofitHelper.create(textView);
Разрешить любой просмотр, расширяющий TextView в XML:
<me.grantland.widget.AutofitLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
</me.grantland.widget.AutofitLayout>
Используйте встроенный виджет в коде или XML:
<me.grantland.widget.AutofitTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
/>
Ответ 2
Я смог ответить на свой вопрос, используя следующий код (см. ниже), но мое решение было очень специфичным для приложения. Например, это, вероятно, будет выглядеть только хорошо и/или работать для TextView размером до ок. 1/2 экрана (также с верхним краем 40px и 20px боковыми полями... без нижней границы).
Используя этот подход, вы можете создать свою аналогичную реализацию. Статический метод в основном просто смотрит на количество символов и определяет коэффициент масштабирования для применения к размеру текста TextView, а затем постепенно увеличивает размер текста до общей высоты (предполагаемая высота - с использованием ширины текста, текста высота и ширина TextView) находится чуть ниже ширины TextView. Параметры, необходимые для определения коэффициента масштабирования (например, if/else if statements), были заданы с помощью угадывания и проверки. Вам, вероятно, придется поиграть с цифрами, чтобы они работали для вашего конкретного приложения.
Это не самое элегантное решение, хотя его легко закодировать, и оно работает для меня. Кто-нибудь имеет лучший подход?
public static void autoScaleTextViewTextToHeight(final TextView tv, String s)
{
float currentWidth=tv.getPaint().measureText(s);
int scalingFactor = 0;
final int characters = s.length();
//scale based on # of characters in the string
if(characters<5)
{
scalingFactor = 1;
}
else if(characters>=5 && characters<10)
{
scalingFactor = 2;
}
else if(characters>=10 && characters<15)
{
scalingFactor = 3;
}
else if(characters>=15 && characters<20)
{
scalingFactor = 3;
}
else if(characters>=20 && characters<25)
{
scalingFactor = 3;
}
else if(characters>=25 && characters<30)
{
scalingFactor = 3;
}
else if(characters>=30 && characters<35)
{
scalingFactor = 3;
}
else if(characters>=35 && characters<40)
{
scalingFactor = 3;
}
else if(characters>=40 && characters<45)
{
scalingFactor = 3;
}
else if(characters>=45 && characters<50)
{
scalingFactor = 3;
}
else if(characters>=50 && characters<55)
{
scalingFactor = 3;
}
else if(characters>=55 && characters<60)
{
scalingFactor = 3;
}
else if(characters>=60 && characters<65)
{
scalingFactor = 3;
}
else if(characters>=65 && characters<70)
{
scalingFactor = 3;
}
else if(characters>=70 && characters<75)
{
scalingFactor = 3;
}
else if(characters>=75)
{
scalingFactor = 5;
}
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
//the +scalingFactor is important... increase this if nec. later
while((((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor)*tv.getTextSize())<tv.getHeight())
{
tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, tv.getTextSize()+0.25f);
currentWidth=tv.getPaint().measureText(s);
//System.out.println(((int)Math.ceil(currentWidth)/tv.getWidth()+scalingFactor));
}
tv.setText(s);
}
Спасибо.
Ответ 3
У меня была такая же проблема, и я написал класс, который, похоже, работает для меня. В принципе, я использовал статический макет, чтобы нарисовать текст в отдельном холсте и восстановить его до тех пор, пока не найду размер шрифта, который подходит. Вы можете увидеть класс, опубликованный в теме ниже. Надеюсь, это поможет.
Автоматическое масштабирование текста TextView для соответствия в пределах
Ответ 4
Наткнулся на это, ища решение самостоятельно... Я пробовал все другие решения, которые я мог видеть при переполнении стека и т.д., но никто из них не работал, поэтому я написал свой собственный.
В основном, обернув текстовое представление в пользовательском линейном макете, я смог успешно правильно измерить текст, убедившись, что он измеряется с фиксированной шириной.
<!-- TextView wrapped in the custom LinearLayout that expects one child TextView -->
<!-- This view should specify the size you would want the text view to be displayed at -->
<com.custom.ResizeView
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_margin="10dp"
android:layout_weight="1"
android:orientation="horizontal" >
<TextView
android:id="@+id/CustomTextView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
</com.custom.ResizeView>
Тогда код линейного макета
public class ResizeView extends LinearLayout {
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// oldWidth used as a fixed width when measuring the size of the text
// view at different font sizes
final int oldWidth = getMeasuredWidth() - getPaddingBottom() - getPaddingTop();
final int oldHeight = getMeasuredHeight() - getPaddingLeft() - getPaddingRight();
// Assume we only have one child and it is the text view to scale
TextView textView = (TextView) getChildAt(0);
// This is the maximum font size... we iterate down from this
// I've specified the sizes in pixels, but sp can be used, just modify
// the call to setTextSize
float size = getResources().getDimensionPixelSize(R.dimen.solutions_view_max_font_size);
for (int textViewHeight = Integer.MAX_VALUE; textViewHeight > oldHeight; size -= 0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
// measure the text views size using a fixed width and an
// unspecified height - the unspecified height means measure
// returns the textviews ideal height
textView.measure(MeasureSpec.makeMeasureSpec(oldWidth, MeasureSpec.EXACTLY), MeasureSpec.UNSPECIFIED);
textViewHeight = textView.getMeasuredHeight();
}
}
}
Надеюсь, это поможет кому-то.
Ответ 5
Я играл с этим довольно долгое время, пытаясь получить правильные размеры шрифтов на самых разных 7-дюймовых планшетах (Kindle Fire, Nexus7 и некоторых недорогих в Китае с экранами с низким разрешением) и устройствах.
Подход, который, наконец, работал у меня, заключается в следующем. "32" - это произвольный фактор, который в основном дает около 70 + символов в горизонтальной линии размером 7 дюймов, что является размером шрифта, который я искал. Соответственно отрегулируйте.
textView.setTextSize(getFontSize(activity));
public static int getFontSize (Activity activity) {
DisplayMetrics dMetrics = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dMetrics);
// lets try to get them back a font size realtive to the pixel width of the screen
final float WIDE = activity.getResources().getDisplayMetrics().widthPixels;
int valueWide = (int)(WIDE / 32.0f / (dMetrics.scaledDensity));
return valueWide;
}
Ответ 6
возможно, попробуйте установить setHoriztonallyScrolling() в true, прежде чем принимать текстовые измерения, чтобы textView не пытался разметки текста на нескольких строках
Ответ 7
Один из способов - указать различные размеры sp для каждого из обобщенных размеров экрана. Например, предоставить 8sp для небольших экранов, 12sp для обычных экранов, 16 sp для больших и 20 sp для xlarge. Тогда просто ваши макеты ссылаются на @dimen text_size или что-то еще, и вы можете быть уверены, так как плотность берется через sp. См. Следующую ссылку для получения дополнительной информации об этом подходе.
http://www.developer.android.com/guide/topics/resources/more-resources.html#Dimension
Однако я должен отметить, что поддержка большего количества языков означает большую работу на этапе тестирования, особенно если вы заинтересованы в сохранении текста в одной строке, поскольку некоторые языки имеют гораздо более длинные слова. В этом случае сделайте файл dimens.xml в папке с большими значениями, например, и измените значение вручную. Надеюсь, это поможет.
Ответ 8
Новый с Android O:
https://developer.android.com/preview/features/autosizing-textview.html
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:autoSizeTextType="uniform"
android:autoSizeMinTextSize="12sp"
android:autoSizeMaxTextSize="100sp"
android:autoSizeStepGranularity="2sp"
/>
Ответ 9
Вот решение, которое я создал на основе других отзывов. Это решение позволяет вам установить размер текста в формате XML, который будет максимальным, и он настроится на высоту представления.
Настройка размера TextView
private float findNewTextSize(int width, int height, CharSequence text) {
TextPaint textPaint = new TextPaint(getPaint());
float targetTextSize = textPaint.getTextSize();
int textHeight = getTextHeight(text, textPaint, width, targetTextSize);
while(textHeight > height && targetTextSize > mMinTextSize) {
targetTextSize = Math.max(targetTextSize - 1, mMinTextSize);
textHeight = getTextHeight(text, textPaint, width, targetTextSize);
}
return targetTextSize;
}
private int getTextHeight(CharSequence source, TextPaint paint, int width, float textSize) {
paint.setTextSize(textSize);
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, mSpacingMult, mSpacingAdd, true);
return layout.getHeight();
}
Ответ 10
Если ваше единственное требование состоит в том, чтобы текст автоматически разбивался и продолжался в следующей строке, а высота не важна, просто используйте его так.
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:maxEms="integer"
android:width="integer"/>
Это приведет к тому, что ваш TextView привяжет к нему содержимое по вертикали в зависимости от вашего значения maxEms.
Ответ 11
Проверьте, помогает ли мое решение:
Автоматическое масштабирование текста TextView для соответствия в пределах
Ответ 12
Я обнаружил, что это сработало для меня. см. https://play.google.com/store/apps/details?id=au.id.rupert.chauffeurs_name_board&hl=en
Исходный код http://www.rupert.id.au/chauffeurs_name_board/verson2.php
http://catchthecows.com/?p=72 и https://github.com/catchthecows/BigTextButton
Ответ 13
Это основано на матовом ответе. Он работал хорошо на некоторых устройствах, но не на всех. Я переместил изменение размера на шаг измерения, сделал максимальный размер шрифта пользовательским атрибутом, принял во внимание маржу и расширил FrameLayout вместо LineairLayout.
public class ResizeView extends FrameLayout {
protected float max_font_size;
public ResizeView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.ResizeView,
0, 0);
max_font_size = a.getDimension(R.styleable.ResizeView_maxFontSize, 30.0f);
}
public ResizeView(Context context) {
super(context);
}
@Override
protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
// Use the parent code for the first measure
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Assume we only have one child and it is the text view to scale
final TextView textView = (TextView) getChildAt(0);
// Check if the default measure resulted in a fitting textView
LayoutParams childLayout = (LayoutParams) textView.getLayoutParams();
final int textHeightAvailable = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - childLayout.topMargin - childLayout.bottomMargin;
int textViewHeight = textView.getMeasuredHeight();
if (textViewHeight < textHeightAvailable) {
return;
}
final int textWidthSpec = MeasureSpec.makeMeasureSpec(
MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft() - getPaddingRight() - childLayout.leftMargin - childLayout.rightMargin,
MeasureSpec.EXACTLY);
final int textHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
for (float size = max_font_size; size >= 1.05f; size-=0.1f) {
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
textView.measure(textWidthSpec, textHeightSpec);
textViewHeight = textView.getMeasuredHeight();
if (textViewHeight <= textHeightAvailable) {
break;
}
}
}
}
И это в attrs.xml:
<declare-styleable name="ResizeView">
<attr name="maxFontSize" format="reference|dimension"/>
</declare-styleable>
И, наконец, используется вот так:
<PACKAGE_NAME.ui.ResizeView xmlns:custom="http://schemas.android.com/apk/res/PACKAGE_NAME"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|center_vertical"
android:padding="5dp"
custom:maxFontSize="@dimen/normal_text">
<TextView android:id="@+id/tabTitle2"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</PACKAGE_NAME.ui.ResizeView>
Ответ 14
Попробуйте это...
tv.setText("Give a very large text anc check , this xample is very usefull");
countLine=tv.getLineHeight();
System.out.println("LineCount " + countLine);
if (countLine>=40){
tv.setTextSize(15);
}