[JAVA] [Android] Ajuster automatiquement textScaleX de TextView

Ce que j'ai fait

Il existe de nombreuses expériences où vous essayez de faire en sorte que TextView affiche une longue chaîne dans une situation où seul un espace limité est disponible, et la chaîne est coupée au milieu et elle ne semble pas bonne. Il peut y avoir différentes solutions, mais pour l'instant, nous allons nous concentrer sur un attribut TextView appelé textScaleX. Il s'agit d'une valeur qui indique le rapport d'expansion / contraction horizontale du caractère à afficher. Si la valeur est supérieure à 1, la chaîne de caractères est développée et si elle est inférieure à 1, la chaîne de caractères est contractée. J'ai créé une vue qui ajuste automatiquement la valeur de ce textScaleX pour compresser la longueur de la chaîne lors de l'affichage d'une chaîne qui ne rentre pas dans la largeur maximale du TextView. Appelons également le nom de classe ExpandableTextView.

Image de démonstration

L'arrière-plan de ExpandableTextView est grisé pour plus de clarté. expandableTextView_demo.gif

Que puis-je faire

Comportement du réglage automatique de la largeur de la vue

Cas pour layout_width de LayoutParam. La valeur de android: layout_width qui est définie statiquement en XML.

Code source

Installez-le dans Github Je vais.

Description de la mise en œuvre

Confirmé avec la version SDK = 26

Hériter de TextView

Je veux l'implémenter facilement, alors j'hérite de TextView et je joue avec. Comportement modifié en remplaçant certaines méthodes.

Contrôle de la chaîne de caractères affichée

Fournit le contrôle pour développer ou réduire la chaîne de caractères ou la remplacer par une chaîne de caractères alternative selon les besoins pour la largeur maximale autorisée

ExpandableTextView#updateText



private Paint mTextPaint;           //Objet pour dessiner des personnages
private CharSequence mDisplayedText //La chaîne de caractères réellement affichée

private void updateText(CharSequence text, BufferType type, int widthSize){
    mTextPaint.setTextScaleX(1f);
    float length = mTextPaint.measureText(text, 0, text.length());
    int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
    if ( length + padding > widthSize ){
        float scale = (widthSize - padding) / length;
        text =  modifyText(scale, text, widthSize - padding);
    }
    mDisplayedText = text;
    super.setText(text, type);
}

/**
 *Réduit la chaîne horizontalement pour l'adapter à la largeur maximale spécifiée.
 *Taux de rétrécissement{@link #mMinTextScaleX}S'il est inférieur à, remplacez-le par une chaîne de caractères alternative qui correspond à la largeur maximale au rapport minimum.
 * @param scale     rough value, with which text width can be adjusted to maxLength
 * @param text      raw text
 * @param maxLength max length of text
 * @return modified text
 */
private CharSequence modifyText(float scale, CharSequence text, float maxLength){
    //Référence du code source
}

Plus précisément, modifyText () est utilisé pour réduire la chaîne de caractères ou la remplacer par une autre chaîne de caractères. Notez que la longueur de la chaîne mesurée par Paint # measureText est

Par conséquent, afin de rechercher le grossissement souhaité textScaleX, le grossissement est modifié petit à petit avec un coup approprié et la meilleure valeur est adoptée. De même, afin de déterminer une chaîne de caractères alternative qui correspond à la chaîne de caractères dans la largeur maximale, il est devenu une méthode de mise en œuvre décevante de recherche tout en grattant de la fin un caractère à la fois. Le code est sale donc je ne le mettrai pas ici.

Contrôlez la largeur de la vue avec View # onMeasure (int, int)

Cette méthode est appelée pour mesurer la taille de cette vue lorsqu'elle est placée par la vue parent. Pour plus d'informations sur le rôle de onMeasure (), voir [Qiita] Understanding onMeasure and onLayout. Cette fois, je ne suis intéressé que par la largeur, je vais donc ajouter une requête à measureSpec pour la largeur. Puisqu'il est difficile d'implémenter tout onMeasure () par vous-même, passez-le à super # onMeasure () tel quel à l'exception de la largeur et lancez-le à la classe parente.

