web-dev-qa-db-fra.com

Comment ajuster automatiquement la taille du texte sur une TextView multilingue en fonction de la vue max dimensions?

J'ai cherché un moyen d'auto-adaptation d'un texte à l'intérieur d'un TextView. À travers ma recherche, j'ai trouvé de nombreuses solutions comme:

Mais comme beaucoup d'autres ceux-ci ne résout pas mon problème. Ils ne fonctionnent pas comme prévu lorsque nous utilisons un TextView avec Multiline.

Fondamentalement, mon objectif est celui-ci:

Phase 1Phase 2Phase 3Phase 4

Comme vous pouvez le constater, le texte redimensionne la largeur, la hauteur et accorde également l'attention sur la pause de la ligne, créant une vision de TextView multilingue. Peut également être capable de changer la police de caractères.

Une de mes idées pour résoudre c'était quelque chose comme ça:

int size = CONSTANT_MAX_SIZE;
TextView tv = (TextView) findViewById(R.id.textview1)
while(Math.abs(tv.getMeasuredHeight()) >= TEXTVIEW_MAX_HEIGHT) {
    size--;
    tv.setTextSize(size);
    tv.measure(MeasureSpec.UNSPECIFIED,MeasureSpec.UNSPECIFIED);
    i++;
}

Constant_max_Size est une constante qui définit la taille maximale de la police (Textsize Propriété dans un TextView)

Textview_max_height est une constante qui définit la taille maximale que le TextView peut avoir.

Cela a été appelé à chaque fois que le texte dans la TextView a été modifié.

TextView XML était quelque chose comme ceci:

<TextView
     Android:id="@+id/textview1"
     Android:layout_width="200dp"
     Android:layout_height="wrap_content"
     Android:singleLine="false"
     Android:inputType="textMultiLine"
     Android:text=""
     Android:textSize="150sp" />

Étant donné que la largeur serait limitée dans le XML, seule la hauteur de la vue doit être envisagée car régler le réglage Android créerait automatiquement une multiligne si nécessaire.

Bien que ce soit une solution potentielle ne fonctionne pas parfaitement (loin de celle-ci) et il ne supporte pas de redimensionner (lorsque vous supprimez le texte).

Des sugestions et/ou des idées?

21
nunofmendes

Bien que j'ai attendu une solution possible à cette question, j'ai expérimenté et essaye de le comprendre.

La solution la plus proche de ce problème était basée sur une méthode de la peinture.

Fondamentalement, la peinture a une méthode appelée "breaktext" qui:

public int breaktext (texte de charcuterie, int start, int. Int-fin, mesure booléenne pour le flotteur Maxwidth, float [] Mesuréwidth)

Ajouté au niveau API 1

Mesurez le texte, arrêtez-vous tôt si la largeur mesurée dépasse maxwidth. Renvoyez le nombre de caractères mesurés et si la mesure de la mesure n'est pas nulle, revenez-la de la largeur réelle mesurée.

Je combine ça avec la peinture "gettextbounds" qui:

