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.
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
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;
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:
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
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);
}
}
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
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.
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)
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();
}
}
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.
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.