Android-TextView extensible avec Animation

J'ai un TextView ce qui montre d'abord une petite partie d'un texte long.

L'utilisateur peut appuyer sur un bouton "Voir plus" pour développer le TextView et voir le reste de ce texte.

En faisant des tests, je peux atteindre cela en échangeant simplement la valeur de TextView.setMaxLines entre 4 pour l'effondrement et L'entier.MAX_VALUE pour l'expansion.

Maintenant, je voudrais que ce comportement soit accompagné d'une animation. Je sais que dans cette question une solution est presque terminé, mais j'ai essayé de mettre en œuvre et je n'ai aucun succès.

Quelqu'un peut m'aider avec ça?

Merci d'avance.

37
demandé sur Community 2013-03-26 04:56:48

11 réponses

Vous pouvez consulter mon article de blog sur ExpandableTexTView:

L'idée est, initialement, le TextView montrera une petite partie d'un texte long et quand il est cliqué, il montrera le reste du texte.

Voici donc le code que je l'ai résolu.

package com.rokonoid.widget;

import android.content.Context;
import android.content.res.TypedArray;
import android.text.SpannableStringBuilder;
import android.util.AttributeSet;
import android.view.View;
import android.widget.TextView;
/**
 * User: Bazlur Rahman Rokon
 * Date: 9/7/13 - 3:33 AM
 */
public class ExpandableTextView extends TextView {
    private static final int DEFAULT_TRIM_LENGTH = 200;
    private static final String ELLIPSIS = ".....";

    private CharSequence originalText;
    private CharSequence trimmedText;
    private BufferType bufferType;
    private boolean trim = true;
    private int trimLength;

    public ExpandableTextView(Context context) {
        this(context, null);
    }

    public ExpandableTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
        this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
        typedArray.recycle();

        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                trim = !trim;
                setText();
                requestFocusFromTouch();
            }
        });
    }

    private void setText() {
        super.setText(getDisplayableText(), bufferType);
    }

    private CharSequence getDisplayableText() {
        return trim ? trimmedText : originalText;
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        originalText = text;
        trimmedText = getTrimmedText(text);
        bufferType = type;
        setText();
    }

    private CharSequence getTrimmedText(CharSequence text) {
        if (originalText != null && originalText.length() > trimLength) {
            return new SpannableStringBuilder(originalText, 0, trimLength + 1).append(ELLIPSIS);
        } else {
            return originalText;
        }
    }

    public CharSequence getOriginalText() {
        return originalText;
    }

    public void setTrimLength(int trimLength) {
        this.trimLength = trimLength;
        trimmedText = getTrimmedText(originalText);
        setText();
    }

    public int getTrimLength() {
        return trimLength;
    }
}

Et ajoutez la ligne suivante dans votre attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ExpandableTextView">
<attr name="trimLength" format="integer"/>
</declare-styleable>
</resources>

Mettez ce qui suit dans votre main.xml

<?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <com.rokonoid.widget.ExpandableTextView
 android:id="@+id/lorem_ipsum"
 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 />
 </LinearLayout>

Et testez votre activité

package com.rokonoid.widget;

import android.app.Activity;
import android.os.Bundle;

public class MyActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        String yourText = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
                "Ut volutpat interdum interdum. Nulla laoreet lacus diam, vitae " +
                "sodales sapien commodo faucibus. Vestibulum et feugiat enim. Donec " +
                "semper mi et euismod tempor. Sed sodales eleifend mi id varius. Nam " +
                "et ornare enim, sit amet gravida sapien. Quisque gravida et enim vel " +
                "volutpat. Vivamus egestas ut felis a blandit. Vivamus fringilla " +
                "dignissim mollis. Maecenas imperdiet interdum hendrerit. Aliquam" +
                " dictum hendrerit ultrices. Ut vitae vestibulum dolor. Donec auctor ante" +
                " eget libero molestie porta. Nam tempor fringilla ultricies. Nam sem " +
                "lectus, feugiat eget ullamcorper vitae, ornare et sem. Fusce dapibus ipsum" +
                " sed laoreet suscipit. ";

        ExpandableTextView expandableTextView = (ExpandableTextView) findViewById(R.id.lorem_ipsum);
        expandableTextView.setText(yourText);

    }
}

Référence: Android – Extensible TextView

64
répondu rokonoid 2014-04-17 09:03:48

