2010-04-11 2 views
170

Есть ли способ в android отредактировать текст в текстовом виде, чтобы он соответствовал занимаемому им пространству?Как отрегулировать размер шрифта текста, чтобы он соответствовал textview

E.g. Я использую TableLayout и добавляю несколько строк TextView в каждую строку. Поскольку я не хочу, чтобы текст TextView был обернут текст, я скорее вижу, что он уменьшает размер шрифта содержимого.

Любые идеи?

Я пробовал measureText, но так как я не знаю, размер столбца кажется хлопотным для использования. Это код, где я хочу, чтобы изменить размер шрифта на то, что соответствует

TableRow row = new TableRow(this); 
for (int i=0; i < ColumnNames.length; i++) {  
    TextView textColumn = new TextView(this);  
    textColumn.setText(ColumnNames[i]); 
    textColumn.setPadding(0, 0, 1, 0); 
    textColumn.setTextColor(getResources().getColor(R.drawable.text_default));   
    row.addView(textColumn, new TableRow.LayoutParams()); 
} 
table.addView(row, new TableLayout.LayoutParams()); 
+0

Проверить мое решение, основанное на коде dunni здесь http://stackoverflow.com/questions/5033012/auto-scale-textview-text-to-fit-within-bounds/5280436#5280436 Примечание. Я не реализовал его с помощью цикла. PS: thanks dunni – vsm

ответ

28

я написал класс, который расширяет TextView и делает это. Он просто использует measureText, как вы предлагаете. В основном он имеет максимальный размер текста и минимальный размер текста (который может быть изменен), и он просто пробегает размеры между ними в декрементах 1, пока не найдет самую большую, которая будет соответствовать. Не особенно элегантный, но я не знаю другого пути.

Вот код:

import android.content.Context; 
import android.graphics.Paint; 
import android.util.AttributeSet; 
import android.widget.TextView; 

public class FontFitTextView extends TextView { 

    public FontFitTextView(Context context) { 
     super(context); 
     initialise(); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialise(); 
    } 

    private void initialise() { 
     testPaint = new Paint(); 
     testPaint.set(this.getPaint()); 
     //max size defaults to the intially specified text size unless it is too small 
     maxTextSize = this.getTextSize(); 
     if (maxTextSize < 11) { 
      maxTextSize = 20; 
     } 
     minTextSize = 10; 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth) { 
     if (textWidth > 0) { 
      int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 
      float trySize = maxTextSize; 

      testPaint.setTextSize(trySize); 
      while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) { 
       trySize -= 1; 
       if (trySize <= minTextSize) { 
        trySize = minTextSize; 
        break; 
       } 
       testPaint.setTextSize(trySize); 
      } 

      this.setTextSize(trySize); 
     } 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { 
     refitText(text.toString(), this.getWidth()); 
    } 

    @Override 
    protected void onSizeChanged (int w, int h, int oldw, int oldh) { 
     if (w != oldw) { 
      refitText(this.getText().toString(), w); 
     } 
    } 

    //Getters and Setters 
    public float getMinTextSize() { 
     return minTextSize; 
    } 

    public void setMinTextSize(int minTextSize) { 
     this.minTextSize = minTextSize; 
    } 

    public float getMaxTextSize() { 
     return maxTextSize; 
    } 

    public void setMaxTextSize(int minTextSize) { 
     this.maxTextSize = minTextSize; 
    } 

    //Attributes 
    private Paint testPaint; 
    private float minTextSize; 
    private float maxTextSize; 

} 
+10

Бинарный поиск обычно будет быстрее, чем линейный поиск. –

+0

Я думаю, было бы разумнее сделать refitText на «onLayout», а не на «onSizeChanged» – Jacky

+0

Можете ли вы посмотреть мою проблему http://stackoverflow.com/questions/36265448/objectanimator-pixlated-textview – Muhammad

2

Я использовал вариант решения Dunni выше, но этот конкретный код не работает для меня. В частности, при попытке использовать объект Paint, который имеет свойства объекта Paint для представления, а затем вызывая measureText(), он не возвращает то же значение, что и прямое обращение к объекту Paint. Возможно, есть некоторые различия в том, как настроены мои представления, которые делают поведение другим.

Мое решение заключалось в том, чтобы напрямую использовать Paint в представлении, хотя при изменении размера шрифта для просмотра несколько раз могут быть некоторые штрафы за производительность.

5

Работы с модификацией

Вы должны установить размер представления текста, как это потому, что в противном случае setTextSize принимает значение в SP единицах:

setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize); 

И вам нужно явно добавить этот код.

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
{ 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
    refitText(this.getText().toString(), parentWidth); 
} 
5

