[JAVA] [Android] Passen Sie textScaleX von TextView automatisch an

Was ich gemacht habe

Es gibt viele Erfahrungen, bei denen Sie versuchen, TextView dazu zu bringen, eine lange Zeichenfolge anzuzeigen, wenn nur ein begrenzter Speicherplatz verfügbar ist und die Zeichenfolge in der Mitte abgeschnitten ist und nicht gut aussieht. Es mag verschiedene Lösungen geben, aber im Moment konzentrieren wir uns auf ein TextView-Attribut namens textScaleX. Dies ist ein Wert, der das horizontale Expansions- / Kontraktionsverhältnis des angezeigten Zeichens angibt. Wenn der Wert größer als 1 ist, wird die Zeichenfolge erweitert, und wenn der Wert kleiner als 1 ist, wird die Zeichenfolge verkleinert. Ich habe eine Ansicht erstellt, die den Wert dieser textScaleX automatisch anpasst, um die Länge der Zeichenfolge zu komprimieren, wenn eine Zeichenfolge angezeigt wird, die nicht in die maximale Breite der Textansicht passt. Nennen wir auch den Klassennamen ExpandableTextView.

Demo-Bild

Der Hintergrund der ExpandableTextView ist aus Gründen der Übersichtlichkeit ausgegraut. expandableTextView_demo.gif

Was kann ich tun

--Dehnen Sie die angezeigte Zeichenfolge horizontal entsprechend der maximalen Breite der Ansicht (textScaleX = (0.0, 1.0]). --OnLayout () und setText () passen sich jedes Mal automatisch an, wenn sich die Situation ändert

Automatisches Anpassungsverhalten der Ansichtsbreite

Fälle für layout_width von LayoutParam. Der Wert von android: layout_width, der statisch in XML definiert ist.

Quellcode

Erhöhen Sie es auf Github Ich werde.

Implementierungsbeschreibung

Bestätigt mit SDK Version = 26

TextView erben

Ich möchte es einfach implementieren, also erbe ich TextView und spiele damit herum. Geändertes Verhalten durch Überschreiben einiger Methoden.

Steuerung der angezeigten Zeichenkette

Bietet Kontrolle zum Erweitern oder Verkleinern der Zeichenfolge oder zum Ersetzen durch eine alternative Zeichenfolge, je nach Bedarf für die maximal zulässige Breite

ExpandableTextView#updateText



private Paint mTextPaint;           //Objekt zum Zeichnen von Zeichen
private CharSequence mDisplayedText //Die tatsächlich angezeigte Zeichenfolge

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);
}