Utilisez un ObjectAnimator .

ObjectAnimator animation = ObjectAnimator.ofInt(yourTextView, "maxLines", tv.getLineCount());
animation.setDuration(200).start();

Cela développera complètement votre TextView sur 200 millisecondes. Vous pouvez remplacer tv.getLineCount() par le nombre de lignes de texte que vous souhaitez réduire.

----mise à Jour----

Voici quelques méthodes de commodité que vous pouvez déposer:

private void expandTextView(TextView tv){
    ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", tv.getLineCount());
    animation.setDuration(200).start();
}

private void collapseTextView(TextView tv, int numLines){
    ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", numLines);
    animation.setDuration(200).start();
}

Si vous utilisez L'API 16+, Vous pouvez utiliser textView.getMaxLines() pour déterminer facilement si votre textView a été développé ou non.

private void cycleTextViewExpansion(TextView tv){
    int collapsedMaxLines = 3;
    ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", 
        tv.getMaxLines() == collapsedMaxLines? tv.getLineCount() : collapsedMaxLines);
    animation.setDuration(200).start();
}

Notes:

Si maxLines n'a pas été définir, ou vous avez définir la hauteur de votre textView en pixels , vous pouvez obtenir une exception ArrayIndexOutOfBounds.

Les exemples ci-dessus prennent toujours 200ms, qu'ils se développent par 3 lignes ou 400. Si vous voulez un taux d'expansion constant, vous pouvez faire quelque chose comme ceci:

int duration = (textView.getLineCount() - collapsedMaxLines) * 10;
49
répondu Amagi82 2015-09-10 11:45:57

J'ai créé une bibliothèque open-source pour cela, parce que je n'étais pas satisfait des autres solutions que j'ai trouvées sur internet. J'ai mis la chose sur GitHub, et c'est gratuit à utiliser par n'importe qui.

public class ExpandableTextView extends TextView
{
    // copy off TextView.LINES
    private static final int MAXMODE_LINES = 1;

    private OnExpandListener onExpandListener;
    private TimeInterpolator expandInterpolator;
    private TimeInterpolator collapseInterpolator;

    private final int maxLines;
    private long animationDuration;
    private boolean animating;
    private boolean expanded;
    private int originalHeight;

    public ExpandableTextView(final Context context)
    {
        this(context, null);
    }

    public ExpandableTextView(final Context context, final AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public ExpandableTextView(final Context context, final AttributeSet attrs, final int defStyle)
    {
        super(context, attrs, defStyle);

        // read attributes
        final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView, defStyle, 0);
        this.animationDuration = attributes.getInt(R.styleable.ExpandableTextView_animation_duration, BuildConfig.DEFAULT_ANIMATION_DURATION);
        attributes.recycle();

        // keep the original value of maxLines
        this.maxLines = this.getMaxLines();

        // create default interpolators
        this.expandInterpolator = new AccelerateDecelerateInterpolator();
        this.collapseInterpolator = new AccelerateDecelerateInterpolator();
    }

    @Override
    public int getMaxLines()
    {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
        {
            return super.getMaxLines();
        }

        try
        {
            final Field mMaxMode = TextView.class.getField("mMaxMode");
            mMaxMode.setAccessible(true);
            final Field mMaximum = TextView.class.getField("mMaximum");
            mMaximum.setAccessible(true);

            final int mMaxModeValue = (int) mMaxMode.get(this);
            final int mMaximumValue = (int) mMaximum.get(this);

            return mMaxModeValue == MAXMODE_LINES ? mMaximumValue : -1;
        }
        catch (final Exception e)
        {
           return -1;
        }
    }

    /**
     * Toggle the expanded state of this {@link ExpandableTextView}.
     * @return true if toggled, false otherwise.
     */
    public boolean toggle()
    {
        if (this.expanded)
        {
            return this.collapse();
        }

        return this.expand();
    }

    /**
     * Expand this {@link ExpandableTextView}.
     * @return true if expanded, false otherwise.
     */
    public boolean expand()
    {
        if (!this.expanded && !this.animating && this.maxLines >= 0)
        {
            this.animating = true;

            // notify listener
            if (this.onExpandListener != null)
            {
                this.onExpandListener.onExpand(this);
            }

            // get original height
            this.measure
            (
                MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
            );

            this.originalHeight = this.getMeasuredHeight();

            // set maxLines to MAX Integer
            this.setMaxLines(Integer.MAX_VALUE);

            // get new height
            this.measure
            (
                MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), MeasureSpec.EXACTLY),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
            );