Незначительная модификация onMeasure:

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
{ 
    super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
    int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
    int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
    refitText(this.getText().toString(), parentWidth); 
    this.setMeasuredDimension(parentWidth, parentHeight); 
} 

и бинарный поиск на refitText:

private void refitText(String text, int textWidth) 
{ 
    if (textWidth > 0) 
    { 
     int availableWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight();   
     int trySize = (int)maxTextSize; 
     int increment = ~(trySize - (int)minTextSize)/2; 

     testPaint.setTextSize(trySize); 
     while ((trySize > minTextSize) && (testPaint.measureText(text) > availableWidth)) 
     { 
      trySize += increment; 
      increment = (increment == 0) ? -1 : ~increment/2; 
      if (trySize <= minTextSize) 
      { 
       trySize = (int)minTextSize; 
       break; 
      } 
      testPaint.setTextSize(trySize); 
     } 

     this.setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize); 
    } 
} 
+3

Вместо бинарный поиск, простое масштабирование работает довольно хорошо: 'trySize * = availableWidth/measured_width' (затем зажат на minTextSize). –

7

У меня была такая же проблема, и написал класс, который, кажется, работает для меня. В принципе, я использовал статический макет, чтобы нарисовать текст в отдельном холсте и восстановить его до тех пор, пока не найду размер шрифта, который подходит. Вы можете увидеть класс, опубликованный в теме ниже. Я надеюсь, что это помогает.

Auto Scale TextView Text to Fit within Bounds

5

Я нашел следующее, чтобы работать хорошо для меня. Он не зацикливается и учитывает как высоту, так и ширину. Обратите внимание, что при вызове setTextSize в представлении важно указать блок PX. Благодаря подсказке в предыдущем посте для этого!

Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight); 
setTextSize(TypedValue.COMPLEX_UNIT_PX,paint.getTextSize()); 

Вот рутина, которую я использую, передавая getPaint() из представления. Для оценки ширины, не зависящей от фактической строки, используется 10-символьная строка с «широким» символом.

private static final String text10="OOOOOOOOOO"; 
public static Paint adjustTextSize(Paint paint, int numCharacters, int widthPixels, int heightPixels) { 
    float width = paint.measureText(text10)*numCharacters/text10.length(); 
    float newSize = (int)((widthPixels/width)*paint.getTextSize()); 
    paint.setTextSize(newSize); 

    // remeasure with font size near our desired result 
    width = paint.measureText(text10)*numCharacters/text10.length(); 
    newSize = (int)((widthPixels/width)*paint.getTextSize()); 
    paint.setTextSize(newSize); 

    // Check height constraints 
    FontMetricsInt metrics = paint.getFontMetricsInt(); 
    float textHeight = metrics.descent-metrics.ascent; 
    if (textHeight > heightPixels) { 
     newSize = (int)(newSize * (heightPixels/textHeight)); 
     paint.setTextSize(newSize); 
    } 

    return paint; 
} 
+0

Как вы включаете его в макет xml? –

+0

Этот код полезен для размещения текста в существующем представлении в области ограниченного размера или вы можете создать свой собственный производный класс из TextView и переопределить onMeasure, как показано в других сообщениях. Сам по себе он не может использоваться в макете. – Glenn

+0

Убедитесь, что вы проверяете размеры вида * после того, как оно было нарисовано. Выполнение этого во время onCreate() слишком рано. Я использовал ViewTreeObserver, чтобы убедиться, что измерения были сделаны в нужное время. –

123

В приведенном ниже решении представлены все предложения. Он начинается с того, что было первоначально опубликовано Dunni. Он использует двоичный поиск, например gjpc, но он немного читаем. Он также включает исправления ошибок gregm и исправление ошибок.

import android.content.Context; 
import android.graphics.Paint; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.widget.TextView; 

public class FontFitTextView extends TextView { 

    public FontFitTextView(Context context) { 
     super(context); 
     initialise(); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialise(); 
    } 