ExpandableTextView#onMeasure


private Paint mTextPaint;           //Objet pour dessiner des personnages
private CharSequence mCurrentText;  //La chaîne de caractères que vous souhaitez afficher
private CharSequence mDisplayedText;//La chaîne de caractères réellement affichée
private int mMaxWidth;              //Largeur maximum
private int mRawWidthMeasureSpec;   //Demande de la taille de cette vue spécifiée par le parent

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    //Ajouter une demande uniquement pour la largeur
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);

    CharSequence text = mCurrentText;
    mTextPaint.setTextScaleX(1f);
    float length = mTextPaint.measureText(text, 0, text.length());

    int padding = getCompoundPaddingLeft() + getCompoundPaddingRight();
    int requestedWidth = 0;

    switch ( widthMode ){
        case MeasureSpec.EXACTLY:
            //Fixé à la largeur spécifiée quelle que soit la chaîne de caractères à afficher
            requestedWidth = widthSize;
            //Ajustez la chaîne de caractères affichée en fonction de la largeur fixe
            updateText(mCurrentText, BufferType.NORMAL, widthSize);
            break;
        case MeasureSpec.AT_MOST:
            int max = Math.min(widthSize, mMaxWidth);
            if ( length + padding > max ){
                //Compresser s'il ne rentre pas dans la largeur maximale
                requestedWidth = max;
                float scale = (max - padding) / length;
                CharSequence modified = modifyText(scale, text, max - padding);
                if ( !mDisplayedText.equals(modified) ){
                    mDisplayedText = modified;
                    super.setText(modified, BufferType.NORMAL);
                }
            }else{
                //S'il convient, ajustez la largeur de la vue à la largeur de la chaîne de caractères
                requestedWidth = (int)Math.ceil(length + padding);
            }
            break;
        case MeasureSpec.UNSPECIFIED:
            //Si non spécifié, décidez vous-même
            requestedWidth = (int)Math.ceil(length + padding);
            mTextPaint.setTextScaleX(1f);
            mDisplayedText = text;
            break;
    }
    
    mRawWidthMeasureSpec = widthMeasureSpec;

    //Ajouter une demande car la largeur est fixe
    int calcWidthMeasureSpec = MeasureSpec.makeMeasureSpec(requestedWidth, MeasureSpec.EXACTLY);
   
    // onMeasure()Quitter la mise en œuvre détaillée de
    super.onMeasure(calcWidthMeasureSpec, heightMeasureSpec);
}

Correspond aux modifications de la chaîne de caractères affichée et de l'environnement

L'implémentation doit se comporter comme prévu même lorsque la chaîne de caractères à afficher avec setText () change. Le point important ici est lorsque TextView appelle layout (). Si la taille d'un TextView normal dépend de son contenu (lorsque MeasureSpec.AT_MOST est spécifié par onMeasure (), par exemple en spécifiant wrap_content dans LayoutParam),

--La chaîne de caractères affichée par setText () change --SetMaxWidth () modifie la largeur maximale --textScaleX change avec setTextScaleX ()

Appelez layout () au moment de pour recalculer la taille de View. Cette fois en plus de ce qui précède

--setMinTextScaleX () modifie la valeur minimale de textScaleX

Implémentez pour que layout () soit appelé même au moment de.

Ce qui précède est le cas où la taille de la vue dépend du contenu. Si la largeur est spécifiée par MeasureSpec.EXACTEMENT à partir du parent, ajustez la chaîne de caractères et le rapport d'expansion / contraction affichés par updateText () en fonction de la largeur fixe.

ExpandableTextView



private CharSequence mCurrentText;  //La chaîne de caractères que vous souhaitez afficher
private CharSequence mDisplayedText //La chaîne de caractères réellement affichée
private int mMaxWidth;              //Largeur maximum
private int mRawWidthMeasureSpec;   //Demande de la taille de cette vue spécifiée par le parent
private float mMinTextScaleX;       //Valeur minimale de textScalex