            final int fullHeight = this.getMeasuredHeight();

            final ValueAnimator valueAnimator = ValueAnimator.ofInt(this.originalHeight, fullHeight);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
            {
                @Override
                public void onAnimationUpdate(final ValueAnimator animation)
                {
                    final ViewGroup.LayoutParams layoutParams = ExpandableTextView.this.getLayoutParams();
                    layoutParams.height = (int) animation.getAnimatedValue();
                    ExpandableTextView.this.setLayoutParams(layoutParams);
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter()
            {
                @Override
                public void onAnimationEnd(final Animator animation)
                {
                    ExpandableTextView.this.expanded = true;
                    ExpandableTextView.this.animating = false;
                }
            });

            // set interpolator
            valueAnimator.setInterpolator(this.expandInterpolator);

            // start the animation
            valueAnimator
                .setDuration(this.animationDuration)
                .start();

            return true;
        }

        return false;
    }

    /**
     * Collapse this {@link TextView}.
     * @return true if collapsed, false otherwise.
     */
    public boolean collapse()
    {
        if (this.expanded && !this.animating && this.maxLines >= 0)
        {
            this.animating = true;

            // notify listener
            if (this.onExpandListener != null)
            {
                this.onExpandListener.onCollapse(this);
            }

            // get new height
            final int fullHeight = this.getMeasuredHeight();

            final ValueAnimator valueAnimator = ValueAnimator.ofInt(fullHeight, this.originalHeight);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()
            {
                @Override
                public void onAnimationUpdate(final ValueAnimator animation)
                {
                    final ViewGroup.LayoutParams layoutParams = ExpandableTextView.this.getLayoutParams();
                    layoutParams.height = (int) animation.getAnimatedValue();
                    ExpandableTextView.this.setLayoutParams(layoutParams);
                }
            });
            valueAnimator.addListener(new AnimatorListenerAdapter()
            {
                @Override
                public void onAnimationEnd(final Animator animation)
                {
                    // set maxLines to original value
                    ExpandableTextView.this.setMaxLines(ExpandableTextView.this.maxLines);

                    ExpandableTextView.this.expanded = false;
                    ExpandableTextView.this.animating = false;
                }
            });

            // set interpolator
            valueAnimator.setInterpolator(this.collapseInterpolator);

            // start the animation
            valueAnimator
                .setDuration(this.animationDuration)
                .start();

            return true;
        }

        return false;
    }

    /**
     * Sets the duration of the expand / collapse animation.
     * @param animationDuration duration in milliseconds.
     */
    public void setAnimationDuration(final long animationDuration)
    {
        this.animationDuration = animationDuration;
    }

    /**
     * Sets a listener which receives updates about this {@link ExpandableTextView}.
     * @param onExpandListener the listener.
     */
    public void setOnExpandListener(final OnExpandListener onExpandListener)
    {
        this.onExpandListener = onExpandListener;
    }

    /**
     * Returns the {@link OnExpandListener}.
     * @return the listener.
     */
    public OnExpandListener getOnExpandListener()
    {
        return onExpandListener;
    }

    /**
     * Sets a {@link TimeInterpolator} for expanding and collapsing.
     * @param interpolator the interpolator
     */
    public void setInterpolator(final TimeInterpolator interpolator)
    {
        this.expandInterpolator = interpolator;
        this.collapseInterpolator = interpolator;
    }

    /**
     * Sets a {@link TimeInterpolator} for expanding.
     * @param expandInterpolator the interpolator
     */
    public void setExpandInterpolator(final TimeInterpolator expandInterpolator)
    {
        this.expandInterpolator = expandInterpolator;
    }

    /**
     * Returns the current {@link TimeInterpolator} for expanding.
     * @return the current interpolator, null by default.
     */
    public TimeInterpolator getExpandInterpolator()
    {
        return this.expandInterpolator;
    }

    /**
     * Sets a {@link TimeInterpolator} for collpasing.
     * @param collapseInterpolator the interpolator
     */
    public void setCollapseInterpolator(final TimeInterpolator collapseInterpolator)
    {
        this.collapseInterpolator = collapseInterpolator;
    }

    /**
     * Returns the current {@link TimeInterpolator} for collapsing.
     * @return the current interpolator, null by default.
     */
    public TimeInterpolator getCollapseInterpolator()
    {
        return this.collapseInterpolator;
    }

    /**
     * Is this {@link ExpandableTextView} expanded or not?
     * @return true if expanded, false if collapsed.
     */
    public boolean isExpanded()
    {
        return this.expanded;
    }

    public interface OnExpandListener
    {
        void onExpand(ExpandableTextView view);
        void onCollapse(ExpandableTextView view);
    }
}