    private void initialise() { 
     mTestPaint = new Paint(); 
     mTestPaint.set(this.getPaint()); 
     //max size defaults to the initially specified text size unless it is too small 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth) 
    { 
     if (textWidth <= 0) 
      return; 
     int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 
     float hi = 100; 
     float lo = 2; 
     final float threshold = 0.5f; // How close we have to be 

     mTestPaint.set(this.getPaint()); 

     while((hi - lo) > threshold) { 
      float size = (hi+lo)/2; 
      mTestPaint.setTextSize(size); 
      if(mTestPaint.measureText(text) >= targetWidth) 
       hi = size; // too big 
      else 
       lo = size; // too small 
     } 
     // Use lo so that we undershoot rather than overshoot 
     this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int height = getMeasuredHeight(); 
     refitText(this.getText().toString(), parentWidth); 
     this.setMeasuredDimension(parentWidth, height); 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { 
     refitText(text.toString(), this.getWidth()); 
    } 

    @Override 
    protected void onSizeChanged (int w, int h, int oldw, int oldh) { 
     if (w != oldw) { 
      refitText(this.getText().toString(), w); 
     } 
    } 

    //Attributes 
    private Paint mTestPaint; 
} 
+8

Спасибо за объединение всех отзывов. Я вижу, что решение принимает ** только ширину **. Моя проблема в том, что фонд превышает высоту. –

+3

О, кажется, частично работает во время работы. На устройстве от эмулятора текст разрезается наполовину вверх. В редакторе макетов Eclipse он выглядит отлично. Есть идеи? –

+0

Прекрасно подходит для меня. Thx для публикации. Как я могу представить атрибут XML для указания hi/lo - и использовать значение атрибута (если оно существует - сохранение существующих значений как значений по умолчанию) в методе refitText? –

0

Вдохновленный предыдущими плакатами, я хотел поделиться своим решением. Он работает с масштабным коэффициентом, который применяется к предыдущему размеру шрифта, чтобы он соответствовал доступному пространству. В дополнение к предотвращению неожиданного поведения метода OnDraw TextViews он просто рисует текст самостоятельно.

public class FontFitTextView extends TextView { 

    // How much of the available space should be used in percent. 
    private static final float MARGINHEIGHT = 0.8f; 
    private static final float MARGINWIDTH = 0.8f; 

    private Paint paint; 
    private int viewWidth; 
    private int viewHeight; 
    private float textHeight; 
    private float textWidth; 

    public FontFitTextView(Context c) { 
     this(c, null); 
    } 

    public FontFitTextView(Context c, AttributeSet attrs) { 
     super(c, attrs); 
     initComponent(); 
    } 

    // Default constructor override 
    public FontFitTextView(Context c, AttributeSet attrs, int defStyle) { 
     super(c, attrs, defStyle); 
     initComponent(); 
    } 

    private void initComponent() { 
     paint = new Paint(); 
     paint.setTextSize(30); 
     paint.setTextAlign(Align.CENTER); 
     paint.setAntiAlias(true); 
    } 

    public void setFontColor(int c) { 
     paint.setColor(c); 
    } 

    private void calcTextSize(String s, Canvas c) { 

     float availableHeight = viewHeight; 
     float availableWidth = viewWidth; 

     // This value scales the old font up or down to match the available 
     // space. 
     float scale = 1.0f; 

     // Rectangle for measuring the text dimensions 
     Rect rect = new Rect(); 
     float oldFontSize = paint.getTextSize(); 

     // Calculate the space used with old font size 
     paint.getTextBounds(s, 0, s.length(), rect); 
     textWidth = rect.width(); 
     textHeight = rect.height(); 

     // find scale-value to fit the text horizontally 
     float scaleWidth = 1f; 
     if (textWidth > 0.0f) { 
      scaleWidth = (availableWidth)/textWidth * MARGINWIDTH; 
     } 

     // find scale-value to fit the text vertically 
     float scaleHeight = 1f; 
     if (textHeight > 0.0f) { 
      scaleHeight = (availableHeight)/textHeight * MARGINHEIGHT; 
     } 

     // We are always limited by the smaller one 
     if (scaleWidth < scaleHeight) { 
      scale = scaleWidth; 
     } else { 
      scale = scaleHeight; 
     } 

     // We apply the scale to the old font size to make it bigger or smaller 
     float newFontSize = (oldFontSize * scale); 
     paint.setTextSize(newFontSize); 
    } 

    /** 
    * Calculates the origin on the Y-Axis (width) for the text in this view. 
    * 
    * @return 
    */ 
    private float calcStartDrawingPosX() { 
     float left = getMeasuredWidth(); 
     float centerY = left - (viewWidth/2); 
     return centerY; 
    } 

    /** 
    * Calculates the origin on the Y-Axis (height) for the text in this view. 
    * 
    * @return 
    */ 
    private float calcStartDrawingPosY() { 
     float bottom = getMeasuredHeight(); 
     // The paint only centers horizontally, origin on the Y-Axis stays at 
     // the bottom, thus we have to lift the origin additionally by the 
     // height of the font. 
     float centerX = bottom - (viewHeight/2) + (textHeight/2); 
     return centerX; 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
     String text = getText().toString(); 
     if (text.length() > 0) { 
      calcTextSize(text, canvas); 
      canvas.drawText(text, calcStartDrawingPosX(), 
        calcStartDrawingPosY(), paint); 
     } 
    }; 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     viewWidth = w; 
     viewHeight = h; 
     super.onSizeChanged(w, h, oldw, oldh); 
    } 
} 
+2

это действительно не работает. –

17

Это speedplane's FontFitTextView, но это только уменьшает размер шрифта, если это необходимо, чтобы сделать текст прилегание и сохраняет размер шрифта в противном случае. Он не увеличивает размер шрифта до высоты.

public class FontFitTextView extends TextView { 

