Saltar al contenido
Codifíca.me | Desarrollo web | Programación

Ajustar TextView Android

20 noviembre, 2013

Cómo expandir el texto de un TextView para ajustarlo al alto y ancho aumentando el tamaño de la fuente en Android

He estado buscando sobre cómo aumentar la fontsize de un TextView en Android para que quede lo más grande posible dentro de un TextView, sin aumentar el tamaño de dicho TextView. Para mi sorpresa, no hay una manera directa, sino que hay que ir probando los tamaños de las fuentes uno a uno, hasta ver cuál es el adecuado. Obviamente, esto no se hará mostrándolo al usuario, aunque sería gracioso/cutre ver como va aumentando la fuente hasta ajustarse.

Algoritmos: he visto varios por StackOverflow que ajusta el ancho perfectamente, con varias variantes si se pasa de largo, o lo divide en varias lineas(obligando al TextView tener una altura de wrap_content y no fija porque si no, no se ve el texto), o añadiendo puntos suspensivos si se ve que se va a partir en varias lineas e incluso uno que usaba un getMeasuredHeight() para tomar la altura y funcionaba perfectamente en casi todos los dispositivos. Sin embargo, en un Samsung GT-N7102, dicho método devolvía datos incoherentes y tuve que buscar otro camino.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
/**
 * Mod of Andreas Krings AutoScaleTextView in order to support resizing according to the TextView height.
 * It works perfectly in all the devices I have tried (Nexus 4, HTC One, HTC Wildfire S, GT-N7102)
 * 
 * @author Héctor Berlanga - www.codifica.me
 * @version 1.1
 * 
 */
/**
 * A custom Text View that lowers the text size when the text is to big for the
 * TextView. Modified version of one found on stackoverflow
 * 
 * @author Andreas Krings - www.ankri.de
 * @version 1.0
 * 
 */
public class AutoScaleTextView extends TextView {
    private Paint textPaint;
 
    private float preferredTextSize;
    private float minTextSize;
 
    public AutoScaleTextView(Context context) {
        this(context, null);
    }
 
    public AutoScaleTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.textPaint = new Paint();
        this.minTextSize = 2;
        this.preferredTextSize = 200;
    }
 
    /**
     * Set the minimum text size for this view
     * 
     * @param minTextSize
     *            The minimum text size
     */
    public void setMinTextSize(float minTextSize) {
        this.minTextSize = minTextSize;
    }
 
    /**
     * Resize the text so that it fits
     * 
     * @param text
     *            The text. Neither <code>null</code> nor empty.
     * @param textWidth
     *            The width of the TextView. > 0
     */
    private void refitText(String text, int textWidth) {
        this.minTextSize = 2;
        this.preferredTextSize = 200;
        if (textWidth <= 0 || text == null || text.length() == 0)
            return;
 
        // the width
        int targetWidth = textWidth - this.getPaddingLeft()
            - this.getPaddingRight();
        int targetHeight = this.getHeight() - this.getPaddingTop()
            - this.getPaddingBottom();
 
        final float threshold = 0.5f; // How close we have to be
 
        this.textPaint.set(this.getPaint());
 
        while ((this.preferredTextSize - this.minTextSize) > threshold) {
            float size = (this.preferredTextSize + this.minTextSize) / 2;
            this.textPaint.setTextSize(size);
            int textHeight = getTextHeight(text, this.getPaint(), targetWidth,
                size);
 
            if (this.textPaint.measureText(text) >= targetWidth
                || textHeight >= targetHeight) {
            this.preferredTextSize = size; // too big
            } else {
            this.minTextSize = size; // too small
            }
        }
        // Use min size so that we undershoot rather than overshoot
        this.setTextSize(TypedValue.COMPLEX_UNIT_PX, this.minTextSize);
    }
 
    @Override
    protected void onTextChanged(final CharSequence text, final int start,
        final int before, final int after) {
        this.refitText(text.toString(), this.getWidth());
    }
 
    @Override
    protected void onSizeChanged(int width, int height, int oldwidth,
        int oldheight) {
        if (width != oldwidth)
            this.refitText(this.getText().toString(), width);
    }
 
    // Set the text size of the text paint object and use a static layout to
    // render text off screen before measuring
    private int getTextHeight(CharSequence source, TextPaint originalPaint,
        int width, float textSize) {
        // Update the text paint object
        // modified: make a copy of the original TextPaint object for measuring
        // (apparently the object gets modified while measuring, see also the
        // docs for TextView.getPaint() (which states to access it read-only)
        TextPaint paint = new TextPaint(originalPaint);
        // Update the text paint object
        paint.setTextSize(textSize);
        // Measure using a static layout
        StaticLayout layout = new StaticLayout(source, paint, width,
            Alignment.ALIGN_CENTER, 1.0f, 0.0f, true);
    return layout.getHeight();
    }
}

Al grano, he modificado la clase AutoScaleTextView de Andreas Krings, que funciona perfectamente ajustandose al ancho del TextView, pero que no tiene en cuenta la altura de dicho TextView.

El funcionamiento es el siguiente: cada vez que se cambia el texto o el tamaño, se recalcula el fontsize. Para recalcular el máximo fontsize permitido, se creará un TextPaint, que no se mostrará al usuario, se le cambiará el fontsize y se probará que encaje dentro de las dimensiones iniciales. El ancho se obtendrá directamente desde measureText(). Para calcular el alto se medirá la altura de un StaticLayout creado a partir del TextPaint.

Como he dicho, he visto formas más sencillas de realizar lo mismo, todas recalculando el fontsize a prueba y error, pero en mi caso, no me funcionaban en todos los dispositivos.

Espero que, si estáis en mi mismo caso, esta clase pueda ayudaros.

Lo podéis descargar en pastebin también