/**
 *Verkleinert die Zeichenfolge horizontal, um sie an die angegebene maximale Breite anzupassen.
 *Schrumpfungsrate{@link #mMinTextScaleX}Wenn es kleiner als ist, ersetzen Sie es durch eine alternative Zeichenfolge, die der maximalen Breite bei minimalem Verhältnis entspricht.
 * @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){
    //Quellcode-Referenz
}

Insbesondere wird modifyText () verwendet, um die Zeichenfolge zu verkleinern oder durch eine alternative Zeichenfolge zu ersetzen. Beachten Sie, dass die Länge der Zeichenfolge, die mit Paint # MeasureText gemessen wird, beträgt

Um nach dem gewünschten Vergrößerungstext ScaleX zu suchen, wird die Vergrößerung daher nach und nach mit einem geeigneten Treffer geändert und der beste Wert wird übernommen. In ähnlicher Weise ist es zu einer enttäuschenden Implementierungsmethode für die Suche geworden, eine alternative Zeichenfolge zu bestimmen, die der Zeichenfolge in der maximalen Breite entspricht, während jeweils ein Zeichen vom Ende abgekratzt wird. Der Code ist schmutzig, deshalb werde ich ihn hier nicht einfügen.

Steuern Sie die Breite der Ansicht mit View # onMeasure (int, int).

Diese Methode wird aufgerufen, um die Größe dieser Ansicht zu messen, wenn sie von der übergeordneten Ansicht platziert wird. Weitere Informationen zur Rolle von onMeasure () finden Sie unter [Qiita] Grundlegendes zu onMeasure und onLayout. Dieses Mal interessiert mich nur die Breite, daher füge ich MeasureSpec eine Anforderung für die Breite hinzu. Da es schwierig ist, alle onMeasure () selbst zu implementieren, übergeben Sie sie mit Ausnahme der Breite an super # onMeasure () und werfen Sie sie an die übergeordnete Klasse.

ExpandableTextView#onMeasure


private Paint mTextPaint;           //Objekt zum Zeichnen von Zeichen
private CharSequence mCurrentText;  //Die Zeichenfolge, die Sie anzeigen möchten
private CharSequence mDisplayedText;//Die tatsächlich angezeigte Zeichenfolge
private int mMaxWidth;              //Maximale Breite
private int mRawWidthMeasureSpec;   //Fordern Sie die vom übergeordneten Element angegebene Größe dieser Ansicht an

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
    //Anfrage nur für Breite hinzufügen
    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:
            //Unabhängig von der anzuzeigenden Zeichenfolge auf die angegebene Breite festgelegt
            requestedWidth = widthSize;
            //Passen Sie die angezeigte Zeichenfolge an die feste Breite an
            updateText(mCurrentText, BufferType.NORMAL, widthSize);
            break;
        case MeasureSpec.AT_MOST:
            int max = Math.min(widthSize, mMaxWidth);
            if ( length + padding > max ){
                //Komprimieren, wenn es nicht in die maximale Breite passt
                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{
                //Wenn es passt, passen Sie die Breite der Ansicht an die Breite der Zeichenfolge an
                requestedWidth = (int)Math.ceil(length + padding);
            }
            break;
        case MeasureSpec.UNSPECIFIED:
            //Wenn nicht angegeben, entscheiden Sie selbst
            requestedWidth = (int)Math.ceil(length + padding);
            mTextPaint.setTextScaleX(1f);
            mDisplayedText = text;
            break;
    }
    
    mRawWidthMeasureSpec = widthMeasureSpec;

    //Anforderung hinzufügen, da die Breite festgelegt ist
    int calcWidthMeasureSpec = MeasureSpec.makeMeasureSpec(requestedWidth, MeasureSpec.EXACTLY);
   
    // onMeasure()Lassen Sie die detaillierte Implementierung von
    super.onMeasure(calcWidthMeasureSpec, heightMeasureSpec);
}

Entspricht Änderungen in der angezeigten Zeichenfolge und Umgebung

Die Implementierung verhält sich wie beabsichtigt, auch wenn sich die mit setText () anzuzeigende Zeichenfolge ändert. Der wichtige Punkt hier ist, wenn TextView layout () aufruft. Wenn die Größe einer normalen Textansicht von ihrem Inhalt abhängt (wenn MeasureSpec.AT_MOST von onMeasure () angegeben wird, z. B. durch Angabe von wrap_content in LayoutParam),

Rufen Sie layout () zum Zeitpunkt auf, um die Größe der Ansicht neu zu berechnen. Diesmal zusätzlich zu den oben genannten

--setMinTextScaleX () ändert den Mindestwert von textScaleX

Implementieren Sie dies so, dass layout () auch zum Zeitpunkt von aufgerufen wird.

Dies ist der Fall, wenn die Größe der Ansicht vom Inhalt abhängt. Wenn die Breite von MeasureSpec.EXACTLY vom übergeordneten Element angegeben wird, passen Sie die von updateText () angezeigte Zeichenfolge und das Expansions- / Kontraktionsverhältnis entsprechend der festen Breite an.

ExpandableTextView



private CharSequence mCurrentText;  //Die Zeichenfolge, die Sie anzeigen möchten
private CharSequence mDisplayedText //Die tatsächlich angezeigte Zeichenfolge
private int mMaxWidth;              //Maximale Breite
private int mRawWidthMeasureSpec;   //Fordern Sie die vom übergeordneten Element angegebene Größe dieser Ansicht an
private float mMinTextScaleX;       //Mindestwert von textScalex

@Override
public void setMaxWidth(int width){
    // super#Da mMaxWidth privat ist, nehmen Sie es selbst auf
    if ( width <= 0 ) return;
    if ( width != mMaxWidth ){
        mMaxWidth = width;
        mRequestMeasure = true;
        super.setMaxWidth(width);
        //maxWidth ist eine Einstellung, bei der die Breite dieser Ansicht vom Inhalt abhängt (Wrap)_Nur gültig für Inhalte etc.)
        //In solchen Fällen ist die übergeordnete Klasse Layout()Anruf
    }
}

/**
 *Geben Sie den Mindestwert der horizontalen Skalierung der Zeichenfolge an.
 * @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()Kein Notwendigkeit für
            updateText(mCurrentText, BufferType.NORMAL, MeasureSpec.getSize(mRawWidthMeasureSpec));
        }else{
            // layout()Und berechnen Sie die Größe neu
            requestLayout();
        }
    }
}


//#setText(char[], int, int) <-Kann im Finale nicht überschrieben werden
//Davon abgesehen ist via hier
@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 ){
        //Die übergeordnete Klasse ist das Layout()Nein, keine Notwendigkeit
        updateText(text, type, MeasureSpec.getSize(mRawWidthMeasureSpec));
    }else{
        //Die übergeordnete Klasse ist das Layout()Machen
        super.setText(text, type);
    }
}

Zwei Fallstricke bei setTextScaleX ()

In dieser Implementierung wird setTextScaleX () verwendet, um das Expansions- / Kontraktionsverhältnis der Zeichenfolge für das Paint-Objekt anzugeben, das zum Zeichnen von Zeichen verwendet wird, die von TextView # getPaint () erhalten wurden. In getPaint () gibt es einen Hinweis, dass Sie sich nicht mit den Paint-Eigenschaften herumschlagen sollten (Original: Verwenden Sie diese Option nur, um die Eigenschaften des Paint zu konsultieren und nicht zu ändern.. /widget/TextView.html#getPaint ())) hat textScaleX die folgenden Implementierungspunkte.

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) {
    /*Unterlassung*/
    if (!mUserSetTextScaleX) mTextPaint.setTextScaleX(1.0f);   
    /*Unterlassung*/
}

Wenn Sie in setTextScaleX () von TextView nicht angeben, wird textScaleX = 1.0f überschrieben ***! *** *** Ich habe es zuerst bekommen. Lösung: Rufen Sie TextView # setTextScaleX () mindestens einmal auf.

ExpandableTextView


public class ExpandableTextView extends AppCompatTextView{

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

        /*Unterlassung*/

        //Sie können einen anderen Wert als den aktuellen Wert angeben, also getTextScaleX() / 2f
        super.setTextScaleX(getTextScaleX() / 2f);
    }

Recommended Posts

[Android] Passen Sie textScaleX von TextView automatisch an
Passen Sie die Höhe der Xib-Datei automatisch an
Definition von Android-Konstanten
So implementieren Sie eine einzeilige Anzeige von TextView in der Android-Entwicklung
Passen Sie die Höhe des WebView-Dialogfelds automatisch an die Größe des Inhalts an