    // Attributes 
    private Paint mTestPaint; 
    private float defaultTextSize; 

    public FontFitTextView(Context context) { 
     super(context); 
     initialize(); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialize(); 
    } 

    private void initialize() { 
     mTestPaint = new Paint(); 
     mTestPaint.set(this.getPaint()); 
     defaultTextSize = getTextSize(); 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth) { 

     if (textWidth <= 0 || text.isEmpty()) 
      return; 

     int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 

     // this is most likely a non-relevant call 
     if(targetWidth<=2) 
      return; 

     // text already fits with the xml-defined font size? 
     mTestPaint.set(this.getPaint()); 
     mTestPaint.setTextSize(defaultTextSize); 
     if(mTestPaint.measureText(text) <= targetWidth) { 
      this.setTextSize(TypedValue.COMPLEX_UNIT_PX, defaultTextSize); 
      return; 
     } 

     // adjust text size using binary search for efficiency 
     float hi = defaultTextSize; 
     float lo = 2; 
     final float threshold = 0.5f; // How close we have to be 
     while (hi - lo > threshold) { 
      float size = (hi + lo)/2; 
      mTestPaint.setTextSize(size); 
      if(mTestPaint.measureText(text) >= targetWidth) 
       hi = size; // too big 
      else 
       lo = size; // too small 

     } 

     // Use lo so that we undershoot rather than overshoot 
     this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int height = getMeasuredHeight(); 
     refitText(this.getText().toString(), parentWidth); 
     this.setMeasuredDimension(parentWidth, height); 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, 
      final int before, final int after) { 
     refitText(text.toString(), this.getWidth()); 
    } 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
     if (w != oldw || h != oldh) { 
      refitText(this.getText().toString(), w); 
     } 
    } 

} 

Вот пример того, как он может быть использован в XML:

<com.your.package.activity.widget.FontFitTextView 
    android:id="@+id/my_id" 
    android:layout_width="match_parent" 
    android:layout_height="wrap_content" 
    android:gravity="center" 
    android:text="My Text" 
    android:textSize="60sp" /> 

Это будет держать размер шрифта 60SP, пока текст помещается в ширину. Если текст длиннее, это уменьшит размер шрифта. В этом случае высота TextView s также изменится из-за height=wrap_content.

Если вы обнаружили какие-либо ошибки, не стесняйтесь редактировать.

+0

Это не сработало, вид занял ... – PearsonArtPhoto

+0

Как вы его использовали? Я добавил пример того, как я использую его для ответа. Я тестировал его с различными версиями Android, эмуляторами и графическим редактором ADT. – sulai

+0

По существу, я не указывал определенную высоту. Ваш onMeasure позволяет просматривать представление, если он хочет. Мне удалось найти решение, изменив последнюю строку в подпрограмме на 'this.setMeasuredDimension (parentWidth, height);' – PearsonArtPhoto

14

Вот мое решение, которое работает на эмуляторе и телефонах, но не очень хорошо работает в редакторе макетов Eclipse. Он вдохновлен кодом килаки, но размер текста не получен из Paint, а из измерения самого TextView, вызывающего measure(0, 0).

Java-класс:

public class FontFitTextView extends TextView 
{ 
    private static final float THRESHOLD = 0.5f; 

    private enum Mode { Width, Height, Both, None } 

    private int minTextSize = 1; 
    private int maxTextSize = 1000; 

    private Mode mode = Mode.None; 
    private boolean inComputation; 
    private int widthMeasureSpec; 
    private int heightMeasureSpec; 