L'utilisation de ExpandableTextView est très facile, c'est juste un TextView régulier avec des fonctionnalités supplémentaires ajoutées. En définissant l'attribut android:maxLines, vous pouvez définir le nombre de lignes par défaut pour L'état réduit TextView.

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <at.blogc.android.views.ExpandableTextView
        android:id="@+id/expandableTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/lorem_ipsum"
        android:maxLines="5"
        android:ellipsize="end"
        app:animation_duration="1000"/>

    <!-- Optional parameter animation_duration: sets the duration of the expand animation -->

    <Button
        android:id="@+id/button_toggle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/expand"/>

</LinearLayout>

Dans votre activité ou Fragment:

    final ExpandableTextView expandableTextView = (ExpandableTextView) this.findViewById(R.id.expandableTextView);
    final Button buttonToggle = (Button) this.findViewById(R.id.button_toggle);

    // set animation duration via code, but preferable in your layout files by using the animation_duration attribute
    expandableTextView.setAnimationDuration(1000L);

// set interpolators for both expanding and collapsing animations
expandableTextView.setInterpolator(new OvershootInterpolator());

// or set them separately
expandableTextView.setExpandInterpolator(new OvershootInterpolator());
expandableTextView.setCollapseInterpolator(new OvershootInterpolator());


    // toggle the ExpandableTextView
    buttonToggle.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(final View v)
        {
            expandableTextView.toggle();
            buttonToggle.setText(expandableTextView.isExpanded() ? R.string.collapse : R.string.expand);
        }
    });

    // but, you can also do the checks yourself
    buttonToggle.setOnClickListener(new View.OnClickListener()
    {
        @Override
        public void onClick(final View v)
        {
            if (expandableTextView.isExpanded())
            {
                expandableTextView.collapse();
                buttonToggle.setText(R.string.expand);
            }
            else
            {
                expandableTextView.expand();
                buttonToggle.setText(R.string.collapse);
            }
        }
    });

    // listen for expand / collapse events
    expandableTextView.setOnExpandListener(new ExpandableTextView.OnExpandListener()
    {
        @Override
        public void onExpand(final ExpandableTextView view)
        {
            Log.d(TAG, "ExpandableTextView expanded");
        }

        @Override
        public void onCollapse(final ExpandableTextView view)
        {
            Log.d(TAG, "ExpandableTextView collapsed");
        }
    });

Vous pouvez facilement ajouter cette bibliothèque en tant que dépendance gradle à votre projet Android. Jetez un oeil au projet sur Github pour plus d'instructions:

Https://github.com/Blogcat/Android-ExpandableTextView

14
répondu Cliffus 2016-05-16 12:56:26

Expansion lisse (en utilisant heigh & ObjectAnimator )
FYI: nécessite API 11

