tamaño - modificar textview android studio
Cómo ajustar el tamaño de fuente del texto para que se ajuste a textview (21)
¿Hay alguna forma en Android para ajustar el texto en una vista de texto para que se ajuste al espacio que ocupa?
Por ejemplo, estoy usando TableLayout
y agregando varios TextView
a cada fila. Como no quiero que TextView
s envuelva el texto, prefiero ver que reduce el tamaño de la fuente del contenido.
¿Algunas ideas?
He intentado measureText
, pero como no sé el tamaño de la columna, parece problemático utilizarla. Este es el código donde quiero cambiar el tamaño de la fuente a algo que se ajuste
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());
Ahora puede hacer esto sin una biblioteca de terceros o un widget. Está integrado en TextView en el nivel 26 de la API. Agrega android:autoSizeTextType="uniform"
a tu TextView
y establece su altura. Eso es todo.
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" />
También puede usar TextViewCompat
para compatibilidad.
Aquí está mi solución que funciona en emulador y teléfonos pero no muy bien en el editor de diseño de Eclipse. Está inspirado en el código de kilaka, pero el tamaño del texto no se obtiene de la pintura, sino de la medición de la measure(0, 0)
llamada de TextView measure(0, 0)
.
La clase de 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();
}
}
El archivo de atributos XML:
<resources>
<declare-styleable name="FontFitTextView">
<attr name="minTextSize" format="dimension" />
<attr name="maxTextSize" format="dimension" />
</declare-styleable>
</resources>
Comprueba mi github para la última versión de esta clase. Espero que pueda ser útil para alguien. Si se encuentra un error o si el código necesita una explicación, no dude en abrir un problema en Github.
Encontré que lo siguiente funciona bien para mí. No realiza bucles y representa tanto la altura como el ancho. Tenga en cuenta que es importante especificar la unidad PX al llamar a setTextSize en la vista. ¡Gracias a la sugerencia en una publicación anterior para esto!
Paint paint = adjustTextSize(getPaint(), numChars, maxWidth, maxHeight);
setTextSize(TypedValue.COMPLEX_UNIT_PX,paint.getTextSize());
Aquí está la rutina que uso, pasando el getPaint () desde la vista. Se utiliza una cadena de 10 caracteres con un carácter "ancho" para estimar el ancho independiente de la cadena real.
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;
}
Esta debería ser una solución simple:
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);
}
Esta es FontFitTextView de FontFitTextView
, pero solo disminuye el tamaño de la fuente si es necesario para que el texto encaje, y mantiene su tamaño de fuente de lo contrario. No aumenta el tamaño de la fuente para adaptarse a la altura.
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);
}
}
}
Aquí hay un ejemplo de cómo se puede usar en 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" />
Esto mantendría el tamaño de fuente en 60sp siempre que el texto se ajuste al ancho. Si el texto es más largo, disminuirá el tamaño de la fuente. En este caso, la altura de TextView
también cambiará debido a height=wrap_content
.
Si encuentra algún error, no dude en editarlo.
Funciona con modificación
Debe establecer el tamaño de vista de texto de esta manera porque de lo contrario setTextSize supone que el valor está en unidades SP:
setTextSize(TypedValue.COMPLEX_UNIT_PX, trySize);
Y necesita agregar explícitamente este código.
@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);
}
He escrito una clase que amplía TextView y lo hace. Simplemente usa measureText como sugieres. Básicamente tiene un tamaño de texto máximo y un tamaño de texto mínimo (que se puede cambiar) y simplemente recorre los tamaños entre ellos en decrementos de 1 hasta que encuentra el más grande que se ajuste. No particularmente elegante, pero no sé de ninguna otra manera.
Aquí está el código:
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;
}
He estado trabajando para mejorar la excelente solución de speedplane, y se me ocurrió esto. Gestiona la altura, incluido el establecimiento de un margen tal que el texto debe estar centrado verticalmente.
Utiliza la misma función para obtener el ancho, ya que parece funcionar mejor, pero utiliza una función diferente para obtener la altura, ya que la altura no se proporciona en ninguna parte. Hay algunas correcciones que deben hacerse, pero descubrí una manera de hacerlo, mientras me veía agradable a la vista.
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;
}
Inspirado por los carteles anteriores, quería compartir mi solución. Funciona con un factor de escala que se aplica al tamaño de fuente anterior para que se ajuste al espacio disponible. Además de evitar el comportamiento inesperado del método TextViews onDraw, simplemente dibuja el texto por sí mismo.
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);
}
}
La siguiente solución incorpora todas las sugerencias aquí. Comienza con lo que originalmente fue publicado por Dunni. Utiliza una búsqueda binaria como gjpc, pero es un poco más legible. También incluye las correcciones de errores de gregm y una corrección de errores por mi cuenta.
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;
}
Ligera modificación en 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);
}
Y búsqueda binaria en 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);
}
}
Muchas gracias a . ¡Gran respuesta!
Aquí hay una versión mejorada de su respuesta que también se ocupa de la altura y viene con un atributo maxFontSize para limitar el tamaño de la fuente (fue útil en mi caso, así que quería compartirlo):
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);
}
}
}
Archivo /res/values/attr.xml correspondiente:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="FontFitTextView">
<attr name="maxFontSize" format="dimension" />
</declare-styleable>
</resources>
Ejemplo:
<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>
Para usar el nuevo atributo maxFontSize
, no olvide agregar xmlns:res-auto="http://schemas.android.com/apk/res-auto"
como se muestra en el ejemplo.
Tuve el mismo problema y escribí una clase que parece funcionar para mí. Básicamente, utilicé un diseño estático para dibujar el texto en un lienzo por separado y volver a medir hasta que encuentre un tamaño de letra que se ajuste. Puedes ver la clase publicada en el tema a continuación. Espero que ayude.
Usé una variante de la solución Dunni anterior, pero ese código en particular no funcionó para mí. En particular, cuando se intenta utilizar el objeto Paint configurado para tener los rasgos del objeto Paint de la vista, y luego llamar a measureText (), no devuelve el mismo valor que llamar directamente al objeto Paint de la vista. Quizás haya algunas diferencias en la forma en que se configuran mis puntos de vista que hacen que el comportamiento sea diferente.
Mi solución fue utilizar directamente Paint de la vista, aunque podría haber algunas penalizaciones de rendimiento al cambiar el tamaño de la fuente para la vista varias veces.
Extend TextView and override onDraw with the code below. It will keep text aspect ratio but size it to fill the space. You could easily modify code to stretch if necessary.
@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);
}
Google ya hizo esta característica.
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:autoSizeTextType="uniform" />
https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview.html
I don''t known this is correct way or not bt its working ...take your view and check OnGlobalLayoutListener() and get textview linecount then set textSize.
yourView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (textView.getLineCount()>=3) {
textView.setTextSize(20);
}else{
//add somthing
}
}
});
Its very simple few line code..
I had this pain in my projects for soooo long until I found this library:
compile ''me.grantland:autofittextview:0.2.+''
You just need to add the xml by your needs and it''s done. Por ejemplo:
<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"
/>
I wrote a short helper class that makes a textview fit within a certain width and adds ellipsize "..." at the end if the minimum textsize cannot be achieved.
Keep in mind that it only makes the text smaller until it fits or until the minimum text size is reached. To test with large sizes, set the textsize to a large number before calling the help method.
It takes Pixels, so if you are using values from dimen, you can call it like this:
float minTextSizePx = getResources().getDimensionPixelSize(R.dimen.min_text_size);
float maxTextWidthPx = getResources().getDimensionPixelSize(R.dimen.max_text_width);
WidgetUtils.fitText(textView, text, minTextSizePx, maxTextWidthPx);
This is the class I use:
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);
}
}
}
If a tranformation like allCaps is set, speedplane''s approach is buggy. I fixed it, resulting in the following code (sorry, my reputation does not allow me to add this as a comment to speedplane''s solution):
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;
}
/* 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;