    public FontFitTextView(Context context) { 
      super(context); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
      this(context, attrs, 0); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs, int defStyle) { 
      super(context, attrs, defStyle); 

      TypedArray tAttrs = context.obtainStyledAttributes(attrs, R.styleable.FontFitTextView, defStyle, 0); 
      maxTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_maxTextSize, maxTextSize); 
      minTextSize = tAttrs.getDimensionPixelSize(R.styleable.FontFitTextView_minTextSize, minTextSize); 
      tAttrs.recycle(); 
    } 

    private void resizeText() { 
      if (getWidth() <= 0 || getHeight() <= 0) 
        return; 
      if(mode == Mode.None) 
        return; 

      final int targetWidth = getWidth(); 
      final int targetHeight = getHeight(); 

      inComputation = true; 
      float higherSize = maxTextSize; 
      float lowerSize = minTextSize; 
      float textSize = getTextSize(); 
      while(higherSize - lowerSize > THRESHOLD) { 
        textSize = (higherSize + lowerSize)/2; 
        if (isTooBig(textSize, targetWidth, targetHeight)) { 
          higherSize = textSize; 
        } else { 
          lowerSize = textSize; 
        } 
      } 
      setTextSize(TypedValue.COMPLEX_UNIT_PX, lowerSize); 
      measure(widthMeasureSpec, heightMeasureSpec); 
      inComputation = false; 
    } 

    private boolean isTooBig(float textSize, int targetWidth, int targetHeight) { 
      setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); 
      measure(0, 0); 
      if(mode == Mode.Both) 
        return getMeasuredWidth() >= targetWidth || getMeasuredHeight() >= targetHeight; 
      if(mode == Mode.Width) 
        return getMeasuredWidth() >= targetWidth; 
      else 
        return getMeasuredHeight() >= targetHeight; 
    } 

    private Mode getMode(int widthMeasureSpec, int heightMeasureSpec) { 
      int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
      int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
      if(widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) 
        return Mode.Both; 
      if(widthMode == MeasureSpec.EXACTLY) 
        return Mode.Width; 
      if(heightMode == MeasureSpec.EXACTLY) 
        return Mode.Height; 
      return Mode.None; 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
      super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
      if(!inComputation) { 
        this.widthMeasureSpec = widthMeasureSpec; 
        this.heightMeasureSpec = heightMeasureSpec; 
        mode = getMode(widthMeasureSpec, heightMeasureSpec); 
        resizeText(); 
      } 
    } 

    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { 
      resizeText(); 
    } 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) { 
      if (w != oldw || h != oldh) 
        resizeText(); 
    } 

    public int getMinTextSize() { 
      return minTextSize; 
    } 

    public void setMinTextSize(int minTextSize) { 
      this.minTextSize = minTextSize; 
      resizeText(); 
    } 

    public int getMaxTextSize() { 
      return maxTextSize; 
    } 

    public void setMaxTextSize(int maxTextSize) { 
      this.maxTextSize = maxTextSize; 
      resizeText(); 
    } 
} 

Файл атрибут XML:

<resources> 
    <declare-styleable name="FontFitTextView"> 
     <attr name="minTextSize" format="dimension" /> 
     <attr name="maxTextSize" format="dimension" /> 
    </declare-styleable> 
</resources> 

Проверить my github последнюю версию этого класса. Надеюсь, это может быть полезно для кого-то. Если обнаружена ошибка или код нуждается в объяснении, не стесняйтесь открыть проблему в Github.

+0

Это работает префект на моей Galaxy Nexus, но у меня проблемы с устройствами с маленькими экранами. –

+0

В чем проблема на устройствах с маленькими экранами? – yDelouis

+0

Извините, проблема не в маленьких экранах, ваш взгляд не работает на всех устройствах с Android 4.0, включая эмулятор. Я открыл новую проблему в своем GitHub –

2

Я работаю над улучшением отличного решения на скоростном плане и придумал это. Он управляет высотой, включая настройку поля таким образом, чтобы текст был правильно центрирован по вертикали.

Эта функция использует ту же функцию, что и для получения ширины, поскольку она, по-видимому, работает лучше всего, но использует другую функцию для получения высоты, поскольку высота не предоставляется нигде. Есть некоторые исправления, которые нужно сделать, но я понял способ сделать это, глядя на глаза приятным.

import android.content.Context; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.widget.TextView; 

public class FontFitTextView extends TextView { 

    public FontFitTextView(Context context) { 
     super(context); 
     initialize(); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialize(); 
    } 

    private void initialize() { 
     mTestPaint = new Paint(); 
     mTestPaint.set(this.getPaint()); 

     //max size defaults to the initially specified text size unless it is too small 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth,int textHeight) 
    { 
     if (textWidth <= 0) 
      return; 
     int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 
     int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); 
     float hi = Math.min(targetHeight,100); 
     float lo = 2; 
     final float threshold = 0.5f; // How close we have to be 

     Rect bounds = new Rect(); 

     mTestPaint.set(this.getPaint()); 

     while((hi - lo) > threshold) { 
      float size = (hi+lo)/2; 
      mTestPaint.setTextSize(size); 
      mTestPaint.getTextBounds(text, 0, text.length(), bounds); 
      if((mTestPaint.measureText(text)) >= targetWidth || (1+(2*(size+(float)bounds.top)-bounds.bottom)) >=targetHeight) 
       hi = size; // too big 
      else 
       lo = size; // too small 
     } 
     // Use lo so that we undershoot rather than overshoot 
     this.setTextSize(TypedValue.COMPLEX_UNIT_PX,(float) lo); 

    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int parentHeight = MeasureSpec.getSize(heightMeasureSpec); 
     int height = getMeasuredHeight(); 
     refitText(this.getText().toString(), parentWidth,height); 
     this.setMeasuredDimension(parentWidth, height); 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { 
     refitText(text.toString(), this.getWidth(),this.getHeight()); 
    } 

    @Override 
    protected void onSizeChanged (int w, int h, int oldw, int oldh) { 

     if (w != oldw) { 
      refitText(this.getText().toString(), w,h); 
     } 
    } 

    //Attributes 
    private Paint mTestPaint; 
} 
12

Большое спасибо https://stackoverflow.com/users/234270/speedplane. Отличный ответ!

Вот улучшенная версия его ответ, который также заботиться о высоте и поставляется с maxFontSize атрибут, чтобы ограничить размер шрифта (был полезным в моем случае, поэтому я хотел бы поделиться):

package com.<your_package>; 

import android.content.Context; 
import android.content.res.TypedArray; 
import android.graphics.Paint; 
import android.graphics.Rect; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.widget.TextView; 


public class FontFitTextView extends TextView 
{ 