@Override
public void setMaxWidth(int width){
    // super#Puisque mMaxWidth est privé, enregistrez-le vous-même
    if ( width <= 0 ) return;
    if ( width != mMaxWidth ){
        mMaxWidth = width;
        mRequestMeasure = true;
        super.setMaxWidth(width);
        //maxWidth est un paramètre où la largeur de cette vue dépend du contenu (wrap)_Uniquement valable pour le contenu, etc.)
        //Dans de tels cas, la classe parente est layout()Appel
    }
}

/**
 *Spécifiez la valeur minimale de l'échelle horizontale de la chaîne de caractères.
 * @param scale in range of (0,1]
 * @see TextPaint#setTextScaleX(float)
 */
public void setMinTextScaleX(float scale){
    if ( scale <= 0 || scale > 1 ) return;
    if ( mMinTextScaleX != scale ){
        mMinTextScaleX = scale;
        if ( MeasureSpec.getMode(mRawWidthMeasureSpec) == MeasureSpec.EXACTLY ){
            // layout()Pas besoin de
            updateText(mCurrentText, BufferType.NORMAL, MeasureSpec.getSize(mRawWidthMeasureSpec));
        }else{
            // layout()Et recalculez la taille
            requestLayout();
        }
    }
}


//#setText(char[], int, int) <-Ne peut pas être remplacé en finale
//Autre que cela, c'est par ici
@Override
public void setText(CharSequence text, BufferType type){
    if ( text == null ) return;
    if ( mCurrentText != null && mCurrentText.equals(text) ) return;

    mCurrentText = text;

    if ( MeasureSpec.getMode(mRawWidthMeasureSpec) == MeasureSpec.EXACTLY ){
        //La classe parent est la mise en page()Non, pas besoin de
        updateText(text, type, MeasureSpec.getSize(mRawWidthMeasureSpec));
    }else{
        //La classe parent est la mise en page()Faire
        super.setText(text, type);
    }
}

Deux pièges de setTextScaleX ()

Dans cette implémentation, setTextScaleX () est utilisé pour spécifier le rapport d'expansion / contraction de la chaîne de caractères de l'objet Paint utilisé pour dessiner des caractères obtenus par TextView # getPaint (). Il y a une note dans getPaint () que vous ne devez pas jouer avec les propriétés de Paint (original: [Utilisez ceci uniquement pour consulter les propriétés de Paint et ne pas les changer.](Https://developer.android.com/reference/android. /widget/TextView.html#getPaint ())), textScaleX a les points d'implémentation suivants.

TextView


public void setTextScaleX(float size) {
    if (size != mTextPaint.getTextScaleX()) {
        mUserSetTextScaleX = true;
        mTextPaint.setTextScaleX(size);
        if (mLayout != null) {
            nullLayouts();
            requestLayout();
            invalidate();
        }
    }
}

private void setText(CharSequence text, BufferType type, boolean notifyBefore, int oldlen) {
    /*Omission*/
    if (!mUserSetTextScaleX) mTextPaint.setTextScaleX(1.0f);   
    /*Omission*/
}

Si vous ne spécifiez pas depuis setTextScaleX () de TextView, textScaleX = 1.0f écrasera ***! *** *** Je l'ai eu au début. Solution: appelez TextView # setTextScaleX () au moins une fois.

ExpandableTextView


public class ExpandableTextView extends AppCompatTextView{

    public ExpandableTextView(Context context, AttributeSet attr, int defaultAttr){
        super(context, attr, defaultAttr);

        /*Omission*/

        //Vous pouvez spécifier une valeur différente de la valeur actuelle, donc getTextScaleX() / 2f
        super.setTextScaleX(getTextScaleX() / 2f);
    }

Recommended Posts

[Android] Ajuster automatiquement textScaleX de TextView
Ajuster automatiquement la hauteur du fichier Xib
Définition des constantes Android
Comment implémenter l'affichage sur une seule ligne de TextView dans le développement Android
Ajustez automatiquement la hauteur de la boîte de dialogue WebView avec la taille du contenu