public Void GetTextbounds (String Text, int Démarrer, int. int.

Ajouté au niveau API 1

Retour dans les limites (allouées par l'appelant) Le plus petit rectangle qui renferme tous les> caractères, avec une origine implicite à (0,0).

Donc maintenant, je peux obtenir le nombre de caractères qui correspondent à une largeur donnée et à la hauteur de ces caractères.

En utilisant un certain temps, vous pouvez continuer à déplacer les caractères de retrait de la chaîne que vous souhaitez mesurer et obtenir le nombre de lignes (en utilisant un moment (index <string.length)) et multipliez que la hauteur obtenue dans les gettextbounds.

De plus, vous devrez ajouter une hauteur variable pour chaque deux lignes qui représentent l'espace entre les lignes (qui ne sont pas comptées dans les gettextbounds).

En tant que code exemple, la fonction pour connaître la hauteur d'un texte à plusieurs lignes est quelque chose comme ceci:

public int getHeightOfMultiLineText(String text,int textSize, int maxWidth) {
    Paint = new TextPaint();
    Paint.setTextSize(textSize);
    int index = 0;
    int linecount = 0;
    while(index < text.length()) {
        index += Paint.breakText(text,index,text.length,true,maxWidth,null);
        linecount++;
    }

    Rect bounds = new Rect();
    Paint.getTextBounds("Yy", 0, 2, bounds);
    // obtain space between lines
    double lineSpacing = Math.max(0,((lineCount - 1) * bounds.height()*0.25)); 

    return (int)Math.floor(lineSpacing + lineCount * bounds.height());

Remarque: la variable MaxWidth est en pixels

Ensuite, vous devrez appeler cette méthode à l'intérieur d'un moment pour déterminer quelle est la taille de la police maximale de cette hauteur. Un exemple de code serait:

textSize = 100;
int maxHeight = 50;
while(getHeightOfMultiLineText(text,textSize,maxWidth) > maxHeight)
  textSize--;

Malheureusement, c'était le seul (autant que je sache), j'ai pu réaliser l'aspect des images ci-dessus.

J'espère que cela peut être utile à quiconque tente de surmonter cet obstacle.

17
nunofmendes

enter image description hereenter image description hereenter image description here

Un peu de réponse améliorée de Phamducgiam. Il montre du texte avec une taille déjà équipée, pas de Skretchs après avoir montré. Ça a l'air laid dans l'éditeur en raison du démarrage de 100 textes, mais fonctionne comme il se doit en cours d'exécution. Voici le code:

import Android.content.Context;
import Android.text.TextPaint;
import Android.util.AttributeSet;
import Android.util.TypedValue;
import Android.widget.TextView;

public class FontFitTextView extends TextView {

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

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

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

    @Override
    protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // start decreasing font size from 100
        setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);

        // calculate dimensions. don't forget about padding
        final float maxWidth = getWidth()-(getPaddingLeft()+getPaddingRight());
        final float maxHeight = getHeight()-(getPaddingTop()+getPaddingBottom());

        if (maxWidth < 1.0f || maxHeight < 1.0f) {
            return;
        }

        CharSequence text = getText();
        int lineCount = getLineCount(maxWidth, text);
        float height = getHeight(lineCount);
        // keep decreasing font size until it fits
        while (height > maxHeight) {
            final float textSize = getTextSize();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
            height = getHeight(getLineCount(maxWidth, getText()));
        }
        // show fitted textView
        requestLayout();
    }

    private float getHeight(int lineCount) {
        return lineCount * getLineHeight() + (lineCount > 0 ? (lineCount - 1) * getPaint().getFontSpacing() : 0);
    }

    private int getLineCount(float maxWidth, CharSequence text) {
        int lineCount = 0;
        int index = 0;
        final TextPaint Paint = getPaint();
        while (index < text.length()) {
            index += Paint.breakText(text, index, text.length(), true, maxWidth, null);
            lineCount++;
        }
        return lineCount;
    }
}
2
Red Hot Chili Coder

merci pour votre solution, vous êtes Superman! J'ai mis en place votre code dans la classe dérivée de TextView. Le code est converti en code C # pour Xamarin Android. Vous pouvez facilement convertir en Java si vous avez besoin (supprimer le premier constructeur de Java).

public class AutoFitTextView : TextView
{
    public AutoFitTextView(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer)
        : base(javaReference, transfer)
    {
    }

    public AutoFitTextView(Context context)
        : base(context)
    {

    }

    public AutoFitTextView(Context context, IAttributeSet attrs)
        : base(context, attrs)
    {

    }


    public void ResizeFontSize()
    {
        int textSize = 50;
        int maxHeight = this.Height;
        while (GetHeightOfMultiLineText(this.Text, textSize, this.Width) > maxHeight)
        {
            textSize--;
        }
        float scaleFactor = Context.Resources.DisplayMetrics.ScaledDensity;
        float additionalFactor = 1.2f;

        TextSize = ((float)(textSize / (additionalFactor * scaleFactor)));
    }


    private int GetHeightOfMultiLineText(string text, int textSize, int maxWidth)
    {
        TextPaint Paint = new TextPaint();
        Paint.TextSize = textSize;
        int index = 0;
        int lineCount = 0;
        while (index < text.Length)
        {
            index += Paint.BreakText(text, index, text.Length, true, maxWidth, null);
            lineCount++;
        }

        Rect bounds = new Rect();
        Paint.GetTextBounds("Yy", 0, 2, bounds);
        // obtain space between lines
        double lineSpacing = Math.Max(0, ((lineCount - 1) * bounds.Height() * 0.25));

        return (int)Math.Floor(lineSpacing + lineCount * bounds.Height());
    }

}

Voici le code AXML

        <touchtest.AutoFitTextView
        Android:layout_width="match_parent"
        Android:layout_height="match_parent"
        Android:id="@+id/tvLabel2"
        Android:singleLine="false"
        Android:inputType="textMultiLine"
        Android:text="I am here To test this text" />
1
Xusan

Utilisation l'idée de Nunofmendes , j'ai écrit une classe dérivée de TextView que Auto redimensionne le texte et prend en charge plusieurs lignes.

import Android.content.Context;
import Android.os.Handler;
import Android.text.Layout;
import Android.text.TextPaint;
import Android.text.TextUtils.TruncateAt;
import Android.util.AttributeSet;
import Android.util.TypedValue;

public class AutoResizeTextView extends TextView {

    private Handler measureHandler = new Handler();
    private Runnable requestLayout = new Runnable() {
        @Override
        public void run() {
            requestLayout();
        }
    };

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

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

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

    @Override
    protected void onMeasure(final int widthMeasureSpec,
                             final int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        final float maxWidth = getWidth();
        final float maxHeight = getHeight();
        if (maxWidth < 1.0f || maxHeight < 1.0f) {
            return;
        }

        int index = 0;
        int lineCount = 0;
        CharSequence text = getText();
        final TextPaint Paint = getPaint();
        while (index < text.length()) {
            index += Paint.breakText(text, index, text.length(), true, maxWidth, null);
            lineCount++;
        }
        final float height = lineCount * getLineHeight() + (lineCount > 0 ?
                (lineCount - 1) * Paint.getFontSpacing() : 0);
        if (height > maxHeight) {
            final float textSize = getTextSize();
            setTextSize(TypedValue.COMPLEX_UNIT_PX, (textSize - 1));
            measureHandler.post(requestLayout);
        }
    }
}
1
phamducgiam