    private Paint mTestPaint; 
    private float maxFontSize; 
    private static final float MAX_FONT_SIZE_DEFAULT_VALUE = 20f; 

    public FontFitTextView(Context context) 
    { 
     super(context); 
     initialise(context, null); 
    } 

    public FontFitTextView(Context context, AttributeSet attributeSet) 
    { 
     super(context, attributeSet); 
     initialise(context, attributeSet); 
    } 

    public FontFitTextView(Context context, AttributeSet attributeSet, int defStyle) 
    { 
     super(context, attributeSet, defStyle); 
     initialise(context, attributeSet); 
    } 

    private void initialise(Context context, AttributeSet attributeSet) 
    { 
     if(attributeSet!=null) 
     { 
      TypedArray styledAttributes = context.obtainStyledAttributes(attributeSet, R.styleable.FontFitTextView); 
      maxFontSize = styledAttributes.getDimension(R.styleable.FontFitTextView_maxFontSize, MAX_FONT_SIZE_DEFAULT_VALUE); 
      styledAttributes.recycle(); 
     } 
     else 
     { 
      maxFontSize = MAX_FONT_SIZE_DEFAULT_VALUE; 
     } 

     mTestPaint = new Paint(); 
     mTestPaint.set(this.getPaint()); 
     //max size defaults to the initially specified text size unless it is too small 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth, int textHeight) 
    { 
     if (textWidth <= 0) 
      return; 
     int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 
     int targetHeight = textHeight - this.getPaddingTop() - this.getPaddingBottom(); 
     float hi = maxFontSize; 
     float lo = 2; 
//  final float threshold = 0.5f; // How close we have to be 
     final float threshold = 1f; // How close we have to be 

     mTestPaint.set(this.getPaint()); 

     Rect bounds = new Rect(); 

     while ((hi - lo) > threshold) 
     { 
      float size = (hi + lo)/2; 
      mTestPaint.setTextSize(size); 

      mTestPaint.getTextBounds(text, 0, text.length(), bounds); 

      if (bounds.width() >= targetWidth || bounds.height() >= targetHeight) 
       hi = size; // too big 
      else 
       lo = size; // too small 

//   if (mTestPaint.measureText(text) >= targetWidth) 
//    hi = size; // too big 
//   else 
//    lo = size; // too small 
     } 
     // Use lo so that we undershoot rather than overshoot 
     this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int height = getMeasuredHeight(); 
     refitText(this.getText().toString(), parentWidth, height); 
     this.setMeasuredDimension(parentWidth, height); 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) 
    { 
     refitText(text.toString(), this.getWidth(), this.getHeight()); 
    } 

    @Override 
    protected void onSizeChanged(int w, int h, int oldw, int oldh) 
    { 
     if (w != oldw) 
     { 
      refitText(this.getText().toString(), w, h); 
     } 
    } 
} 

Соответствующий файл /res/values/attr.xml:

<?xml version="1.0" encoding="utf-8"?> 
<resources> 

