Skip to content

Commit 6c72da8

Browse files
committed
some optimizations
1 parent e3018cc commit 6c72da8

File tree

2 files changed

+166
-120
lines changed

2 files changed

+166
-120
lines changed

expandablelayout/src/main/java/com/bosong/expandablelayout/ExpandableLayout.java

+165-119
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import android.support.annotation.Nullable;
2020
import android.support.v7.widget.RecyclerView;
2121
import android.util.AttributeSet;
22+
import android.util.Log;
2223
import android.view.View;
2324
import android.view.ViewGroup;
2425
import android.view.ViewParent;
@@ -32,26 +33,57 @@
3233
*/
3334

3435
public class ExpandableLayout extends LinearLayout{
36+
private static final String TAG = "ExpandableLayout";
3537
private final int ANIMATION_DURATION = 300;
3638

39+
/**
40+
* Height in collapsed state
41+
* 收缩起来的高度
42+
*/
3743
private int mCollapsedHeight;
44+
/**
45+
* Height in expanded state
46+
* 展开的高度
47+
*/
3848
private int mExpandedHeight;
39-
private int mCurrentHeight = -1; // Used for animating
40-
41-
private boolean mCollapsed = true; // current state: true -> collapsed
42-
private boolean mInitialMeasure = true;
43-
49+
/**
50+
* Used in OnMeasure method
51+
*/
52+
private int mCurrentHeight = -1;
53+
/**
54+
* current state: true -> collapsed
55+
* 标记当前的状态 true -> 收缩
56+
*/
57+
private boolean mCollapsed = true;
58+
/**
59+
* The parent which can scroll (RecyclerView, ListView)
60+
* 可滚动的父布局
61+
*/
4462
private ViewGroup mScrolledParent;
45-
private View mLastView; // Last view in collapsed state
63+
/**
64+
* Last visible view in collapsed state
65+
* 收缩后最下面的View,通过这个view计算得到收缩后的高度
66+
*/
67+
private View mLastView;
4668
private View mAnchorView;
69+
/**
70+
* Offset for collapsed height
71+
* 收缩起来高度的偏移
72+
*/
4773
private int mCollapsedOffset;
74+
/**
75+
* Offset for expanded height
76+
*
77+
*/
4878
private int mExpandedOffset;
4979
private boolean mExpandWithScroll; // true to scroll parent if expanded content exceeds parent's bottom edge
5080
private boolean mCollapseWithScroll; // true to scroll to ExpandableLayout's head
5181
private int mExpandScrollOffset;
5282
private int mCollapseScrollOffset;
5383
private boolean mAnimating;
5484

85+
private ExpandCollapseAnimation mAnimation;
86+
5587
private OnExpandListener mOnExpandListener;
5688

5789
public ExpandableLayout(Context context) {
@@ -61,6 +93,51 @@ public ExpandableLayout(Context context) {
6193
public ExpandableLayout(Context context, @Nullable AttributeSet attrs) {
6294
super(context, attrs);
6395
setOrientation(VERTICAL);
96+
97+
mAnimation = new ExpandCollapseAnimation();
98+
mAnimation.setFillAfter(true);
99+
mAnimation.setAnimationListener(new Animation.AnimationListener() {
100+
@Override
101+
public void onAnimationStart(Animation animation) {
102+
mAnimating = true;
103+
}
104+
105+
@Override
106+
public void onAnimationEnd(Animation animation) {
107+
// clear animation here to avoid repeated applyTransformation() calls
108+
clearAnimation();
109+
110+
if (mOnExpandListener != null) {
111+
mOnExpandListener.onExpand(!mCollapsed);
112+
}
113+
114+
if (mScrolledParent != null) {
115+
int scrollDistanceY = 0;
116+
if (mExpandWithScroll && !mCollapsed && expandShouldScrollParent()) {
117+
scrollDistanceY = getDescendantBottom((ViewGroup) mScrolledParent.getParent(), ExpandableLayout.this)
118+
- mScrolledParent.getBottom() + mExpandScrollOffset;
119+
} else if (mCollapseWithScroll && collapsedShouldScrollParent()) {
120+
scrollDistanceY = getDescendantTop((ViewGroup) mScrolledParent.getParent(), ExpandableLayout.this)
121+
- mScrolledParent.getTop() - mCollapseScrollOffset;
122+
}
123+
124+
if (mScrolledParent instanceof RecyclerView) {
125+
RecyclerView recyclerView = (RecyclerView) mScrolledParent;
126+
recyclerView.smoothScrollBy(0, scrollDistanceY);
127+
} else if (mScrolledParent instanceof AbsListView) {
128+
AbsListView listView = (AbsListView) mScrolledParent;
129+
listView.smoothScrollBy(scrollDistanceY, ANIMATION_DURATION);
130+
}
131+
}
132+
133+
mAnimating = false;
134+
}
135+
136+
@Override
137+
public void onAnimationRepeat(Animation animation) {
138+
139+
}
140+
});
64141
}
65142

66143
@Override
@@ -71,15 +148,34 @@ protected void onAttachedToWindow() {
71148
}
72149

73150
/**
74-
* Set a View, we will collapsed Views after this View.
151+
* Set a View, we will collapse views under this view in the screen.
75152
* @param view Last view in collapsed state
76153
*/
77154
public void setCollapsedEdgeView(View view){
78-
if(view == null){
155+
if(view == null || view.equals(mLastView)){
79156
return;
80157
}
81158
mLastView = view;
82-
requestLayout();
159+
160+
measure(0, 0);
161+
int width = getMeasuredWidth();
162+
int height = getMeasuredHeight();
163+
layout(0, 0, width, height);
164+
165+
int collapsedHeight = getDescendantBottom(ExpandableLayout.this, mLastView) + mCollapsedOffset;
166+
if(collapsedHeight > 0 && mCollapsedHeight != collapsedHeight) {
167+
Log.d(TAG, "collapsedHeight ->>>>> " + collapsedHeight);
168+
mCollapsedHeight = collapsedHeight;
169+
mCurrentHeight = collapsedHeight;
170+
}
171+
}
172+
173+
public void setCollapsedEdgeView(View view, int collapsedOffset) {
174+
if(view == null) {
175+
return;
176+
}
177+
mCollapsedOffset = collapsedOffset;
178+
setCollapsedEdgeView(view);
83179
}
84180

85181
// Not Supported
@@ -119,133 +215,75 @@ public void setCollapseWithScroll(boolean scroll, int offset) {
119215
}
120216

121217
public void initState(boolean collapsed){
122-
if(collapsed){
123-
mCurrentHeight = mCollapsedHeight;
218+
if(collapsed){ // init as collapsed
219+
collapse(false);
124220
}else{
125-
mCurrentHeight = mExpandedHeight;
221+
expand(false);
126222
}
127-
mCollapsed = collapsed;
128-
requestLayout();
129223
}
130224

131225
@Override
132226
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
133227
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
134228

135-
int width = getMeasuredWidth();
136-
int height = getMeasuredHeight();
137-
138-
if(mExpandedHeight == 0 || mExpandedHeight > height) {
139-
mExpandedHeight = height;
140-
}
141-
142-
if(mCurrentHeight >= 0 && height != mCurrentHeight) {
229+
mExpandedHeight = getMeasuredHeight();
230+
if(mCurrentHeight >= 0 && mExpandedHeight != mCurrentHeight) {
231+
int width = getMeasuredWidth();
143232
setMeasuredDimension(width, mCurrentHeight);
233+
Log.d(TAG, "current height ->>>>>> " + mCurrentHeight);
144234
}
145235
}
146236

147-
@Override
148-
protected void onLayout(boolean changed, int l, int t, int r, int b) {
149-
super.onLayout(changed, l, t, r, b);
150-
if(!mAnimating && mCollapsed) {
151-
if(mLastView != null) {
152-
int collapsedHeight = getDescendantBottom(ExpandableLayout.this, mLastView);
153-
if(collapsedHeight > 0 && mCollapsedHeight != collapsedHeight) {
154-
mCollapsedHeight = collapsedHeight;
155-
mCurrentHeight = collapsedHeight;
156-
mLastView.post(new Runnable() {
157-
@Override
158-
public void run() {
159-
requestLayout();
160-
}
161-
});
162-
}
163-
} else {
164-
mCollapsedHeight = 0;
165-
mCurrentHeight = 0;
166-
}
237+
public void toggle(boolean withAnimation){
238+
if(mAnimating) {
239+
return;
167240
}
168-
169-
}
170-
171-
public void toggle(){
172-
mInitialMeasure = false;
173-
174-
clearAnimation();
175-
Animation animation;
176-
if(mCollapsed){ // need expand
177-
animation = new ExpandCollapseAnimation(getHeight(), mExpandedHeight + mExpandedOffset);
178-
}else { // need collapsed
179-
animation = new ExpandCollapseAnimation(getHeight(), mCollapsedHeight + mCollapsedOffset);
241+
if (mCollapsed) { // need expand
242+
expand(withAnimation);
243+
} else { // need collapsed
244+
collapse(withAnimation);
180245
}
246+
}
181247

182-
mCollapsed = !mCollapsed;
183-
animation.setFillAfter(true);
184-
animation.setAnimationListener(new Animation.AnimationListener() {
185-
@Override
186-
public void onAnimationStart(Animation animation) {
187-
mAnimating = true;
188-
}
189-
190-
@Override
191-
public void onAnimationEnd(Animation animation) {
192-
// clear animation here to avoid repeated applyTransformation() calls
193-
clearAnimation();
194-
195-
if(mOnExpandListener != null) {
196-
mOnExpandListener.onExpand(!mCollapsed);
197-
}
198-
199-
if(mScrolledParent != null){
200-
int scrollDistanceY = 0;
201-
if(mExpandWithScroll && !mCollapsed && expandShouldScrollParent()){
202-
scrollDistanceY = getDescendantBottom((ViewGroup) mScrolledParent.getParent(), ExpandableLayout.this)
203-
- mScrolledParent.getBottom() + mExpandScrollOffset;
204-
}else if(mCollapseWithScroll && collapsedShouldScrollParent()){
205-
scrollDistanceY = getDescendantTop((ViewGroup) mScrolledParent.getParent(), ExpandableLayout.this)
206-
- mScrolledParent.getTop() - mCollapseScrollOffset;
207-
}
208-
209-
if(mScrolledParent instanceof RecyclerView){
210-
RecyclerView recyclerView = (RecyclerView) mScrolledParent;
211-
recyclerView.smoothScrollBy(0, scrollDistanceY);
212-
}else if(mScrolledParent instanceof AbsListView) {
213-
AbsListView listView = (AbsListView) mScrolledParent;
214-
listView.smoothScrollBy(scrollDistanceY, ANIMATION_DURATION);
215-
}
216-
}
217-
218-
mAnimating = false;
219-
}
220-
221-
@Override
222-
public void onAnimationRepeat(Animation animation) {
223-
224-
}
225-
});
226-
227-
startAnimation(animation);
228-
229-
// Collapse/Expand with no animation
230-
// ViewGroup.LayoutParams lp = getLayoutParams();
231-
// if(mCollapsed){
232-
// lp.height = mExpandedHeight;
233-
// }else{
234-
// lp.height = mCollapsedHeight;
235-
// }
236-
// setLayoutParams(lp);
237-
// mCollapsed = !mCollapsed;
248+
public void toggleWithAnimation() {
249+
toggle(true);
238250
}
239251

240252
/**
241253
* Toggle with offsets
242254
* @param expandedOffset Add offset to expanded height
243255
* @param collapsedOffset Add offset to collapsed height
244256
*/
245-
public void toggle(int expandedOffset, int collapsedOffset){
257+
public void toggleWithOffset(int expandedOffset, int collapsedOffset){
246258
mExpandedOffset = expandedOffset;
247259
mCollapsedOffset = collapsedOffset;
248-
toggle();
260+
mExpandedHeight += mExpandedOffset;
261+
mCollapsedHeight += mCollapsedOffset;
262+
toggle(true);
263+
}
264+
265+
public void expand(boolean withAnimation) {
266+
if(withAnimation && mAnimation != null) {
267+
clearAnimation();
268+
mAnimation.setData(getHeight(), mExpandedHeight);
269+
startAnimation(mAnimation);
270+
} else {
271+
mCurrentHeight = mExpandedHeight;
272+
requestLayout();
273+
}
274+
mCollapsed = false;
275+
}
276+
277+
public void collapse(boolean withAnimation) {
278+
if(withAnimation && mAnimation != null) {
279+
clearAnimation();
280+
mAnimation.setData(getHeight(), mCollapsedHeight);
281+
startAnimation(mAnimation);
282+
} else {
283+
mCurrentHeight = mCollapsedHeight;
284+
requestLayout();
285+
}
286+
mCollapsed = true;
249287
}
250288

251289
private boolean expandShouldScrollParent() {
@@ -315,20 +353,27 @@ private int getDescendantTop(ViewGroup parent, View view) {
315353
}else{
316354
return top + getDescendantTop(parent, (View) view.getParent());
317355
}
318-
} else { // view.getParent() is a ViewRootImpl instance means that parent does not contains view
356+
} else { // view.getParent() is a ViewRootImpl instance means that parent does not contain this view
319357
throw new RuntimeException("view must be included in the parent");
320358
}
321-
322359
}
323360

324-
class ExpandCollapseAnimation extends Animation {
325-
private final int mStartHeight;
326-
private final int mEndHeight;
361+
private class ExpandCollapseAnimation extends Animation {
362+
private int mStartHeight;
363+
private int mEndHeight;
364+
365+
public ExpandCollapseAnimation() {
366+
setDuration(ANIMATION_DURATION);
367+
}
327368

328369
public ExpandCollapseAnimation(int startHeight, int endHeight) {
370+
setData(startHeight, endHeight);
371+
setDuration(ANIMATION_DURATION);
372+
}
373+
374+
public void setData(int startHeight, int endHeight) {
329375
mStartHeight = startHeight;
330376
mEndHeight = endHeight;
331-
setDuration(ANIMATION_DURATION);
332377
}
333378

334379
@Override
@@ -342,11 +387,12 @@ protected void applyTransformation(float interpolatedTime, Transformation t) {
342387
}
343388

344389
mCurrentHeight = newHeight + heightOffset;
390+
345391
requestLayout();
346392
}
347393
}
348394

349-
public interface OnExpandListener{
395+
interface OnExpandListener{
350396
void onExpand(boolean expand);
351397

352398
/**

sample/src/main/java/com/bosong/sample/MyAdapter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,6 @@ public int getItemCount() {
5454

5555
private void onExpandClick(int position, ExpandableLayout expandableLayout) {
5656
mExpandState[position] = !mExpandState[position];
57-
expandableLayout.toggle();
57+
expandableLayout.toggleWithAnimation();
5858
}
5959
}

0 commit comments

Comments
 (0)