public static void expandCollapsedByMaxLines(@NonNull final TextView text) {
    final int height = text.getMeasuredHeight();
    text.setHeight(height);
    text.setMaxLines(Integer.MAX_VALUE); //expand fully
    text.measure(View.MeasureSpec.makeMeasureSpec(text.getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(ViewGroup.LayoutParams.WRAP_CONTENT, View.MeasureSpec.UNSPECIFIED));
    final int newHeight = text.getMeasuredHeight();
    ObjectAnimator animation = ObjectAnimator.ofInt(text, "height", height, newHeight);
    animation.setDuration(250).start();
}

P.S. Je suppose que TextView est limité par maxLines.
P. S. S. Merci Amagi82 pour L'exemple ObjectAnimator

6
répondu igork 2017-05-23 12:02:58

Si vous voulez le faire sur la base du nombre de lignes, voici un moyen de le faire:

(Gist du code complet)

/**
 * Ellipsize the text when the lines of text exceeds the value provided by {@link #makeExpandable} methods.
 * Appends {@link #MORE} or {@link #LESS} as needed.
 * TODO: add animation
 * Created by vedant on 3/10/15.
 */
public class ExpandableTextView extends TextView {
    private static final String TAG = "ExpandableTextView";
    private static final String ELLIPSIZE = "... ";
    private static final String MORE = "more";
    private static final String LESS = "less";

    private String mFullText;
    private int mMaxLines;

    //...constructors...

    public void makeExpandable(String fullText, int maxLines) {
        mFullText =fullText;
        mMaxLines = maxLines;
        ViewTreeObserver vto = getViewTreeObserver();
        vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                ViewTreeObserver obs = getViewTreeObserver();
                obs.removeOnGlobalLayoutListener(this);
                if (getLineCount() <= maxLines) {
                    setText(mFullText);
                } else {
                    setMovementMethod(LinkMovementMethod.getInstance());
                    showLess();
                }
            }
        });
    }

    /**
     * truncate text and append a clickable {@link #MORE}
     */
    private void showLess() {
        int lineEndIndex = getLayout().getLineEnd(mMaxLines - 1);
        String newText = mFullText.substring(0, lineEndIndex - (ELLIPSIZE.length() + MORE.length() + 1))
                + ELLIPSIZE + MORE;
        SpannableStringBuilder builder = new SpannableStringBuilder(newText);
        builder.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                showMore();
            }
        }, newText.length() - MORE.length(), newText.length(), 0);
        setText(builder, BufferType.SPANNABLE);
    }

    /**
     * show full text and append a clickable {@link #LESS}
     */
    private void showMore() {
        // create a text like subText + ELLIPSIZE + MORE
        SpannableStringBuilder builder = new SpannableStringBuilder(mFullText + LESS);
        builder.setSpan(new ClickableSpan() {
            @Override
            public void onClick(View widget) {
                showLess();
            }
        }, builder.length() - LESS.length(), builder.length(), 0);
        setText(builder, BufferType.SPANNABLE);
    }
}
4
répondu vedant 2015-10-03 07:34:27

Reportez-vous au lien ci-dessous pour TextView extensible avec des options pour un certain nombre de lignes et plus moins de texte.

Vue texte redimensionnable (Voir plus Et Voir moins)

Ajoutez la ligne ci-dessous dans la classe Java après avoir défini le texte dans votre TextView.

// YourCustomeClass.class [your customized class]
// yourTextView [ TextView yourTextView = findViewById(R.id.yourTextView) ];

YourCustomeClass.doResizeTextView(yourTextView, 3, "More", true);

// 3 - No of lines after user wants to expand it. 
// "More" : text want to see end of your TextView after shrink
// True : flag for viewMore
3
répondu Mihir Trivedi 2017-12-25 10:51:38

Voici ce qui a fonctionné pour moi en utilisant certaines des réponses ci-dessus (j'utilise ButterKnife dans l'exemple):

private static final MAX_LINE_COUNT = 3;    

@Bind(R.id.description)
TextView mDescription;    

@Override
protected void onCreate(Bundle savedInstanceState) {

  if(!TextUtils.isEmpty(mDescription)) {
    mDescription.setText(mItem.description);
    mDescription.setMaxLines(MAX_LINE_COUNT);
    mDescription.setEllipsize(TextUtils.TruncateAt.END);
  } else {
    mDescription.setVisibility(View.GONE);
  }

}

@OnClick(R.id.description)
void collapseExpandTextView(TextView tv) {

    if (tv.getMaxLines() == MAX_LINE_COUNT) {
        // collapsed - expand it
        tv.setEllipsize(null);
        tv.setMaxLines(Integer.MAX_VALUE);
    } else {
        // expanded - collapse it
        tv.setEllipsize(TextUtils.TruncateAt.END);
        tv.setMaxLines(MAX_LINE_COUNT);
    }

    ObjectAnimator animation = ObjectAnimator.ofInt(tv, "maxLines", tv.getMaxLines());
    animation.setDuration(200).start();
}   

Lorsque l'utilisateur clique sur la description, il s'effondrera ou se développera en fonction des lignes max. cela ne fonctionnera que pour L'API 16+.

Le problème que j'ai rencontré était que le nombre de lignes renvoyait zéro aux points et que le nombre de lignes et le nombre maximum étaient les mêmes valeurs à certains points.

2
répondu Ray Hunter 2015-12-08 18:51:53

Vous pouvez faire quelque chose comme ça. Cela fonctionnera dans n'importe quel type de vue, qu'il s'agisse d'une vue normale ou d'une vue dans ListView ou RecyclerView:

Dans onCreate() ou quelque chose de similaire, ajouter:

// initialize integers
int collapsedHeight, expandedHeight;

// get collapsed height after TextView is drawn
textView.post(new Runnable() {
    @Override
    public void run() {
        collapsedHeight = textView.getMeasuredHeight();
    }
});

// view that will expand/collapse your TextView
view.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        // number of max lines when collapsed
        if (textView.getMaxLines() == 2) {
            // expand
            textView.setMaxLines(Integer.MAX_VALUE);
            textView.measure(View.MeasureSpec.makeMeasureSpec(notifMessage.getMeasuredWidth(), View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED));
            expandedHeight = textView.getMeasuredHeight();
            ObjectAnimator animation = ObjectAnimator.ofInt(textView, "height", collapsedHeight, expandedHeight);
            animation.setDuration(250).start();
        } else {
            // collapse
            ObjectAnimator animation = ObjectAnimator.ofInt(textView, "height", expandedHeight, collapsedHeight);
            animation.addListener(new Animator.AnimatorListener() {
                @Override
                public void onAnimationStart(Animator animator) {

                }

                @Override
                public void onAnimationEnd(Animator animator) {
                    // number of max lines when collapsed
                    textView.setMaxLines(2);
                }

                @Override
                public void onAnimationCancel(Animator animator) {

                }

                @Override
                public void onAnimationRepeat(Animator animator) {

                }
            });
            animation.setDuration(250).start();
        }
    }
});

Cela vous permettra de développer / réduire un TextView en cliquant sur n'importe quelle vue de votre choix. (vous pouvez sûrement choisir le TextView lui-même aussi)

2
répondu Hussein El Feky 2016-10-22 21:16:09

Dans ListView ou RecyclerView au lieu D'utiliser OnGlobalLayoutListener, nous utilisons toujours OnPreDrawListener. Ce rappel est également déclenché pour les lignes non visibles au début. De la documentation officielle:

private void makeTextViewResizable(final TextView tv, final int maxLine, final String expandText, final boolean viewMore){
        try {
            if (tv.getTag() == null) {
                tv.setTag(tv.getText());
            }
            //OnGlobalLayoutListener
            ViewTreeObserver vto = tv.getViewTreeObserver();
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

                @Override
                public boolean onPreDraw() {

                        ViewTreeObserver obs = tv.getViewTreeObserver();
                       // obs.removeGlobalOnLayoutListener((ViewTreeObserver.OnGlobalLayoutListener) mActivity);
                        obs.removeOnPreDrawListener(this);
                        if (maxLine == 0) {
                            int lineEndIndex = tv.getLayout().getLineEnd(0);
                            String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                            tv.setText(text);
                            tv.setMovementMethod(LinkMovementMethod.getInstance());
                            tv.setText(
                                    addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
                                            viewMore), TextView.BufferType.SPANNABLE);
                        } else if (maxLine > 0 && tv.getLineCount() >= maxLine) {
                            int lineEndIndex = tv.getLayout().getLineEnd(maxLine - 1);
                            String text = tv.getText().subSequence(0, lineEndIndex - expandText.length() + 1) + " " + expandText;
                            tv.setText(text);
                            tv.setMovementMethod(LinkMovementMethod.getInstance());
                            tv.setText(
                                    addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
                                            viewMore), TextView.BufferType.SPANNABLE);
                        } else {
                            int lineEndIndex = tv.getLayout().getLineEnd(tv.getLayout().getLineCount() - 1);
                            String text = tv.getText().subSequence(0, lineEndIndex) + " " + expandText;
                            tv.setText(text);
                            tv.setMovementMethod(LinkMovementMethod.getInstance());
                            tv.setText(
                                    addClickablePartTextViewResizable(Html.fromHtml(tv.getText().toString()), tv, expandText,
                                            viewMore), TextView.BufferType.SPANNABLE);
                        }


                    return true;
                }


            });
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
0
répondu Mohammad 2016-09-27 09:44:56

Principalement pour le cas d'ajouter le "voir plus" à la fin du texte, je vous présente mon TruncatingTextView. Après beaucoup d'expérimentation, il semble fonctionner de manière transparente lors du chargement de ces vues de texte dans une vue D'élément RecyclerView.

package com.example.android.widgets;

import android.content.Context;
import android.support.annotation.Nullable;
import android.support.v7.widget.AppCompatTextView;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;

import com.example.android.R;

public class TruncatingTextView extends AppCompatTextView {
    public static final String TWO_SPACES = "  ";

    private int truncateAfter = Integer.MAX_VALUE;

    private String suffix;
    private RelativeSizeSpan truncateTextSpan = new RelativeSizeSpan(0.75f);
    private ForegroundColorSpan viewMoreTextSpan = new ForegroundColorSpan(Color.BLUE);
    private static final String MORE_STRING = getContext().getString(R.string.more);

    private static final String ELLIPSIS = getContext().getString(R.string.ellipsis);

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

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

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

    public void setText(CharSequence fullText, @Nullable CharSequence afterTruncation, int truncateAfterLineCount) {
        this.suffix = TWO_SPACES + MORE_STRING;

        if (!TextUtils.isEmpty(afterTruncation)) {
            suffix += TWO_SPACES + afterTruncation;
        }

        // Don't call setMaxLines() unless we have to, since it does a redraw.
        if (this.truncateAfter != truncateAfterLineCount) {
            this.truncateAfter = truncateAfterLineCount;
            setMaxLines(truncateAfter);
        }

        setText(fullText);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);

        if (getLayout() != null && getLayout().getLineCount() > truncateAfter) {
            int lastCharToShowOfFullTextAfterTruncation = getLayout().getLineVisibleEnd(truncateAfter - 1) - suffix.length() - ELLIPSIS.length();

            if (getText().length() <= lastCharToShowOfFullTextAfterTruncation) {
                // No idea why this would be the case, but to prevent a crash, here it is. Besides, if this is true, we should be less than our maximum lines and thus good to go.
                return;
            }

            int startIndexOfMoreString = lastCharToShowOfFullTextAfterTruncation + TWO_SPACES.length() + 1;

            SpannableString truncatedSpannableString = new SpannableString(getText().subSequence(0, lastCharToShowOfFullTextAfterTruncation) + ELLIPSIS + suffix);
            truncatedSpannableString.setSpan(truncateTextSpan, startIndexOfMoreString, truncatedSpannableString.length(), Spannable.SPAN_EXCLUSIVE_INCLUSIVE);
            truncatedSpannableString.setSpan(viewMoreTextSpan, startIndexOfMoreString, startIndexOfMoreString + MORE_STRING.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
            setText(truncatedSpannableString);
        }
    }
}

Vous pouvez toujours choisir d'ajouter votre propre attribut pour truncateAfter, et utiliser l'une des réponses ci-dessus pour ajouter l'animation pour expand/collapse (Je n'ai pas codé pour gérer expand/collapse mais facilement en utilisant l'une des animations ci-dessus réponse).

Je place ceci ici plus pour les autres qui essaient de trouver la fonctionnalité "voir plus" pour leurs vues de texte.

0
répondu Chantell Osejo 2017-01-07 00:55:09

Maintenant, il est encore plus facile de fournir le TextView demandé avec animation et tous les contrôles requis en utilisant cette bibliothèque impressionnante ExpandableTextView , dans cette bibliothèque, vous n'avez qu'à l'ajouter dans votre gradle et ensuite le Définir comme suit dans votre xml:

  <com.ms.square.android.expandabletextview.ExpandableTextView
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:expandableTextView="http://schemas.android.com/apk/res-auto"
      android:id="@+id/expand_text_view"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      expandableTextView:maxCollapsedLines="4"
      expandableTextView:animDuration="200">
      <TextView
          android:id="@id/expandable_text"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:layout_marginLeft="10dp"
          android:layout_marginRight="10dp"
          android:textSize="16sp"
          android:textColor="#666666" />
      <ImageButton
          android:id="@id/expand_collapse"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:padding="16dp"
          android:layout_gravity="right|bottom"
          android:background="@android:color/transparent"/>
  </com.ms.square.android.expandabletextview.ExpandableTextView>

Et après cela, utilisez-le dans votre code comme:

TextView expandableTextView = (ExpandableTextView) findViewById(R.id.expand_text_view);

Et comme vous le voyez, vous pouvez contrôler les lignes max que vous voulez et la durée de l'animation et tous les paramètres requis pour votre TextView développer technique.

0
répondu Muhammed Refaat 2017-03-20 11:25:38