    <declare-styleable name="FontFitTextView"> 
     <attr name="maxFontSize" format="dimension" /> 
    </declare-styleable> 

</resources> 

Пример:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    xmlns:res-auto="http://schemas.android.com/apk/res-auto" 
    android:id="@+id/home_Layout" 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:background="@drawable/background" 
    tools:ignore="ContentDescription" > 
... 

<com.<your_package>.FontFitTextView 
        android:layout_width="match_parent" 
        android:layout_height="wrap_content" 
        android:singleLine="true" 
        android:text="Sample Text" 
        android:textSize="28sp" 
        res-auto:maxFontSize="35sp"/> 

... 
</RelativeLayout> 

Чтобы использовать новый атрибут maxFontSize, не забудьте добавить xmlns:res-auto="http://schemas.android.com/apk/res-auto" как показано в примере.

+0

Извините, но она по-прежнему не работает во всех случаях. Я также думаю, что он не обрабатывает многострочные строки правильно. –

+0

Oh .. Не могли бы вы дать мне случай, для которого он не работает? (Многострочная линия не поддерживается, вы правы) – Pascal

+0

Многие случаи. Я создал случайный тестер, чтобы доказать, что это правильно. Вот один пример: ширина вида: 317px, высота: 137px, текст: «q7Lr». Я вижу следующее: http://tinypic.com/view.php?pic=2dv5yf9&s=6. вот пример проекта, который я сделал: https://mega.co.nz/#!w9QRyBaB!J3KdnyTuNXRsYH9Hk5f4nBBXkRM1IAQnV8-53ckRooY. Я думаю, что такое представление должно обрабатывать несколько строк, поддерживать любой размер шрифтов, обрабатывать высоту, а не только ширину ... К сожалению, ни один из найденных мной образцов не работал хорошо. –

0
/* get your context */ 
Context c = getActivity().getApplicationContext(); 

LinearLayout l = new LinearLayout(c); 
l.setOrientation(LinearLayout.VERTICAL); 
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, 0); 

l.setLayoutParams(params); 
l.setBackgroundResource(R.drawable.border); 

TextView tv=new TextView(c); 
tv.setText(" your text here"); 

/* set typeface if needed */ 
Typeface tf = Typeface.createFromAsset(c.getAssets(),"fonts/VERDANA.TTF"); 
tv.setTypeface(tf); 

// LayoutParams lp = new LayoutParams(); 

tv.setTextColor(Color.parseColor("#282828")); 

tv.setGravity(Gravity.CENTER | Gravity.BOTTOM); 
// tv.setLayoutParams(lp); 

tv.setTextSize(20); 
l.addView(tv); 

return l; 
+1

Просто читая ваш код, я не думаю, что это сработает. Помните, вопрос состоял в том, чтобы автоматически регулировать размер шрифта текстового вида, чтобы вписаться в представление. Вы устанавливаете фиксированный размер шрифта. – sulai

0

Это должно быть простое решение:

public void correctWidth(TextView textView, int desiredWidth) 
{ 
    Paint paint = new Paint(); 
    Rect bounds = new Rect(); 

    paint.setTypeface(textView.getTypeface()); 
    float textSize = textView.getTextSize(); 
    paint.setTextSize(textSize); 
    String text = textView.getText().toString(); 
    paint.getTextBounds(text, 0, text.length(), bounds); 

    while (bounds.width() > desiredWidth) 
    { 
     textSize--; 
     paint.setTextSize(textSize); 
     paint.getTextBounds(text, 0, text.length(), bounds); 
    } 

    textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize); 
} 
0

Продлить TextView и переопределить OnDraw с кодом ниже. Он будет сохранять пропорции текста, но размер его заполняет пространство. Вы можете легко изменить код, чтобы растянуть, если необходимо.

@Override 
    protected void onDraw(@NonNull Canvas canvas) { 
    TextPaint textPaint = getPaint(); 
    textPaint.setColor(getCurrentTextColor()); 
    textPaint.setTextAlign(Paint.Align.CENTER); 
    textPaint.drawableState = getDrawableState(); 

    String text = getText().toString(); 
    float desiredWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2; 
    float desiredHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2; 
    float textSize = textPaint.getTextSize(); 

    for (int i = 0; i < 10; i++) { 
     textPaint.getTextBounds(text, 0, text.length(), rect); 
     float width = rect.width(); 
     float height = rect.height(); 

     float deltaWidth = width - desiredWidth; 
     float deltaHeight = height - desiredHeight; 

     boolean fitsWidth = deltaWidth <= 0; 
     boolean fitsHeight = deltaHeight <= 0; 

     if ((fitsWidth && Math.abs(deltaHeight) < 1.0) 
      || (fitsHeight && Math.abs(deltaWidth) < 1.0)) { 
     // close enough 
     break; 
     } 

     float adjustX = desiredWidth/width; 
     float adjustY = desiredHeight/height; 

     textSize = textSize * (adjustY < adjustX ? adjustY : adjustX); 

     // adjust text size 
     textPaint.setTextSize(textSize); 
    } 
    float x = desiredWidth/2f; 
    float y = desiredHeight/2f - rect.top - rect.height()/2f; 
    canvas.drawText(text, x, y, textPaint); 
    } 
0

я написал короткий вспомогательный класс, который делает TextView посадки в пределах определенной ширины и добавляет ellipsize «...» в конце, если минимальный TEXTSIZE не может быть достигнут.

Имейте в виду, что текст уменьшает размер текста до тех пор, пока он не подходит или пока не будет достигнут минимальный размер текста. Чтобы протестировать с большими размерами, перед тем, как вызвать метод справки, установите текст в большом количестве.

Он принимает пикселы, так что если вы используете значения из Димена, вы можете назвать это так:


float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size); 
float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width); 
WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx); 

Это класс я использую:


public class WidgetUtils { 

    public static void fitText(TextView textView, String text, float minTextSizePx, float maxWidthPx) { 
     textView.setEllipsize(null); 
     int size = (int)textView.getTextSize(); 
     while (true) { 
      Rect bounds = new Rect(); 
      Paint textPaint = textView.getPaint(); 
      textPaint.getTextBounds(text, 0, text.length(), bounds); 
      if(bounds.width() < maxWidthPx){ 
       break; 
      } 
      if (size <= minTextSizePx) { 
       textView.setEllipsize(TextUtils.TruncateAt.END); 
       break; 
      } 
      size -= 1; 
      textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, size); 
     } 
    } 
} 
+0

Поддерживает ли это несколько строк текста? – Roymunson

0

Если задана трансформация, такая как allCaps, подход с использованием скоростной плоскости является ошибкой. Я зафиксировал его, в результате чего в следующем коде (извините, моя репутация не позволяет мне добавить это в качестве комментария к решению speedplane в):

import android.content.Context; 
import android.graphics.Paint; 
import android.util.AttributeSet; 
import android.util.TypedValue; 
import android.widget.TextView; 

public class FontFitTextView extends TextView { 

    public FontFitTextView(Context context) { 
     super(context); 
     initialise(); 
    } 

    public FontFitTextView(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     initialise(); 
    } 

    private void initialise() { 
     mTestPaint = new Paint(); 
     mTestPaint.set(this.getPaint()); 
     //max size defaults to the initially specified text size unless it is too small 
    } 

    /* Re size the font so the specified text fits in the text box 
    * assuming the text box is the specified width. 
    */ 
    private void refitText(String text, int textWidth) 
    { 
     if (getTransformationMethod() != null) { 
      text = getTransformationMethod().getTransformation(text, this).toString(); 
     } 

     if (textWidth <= 0) 
      return; 
     int targetWidth = textWidth - this.getPaddingLeft() - this.getPaddingRight(); 
     float hi = 100; 
     float lo = 2; 
     final float threshold = 0.5f; // How close we have to be 

     mTestPaint.set(this.getPaint()); 

     while((hi - lo) > threshold) { 
      float size = (hi+lo)/2; 
      if(mTestPaint.measureText(text) >= targetWidth) 
       hi = size; // too big 
      else 
       lo = size; // too small 
     } 
     // Use lo so that we undershoot rather than overshoot 
     this.setTextSize(TypedValue.COMPLEX_UNIT_PX, lo); 
    } 

    @Override 
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
    { 
     super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
     int parentWidth = MeasureSpec.getSize(widthMeasureSpec); 
     int height = getMeasuredHeight(); 
     refitText(this.getText().toString(), parentWidth); 
     this.setMeasuredDimension(parentWidth, height); 
    } 

    @Override 
    protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) { 
     refitText(text.toString(), this.getWidth()); 
    } 

    @Override 
    protected void onSizeChanged (int w, int h, int oldw, int oldh) { 
     if (w != oldw) { 
      refitText(this.getText().toString(), w); 
     } 
    } 

    //Attributes 
    private Paint mTestPaint; 
} 
0

Я не известное это правильный путь или не бть его работу ... возьмите свой взгляд и проверьте OnGlobalLayoutListener() и получите текстовое поле linecount, а затем установите textSize.

yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 
     @Override 
     public void onGlobalLayout() { 
      if (textView.getLineCount()>=3) { 
       textView.setTextSize(20); 
      }else{ 
       //add somthing 
       } 
     } 
    }); 

Его очень просто мало код строки ..

0

У меня была боль в моих проектах тааак долго, пока я не нашел эту библиотеку:

compile 'me.grantland:autofittextview:0.2.+' 

Вам просто нужно добавить XML с помощью ваши потребности и все сделано. Например:

<me.grantland.widget.AutofitTextView 
android:layout_width="match_parent" 
android:layout_height="wrap_content" 
android:singleLine="true" 
android:maxLines="2" 
android:textSize="40sp" 
autofit:minTextSize="16sp" 
/> 
2

Теперь вы можете сделать это без участия сторонней библиотеки или виджета. Он встроен в TextView в API уровня 26.Добавьте android:autoSizeTextType="uniform" к вашему TextView и установите для него высоту. Это все.

https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html

<?xml version="1.0" encoding="utf-8"?> 
<TextView 
    android:layout_width="match_parent" 
    android:layout_height="200dp" 
    android:autoSizeTextType="uniform" /> 

Вы также можете использовать TextViewCompat для совместимости.

Смежные вопросы