Skip to content

Instantly share code, notes, and snippets.

@atermenji
Created November 8, 2012 11:06
Show Gist options
  • Save atermenji/4038192 to your computer and use it in GitHub Desktop.
Save atermenji/4038192 to your computer and use it in GitHub Desktop.

Revisions

  1. atermenji revised this gist Nov 9, 2012. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion layout.xml
    Original file line number Diff line number Diff line change
    @@ -22,7 +22,7 @@
    android:paddingBottom="5dp"
    android:paddingRight="15dp" >

    <some.awesome.package.NavigationView
    <some.awesome.package.SomeAwesomeContentView
    android:id="@id/value"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
  2. atermenji revised this gist Nov 9, 2012. No changes.
  3. atermenji revised this gist Nov 9, 2012. 1 changed file with 35 additions and 4 deletions.
    39 changes: 35 additions & 4 deletions layout.xml
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,37 @@
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    <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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
    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.NavigationView
    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>
  4. atermenji revised this gist Nov 9, 2012. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions layout.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,6 @@
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:panel="http://schemas.android.com/apk/res/com.digitalfootsteps.android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
  5. atermenji revised this gist Nov 9, 2012. No changes.
  6. atermenji revised this gist Nov 9, 2012. 1 changed file with 2 additions and 1 deletion.
    3 changes: 2 additions & 1 deletion ExpandablePanel.java
    Original file line number Diff line number Diff line change
    @@ -133,6 +133,7 @@ public void onClick(View v) {
    mListener.onCollapse(mHandle, mContentContainer);
    }
    } else {
    ExpandablePanel.this.invalidate();
    animation = new ExpandAnimation(0, mContentWidth, mCollapsedHeight, mContentHeight);
    if (mListener != null) {
    mListener.onExpand(mHandle, mContentContainer);
    @@ -194,4 +195,4 @@ public interface OnExpandListener {
    public void onCollapse(View handle, View content);

    }
    }
    }
  7. atermenji revised this gist Nov 8, 2012. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions ExpandablePanel.java
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    package com.digitalfootsteps.android.ui.widget;
    package some.awesome.package;

    import android.content.Context;
    import android.content.res.TypedArray;
    @@ -9,8 +9,6 @@
    import android.view.animation.Transformation;
    import android.widget.RelativeLayout;

    import com.digitalfootsteps.android.R;

    public class ExpandablePanel extends RelativeLayout {

    private static final int DEFAULT_ANIM_DURATION = 500;
  8. atermenji revised this gist Nov 8, 2012. 1 changed file with 9 additions and 6 deletions.
    15 changes: 9 additions & 6 deletions ExpandablePanel.java
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    package some.awesome.package;
    package com.digitalfootsteps.android.ui.widget;

    import android.content.Context;
    import android.content.res.TypedArray;
    @@ -9,6 +9,8 @@
    import android.view.animation.Transformation;
    import android.widget.RelativeLayout;

    import com.digitalfootsteps.android.R;

    public class ExpandablePanel extends RelativeLayout {

    private static final int DEFAULT_ANIM_DURATION = 500;
    @@ -107,17 +109,18 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    int width = mHandle.getMeasuredWidth()
    + mContentContainer.getMeasuredWidth()
    + mContentContainer.getPaddingRight();

    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, mContentContainer.getMeasuredHeight());
    setMeasuredDimension(width, height);
    }

    private class PanelToggler implements OnClickListener {
  9. atermenji revised this gist Nov 8, 2012. 1 changed file with 1 addition and 3 deletions.
    4 changes: 1 addition & 3 deletions ExpandablePanel.java
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    package com.digitalfootsteps.android.ui.widget;
    package some.awesome.package;

    import android.content.Context;
    import android.content.res.TypedArray;
    @@ -9,8 +9,6 @@
    import android.view.animation.Transformation;
    import android.widget.RelativeLayout;

    import com.digitalfootsteps.android.R;

    public class ExpandablePanel extends RelativeLayout {

    private static final int DEFAULT_ANIM_DURATION = 500;
  10. atermenji created this gist Nov 8, 2012.
    198 changes: 198 additions & 0 deletions ExpandablePanel.java
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,198 @@
    package com.digitalfootsteps.android.ui.widget;

    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;

    import com.digitalfootsteps.android.R;

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

    int width = mHandle.getMeasuredWidth()
    + mContentContainer.getMeasuredWidth()
    + mContentContainer.getPaddingRight();

    if (mFirstOpen) {
    mContentContainer.getLayoutParams().width = 0;
    mContentContainer.getLayoutParams().height = mCollapsedHeight;
    mFirstOpen = false;
    }

    setMeasuredDimension(width, mContentContainer.getMeasuredHeight());
    }

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

    }
    }
    11 changes: 11 additions & 0 deletions attrs.xml
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,11 @@
    <?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>