-
-
Save vothanhkiet/98e5209c6e352d59676d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="ExpandablePanel"> | |
<attr name="handle" format="reference" /> | |
<attr name="content" format="reference" /> | |
<attr name="contentContainer" format="reference" /> | |
<attr name="animationDuration" format="integer"/> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package some.awesome.package; | |
import android.content.Context; | |
import android.content.res.TypedArray; | |
import android.util.AttributeSet; | |
import android.view.View; | |
import android.view.animation.Animation; | |
import android.view.animation.Animation.AnimationListener; | |
import android.view.animation.Transformation; | |
import android.widget.RelativeLayout; | |
public class ExpandablePanel extends RelativeLayout { | |
private static final int DEFAULT_ANIM_DURATION = 500; | |
private final int mHandleId; | |
private final int mContentContainerId; | |
private final int mContentId; | |
private View mHandle; | |
private View mContentContainer; | |
private View mContent; | |
private boolean mExpanded = false; | |
private boolean mFirstOpen = true; | |
private int mCollapsedHeight; | |
private int mContentHeight; | |
private int mContentWidth; | |
private int mAnimationDuration = 0; | |
private OnExpandListener mListener; | |
public ExpandablePanel(Context context) { | |
this(context, null); | |
} | |
public ExpandablePanel(Context context, AttributeSet attrs) { | |
super(context, attrs); | |
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ExpandablePanel, 0, 0); | |
mAnimationDuration = a.getInteger(R.styleable.ExpandablePanel_animationDuration, DEFAULT_ANIM_DURATION); | |
int handleId = a.getResourceId(R.styleable.ExpandablePanel_handle, 0); | |
if (handleId == 0) { | |
throw new IllegalArgumentException( | |
"The handle attribute is required and must refer to a valid child."); | |
} | |
int contentContainerId = a.getResourceId(R.styleable.ExpandablePanel_contentContainer, 0); | |
if (contentContainerId == 0) { | |
throw new IllegalArgumentException("The content attribute is required and must refer to a valid child."); | |
} | |
int contentId = a.getResourceId(R.styleable.ExpandablePanel_content, 0); | |
if (contentId == 0) { | |
throw new IllegalArgumentException("The content attribute is required and must refer to a valid child."); | |
} | |
mHandleId = handleId; | |
mContentContainerId = contentContainerId; | |
mContentId = contentId; | |
a.recycle(); | |
} | |
public void setOnExpandListener(OnExpandListener listener) { | |
mListener = listener; | |
} | |
public void setAnimationDuration(int animationDuration) { | |
mAnimationDuration = animationDuration; | |
} | |
@Override | |
protected void onFinishInflate() { | |
super.onFinishInflate(); | |
mHandle = findViewById(mHandleId); | |
if (mHandle == null) { | |
throw new IllegalArgumentException("The handle attribute is must refer to an existing child."); | |
} | |
mContentContainer = findViewById(mContentContainerId); | |
if (mContentContainer == null) { | |
throw new IllegalArgumentException("The content container attribute must refer to an existing child."); | |
} | |
mContent = findViewById(mContentId); | |
if (mContentContainer == null) { | |
throw new IllegalArgumentException("The content attribute must refer to an existing child."); | |
} | |
mContent.setVisibility(View.INVISIBLE); | |
mHandle.setOnClickListener(new PanelToggler()); | |
} | |
@Override | |
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | |
mContentContainer.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); | |
mHandle.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec); | |
mCollapsedHeight = mHandle.getMeasuredHeight(); | |
mContentWidth = mContentContainer.getMeasuredWidth(); | |
mContentHeight = mContentContainer.getMeasuredHeight(); | |
super.onMeasure(widthMeasureSpec, heightMeasureSpec); | |
if (mFirstOpen) { | |
mContentContainer.getLayoutParams().width = 0; | |
mContentContainer.getLayoutParams().height = mCollapsedHeight; | |
mFirstOpen = false; | |
} | |
int width = mHandle.getMeasuredWidth() | |
+ mContentContainer.getMeasuredWidth() | |
+ mContentContainer.getPaddingRight(); | |
int height = mContentContainer.getMeasuredHeight() + mContentContainer.getPaddingBottom(); | |
setMeasuredDimension(width, height); | |
} | |
private class PanelToggler implements OnClickListener { | |
@Override | |
public void onClick(View v) { | |
Animation animation; | |
if (mExpanded) { | |
mContent.setVisibility(View.INVISIBLE); | |
animation = new ExpandAnimation(mContentWidth, 0, mContentHeight, mCollapsedHeight); | |
if (mListener != null) { | |
mListener.onCollapse(mHandle, mContentContainer); | |
} | |
} else { | |
ExpandablePanel.this.invalidate(); | |
animation = new ExpandAnimation(0, mContentWidth, mCollapsedHeight, mContentHeight); | |
if (mListener != null) { | |
mListener.onExpand(mHandle, mContentContainer); | |
} | |
} | |
animation.setDuration(mAnimationDuration); | |
animation.setAnimationListener(new AnimationListener() { | |
@Override | |
public void onAnimationStart(Animation animation) { | |
} | |
@Override | |
public void onAnimationRepeat(Animation animation) { | |
} | |
@Override | |
public void onAnimationEnd(Animation animation) { | |
mExpanded = !mExpanded; | |
if (mExpanded) { | |
mContent.setVisibility(View.VISIBLE); | |
} | |
} | |
}); | |
mContentContainer.startAnimation(animation); | |
} | |
} | |
private class ExpandAnimation extends Animation { | |
private final int mStartWidth; | |
private final int mDeltaWidth; | |
private final int mStartHeight; | |
private final int mDeltaHeight; | |
public ExpandAnimation(int startWidth, int endWidth, int startHeight, int endHeight) { | |
mStartWidth = startWidth; | |
mDeltaWidth = endWidth - startWidth; | |
mStartHeight = startHeight; | |
mDeltaHeight = endHeight - startHeight; | |
} | |
@Override | |
protected void applyTransformation(float interpolatedTime, Transformation t) { | |
android.view.ViewGroup.LayoutParams lp = mContentContainer.getLayoutParams(); | |
lp.width = (int) (mStartWidth + mDeltaWidth * interpolatedTime); | |
lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime); | |
mContentContainer.setLayoutParams(lp); | |
} | |
@Override | |
public boolean willChangeBounds() { | |
return true; | |
} | |
} | |
public interface OnExpandListener { | |
public void onExpand(View handle, View content); | |
public void onCollapse(View handle, View content); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<some.awesome.package.ExpandablePanel | |
xmlns:android="http://schemas.android.com/apk/res/android" | |
xmlns:panel="http://schemas.android.com/apk/res/com.digitalfootsteps.android" | |
android:id="@+id/expandable_panel" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_alignParentRight="true" | |
android:layout_marginRight="10dp" | |
android:layout_marginTop="10dp" | |
android:background="@drawable/some_awesome_background" | |
panel:content="@+id/value" | |
panel:contentContainer="@+id/content_container" | |
panel:handle="@+id/expand" > | |
<FrameLayout | |
android:id="@id/content_container" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_alignParentTop="true" | |
android:layout_toLeftOf="@id/expand" | |
android:paddingBottom="5dp" | |
android:paddingRight="15dp" > | |
<some.awesome.package.SomeAwesomeContentView | |
android:id="@id/value" | |
android:layout_width="match_parent" | |
android:layout_height="match_parent" /> | |
</FrameLayout> | |
<Button | |
android:id="@id/expand" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content" | |
android:layout_alignParentRight="true" | |
android:layout_alignParentTop="true" /> | |
</some.awesome.package.ExpandablePanel> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment