66import android .animation .ValueAnimator ;
77import android .content .Context ;
88import android .content .res .TypedArray ;
9- import android .os .Build ;
109import android .support .annotation .NonNull ;
1110import android .support .annotation .Nullable ;
1211import android .util .AttributeSet ;
1312import android .view .ViewGroup ;
1413import android .view .animation .AccelerateDecelerateInterpolator ;
1514import android .widget .TextView ;
1615
17- import java .lang .reflect .Field ;
1816import java .util .ArrayList ;
1917import java .util .List ;
2018
2119import at .blogc .expandabletextview .BuildConfig ;
2220import at .blogc .expandabletextview .R ;
2321
2422/**
25- * Copyright (C) 2016 Cliff Ophalvens (Blogc.at)
23+ * Copyright (C) 2017 Cliff Ophalvens (Blogc.at)
2624 *
2725 * Licensed under the Apache License, Version 2.0 (the "License");
2826 * you may not use this file except in compliance with the License.
4038 */
4139public class ExpandableTextView extends TextView
4240{
43- // copy off TextView.LINES
44- private static final int MAXMODE_LINES = 1 ;
45-
4641 private final List <OnExpandListener > onExpandListeners ;
4742 private TimeInterpolator expandInterpolator ;
4843 private TimeInterpolator collapseInterpolator ;
@@ -75,7 +70,7 @@ public ExpandableTextView(final Context context, @Nullable final AttributeSet at
7570 // keep the original value of maxLines
7671 this .maxLines = this .getMaxLines ();
7772
78- // create bucket for OnExpandListener instances
73+ // create bucket of OnExpandListener instances
7974 this .onExpandListeners = new ArrayList <>();
8075
8176 // create default interpolators
@@ -84,32 +79,18 @@ public ExpandableTextView(final Context context, @Nullable final AttributeSet at
8479 }
8580
8681 @ Override
87- public int getMaxLines ( )
82+ protected void onMeasure ( final int widthMeasureSpec , int heightMeasureSpec )
8883 {
89- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .JELLY_BEAN )
84+ // if this TextView is collapsed and maxLines = 0,
85+ // than make its height equals to zero
86+ if (this .maxLines == 0 && !this .expanded && !this .animating )
9087 {
91- return super . getMaxLines ( );
88+ heightMeasureSpec = MeasureSpec . makeMeasureSpec ( 0 , MeasureSpec . EXACTLY );
9289 }
9390
94- try
95- {
96- final Field mMaxMode = TextView .class .getField ("mMaxMode" );
97- mMaxMode .setAccessible (true );
98- final Field mMaximum = TextView .class .getField ("mMaximum" );
99- mMaximum .setAccessible (true );
100-
101- final int mMaxModeValue = (int ) mMaxMode .get (this );
102- final int mMaximumValue = (int ) mMaximum .get (this );
103-
104- return mMaxModeValue == MAXMODE_LINES ? mMaximumValue : -1 ;
105- }
106- catch (final Exception e )
107- {
108- return -1 ;
109- }
91+ super .onMeasure (widthMeasureSpec , heightMeasureSpec );
11092 }
11193
112-
11394 //region public helper methods
11495
11596 /**
@@ -131,12 +112,10 @@ public boolean expand()
131112 {
132113 if (!this .expanded && !this .animating && this .maxLines >= 0 )
133114 {
134- this .animating = true ;
135-
136115 // notify listener
137116 this .notifyOnExpand ();
138117
139- // get collapsed height
118+ // measure collapsed height
140119 this .measure
141120 (
142121 MeasureSpec .makeMeasureSpec (this .getMeasuredWidth (), MeasureSpec .EXACTLY ),
@@ -145,10 +124,13 @@ public boolean expand()
145124
146125 this .collapsedHeight = this .getMeasuredHeight ();
147126
127+ // indicate that we are now animating
128+ this .animating = true ;
129+
148130 // set maxLines to MAX Integer, so we can calculate the expanded height
149131 this .setMaxLines (Integer .MAX_VALUE );
150132
151- // get expanded height
133+ // measure expanded height
152134 this .measure
153135 (
154136 MeasureSpec .makeMeasureSpec (this .getMeasuredWidth (), MeasureSpec .EXACTLY ),
@@ -164,17 +146,20 @@ public boolean expand()
164146 @ Override
165147 public void onAnimationUpdate (final ValueAnimator animation )
166148 {
167- final ViewGroup .LayoutParams layoutParams = ExpandableTextView .this .getLayoutParams ();
168- layoutParams .height = (int ) animation .getAnimatedValue ();
169- ExpandableTextView .this .setLayoutParams (layoutParams );
149+ ExpandableTextView .this .setHeight ((int ) animation .getAnimatedValue ());
170150 }
171151 });
172152
153+ // wait for the animation to end
173154 valueAnimator .addListener (new AnimatorListenerAdapter ()
174155 {
175156 @ Override
176157 public void onAnimationEnd (final Animator animation )
177158 {
159+ // reset min & max height (previously set with setHeight() method)
160+ ExpandableTextView .this .setMaxHeight (Integer .MAX_VALUE );
161+ ExpandableTextView .this .setMinHeight (0 );
162+
178163 // if fully expanded, set height to WRAP_CONTENT, because when rotating the device
179164 // the height calculated with this ValueAnimator isn't correct anymore
180165 final ViewGroup .LayoutParams layoutParams = ExpandableTextView .this .getLayoutParams ();
@@ -209,44 +194,44 @@ public boolean collapse()
209194 {
210195 if (this .expanded && !this .animating && this .maxLines >= 0 )
211196 {
212- this .animating = true ;
213-
214197 // notify listener
215198 this .notifyOnCollapse ();
216199
217- // get expanded height
200+ // measure expanded height
218201 final int expandedHeight = this .getMeasuredHeight ();
219202
203+ // indicate that we are now animating
204+ this .animating = true ;
205+
220206 // animate from expanded height to collapsed height
221207 final ValueAnimator valueAnimator = ValueAnimator .ofInt (expandedHeight , this .collapsedHeight );
222208 valueAnimator .addUpdateListener (new ValueAnimator .AnimatorUpdateListener ()
223209 {
224210 @ Override
225211 public void onAnimationUpdate (final ValueAnimator animation )
226212 {
227- final ViewGroup .LayoutParams layoutParams = ExpandableTextView .this .getLayoutParams ();
228- layoutParams .height = (int ) animation .getAnimatedValue ();
229- ExpandableTextView .this .setLayoutParams (layoutParams );
213+ ExpandableTextView .this .setHeight ((int ) animation .getAnimatedValue ());
230214 }
231215 });
232216
217+ // wait for the animation to end
233218 valueAnimator .addListener (new AnimatorListenerAdapter ()
234219 {
235220 @ Override
236221 public void onAnimationEnd (final Animator animation )
237222 {
238- // set maxLines to original value
223+ // keep track of current status
224+ ExpandableTextView .this .expanded = false ;
225+ ExpandableTextView .this .animating = false ;
226+
227+ // set maxLines back to original value
239228 ExpandableTextView .this .setMaxLines (ExpandableTextView .this .maxLines );
240229
241- // if fully collapsed, set height to WRAP_CONTENT, because when rotating the device
242- // the height calculated with this ValueAnimator isn't correct anymore
230+ // if fully collapsed, set height back to WRAP_CONTENT, because when rotating the device
231+ // the height previously calculated with this ValueAnimator isn't correct anymore
243232 final ViewGroup .LayoutParams layoutParams = ExpandableTextView .this .getLayoutParams ();
244233 layoutParams .height = ViewGroup .LayoutParams .WRAP_CONTENT ;
245234 ExpandableTextView .this .setLayoutParams (layoutParams );
246-
247- // keep track of current status
248- ExpandableTextView .this .expanded = false ;
249- ExpandableTextView .this .animating = false ;
250235 }
251236 });
252237
0 commit comments