Saltar al contenido

Android: TextView expandible con animación

Después de de esta extensa búsqueda de datos hemos podido resolver este dilema que presentan algunos lectores. Te compartimos la solución y deseamos que resulte de gran apoyo.

Solución:

Puede consultar la publicación de mi blog en ExpandableTexTView:

La idea es que, inicialmente, TextView mostrará una pequeña parte de un texto largo y cuando se haga clic en él, mostrará el resto del texto.

Así que aquí está el código de cómo lo resolví.

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;
    

Y agregue la siguiente línea en su attr.xml



    
        
    

Pon lo siguiente en tu main.xml


 

     

 

Y prueba tu actividad

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

Referencia: Android – TextView expandible

Utilice un ObjectAnimator.

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

Esto expandirá completamente su TextView en más de 200 milisegundos. Puedes reemplazar tv.getLineCount() con la cantidad de líneas de texto que desee, volver a contraerlo.

—-Actualizar—-

A continuación, se muestran algunos métodos prácticos que puede utilizar:

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 está en API 16+, puede usar textView.getMaxLines () para determinar fácilmente si su textView se ha expandido o no.

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

Notas:

Si no se ha establecido maxLines, o si ha establecido la altura de su textView en píxeles, puede obtener una excepción ArrayIndexOutOfBounds.

Los ejemplos anteriores siempre toman 200 ms, ya sea que se expandan en 3 líneas o 400. Si desea una tasa de expansión constante, puede hacer algo como esto:

int duration = (textView.getLineCount() - collapsedMaxLines) * 10;

Creé una biblioteca de código abierto para esto, porque no estaba satisfecho con las otras soluciones que encontré en Internet. Lo puse en GitHub y cualquiera puede usarlo gratis.

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

Usar el ExpandableTextView es muy fácil, es solo un TextView normal con algunas funciones adicionales agregadas. Al definir el atributo android: maxLines, puede establecer el número predeterminado de líneas para el estado contraído de TextView.



    

    

    

En su actividad o fragmento:

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

Puede agregar fácilmente esta biblioteca como una dependencia de Gradle a su proyecto de Android. Eche un vistazo al proyecto en Github para obtener más instrucciones:

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

Valoraciones y comentarios

Si guardas alguna vacilación y disposición de acrecentar nuestro reseña te proponemos escribir una nota y con mucho placer lo analizaremos.

¡Haz clic para puntuar esta entrada!
(Votos: 2 Promedio: 4)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *