Note that there are some explanatory texts on larger screens.

plurals
  1. POHow to implement expandable panels in Android?
    primarykey
    data
    text
    <p>Is there an easy way to create expandable/collapsible blocks like seen in official market app?</p> <p>Screenshot of Market app, when you click on "More" button, the description section expands with animation:</p> <p><img src="https://i.stack.imgur.com/0hqno.jpg" alt="enter image description here"></p> <p>I know of <a href="http://developer.android.com/reference/android/widget/SlidingDrawer.html" rel="noreferrer">SlidingDrawer</a> but it doesn't seem to be suited for stuff like this--it's supposed to be put in overlay, and doesn't support half-open states.</p> <p><strong>Update:</strong></p> <p>Here's my half-working solution. It's a custom widget that extends <code>LinearLayout</code>. It kind-of works, but doesn't handle edge cases well, like content height smaller than <code>collapsedHeight</code> parameter. I'm sure with enough staring, digging in code and experimenting the quirks could be fixed. Was hoping to avoid doing that, and save some time by using a ready-made official or 3rd party solution. Anyway, here it is, code:</p> <pre><code>package com.example.androidapp.widgets; 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.Transformation; import android.widget.LinearLayout; import com.example.androidapp.R; public class ExpandablePanel extends LinearLayout { private final int mHandleId; private final int mContentId; private View mHandle; private View mContent; private boolean mExpanded = true; private int mCollapsedHeight = 0; private int mContentHeight = 0; 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); // How high the content should be in "collapsed" state mCollapsedHeight = (int) a.getDimension( R.styleable.ExpandablePanel_collapsedHeight, 0.0f); 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 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; mContentId = contentId; a.recycle(); } @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."); } mContent = findViewById(mContentId); if (mContent == null) { throw new IllegalArgumentException( "The content attribute is must refer to an" + " existing child."); } mHandle.setOnClickListener(new PanelToggler()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mContentHeight == 0) { // First, measure how high content wants to be mContent.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED); mContentHeight = mContent.getMeasuredHeight(); } // Then let the usual thing happen super.onMeasure(widthMeasureSpec, heightMeasureSpec); } private class PanelToggler implements OnClickListener { public void onClick(View v) { Animation a; if (mExpanded) { a = new ExpandAnimation(mContentHeight, mCollapsedHeight); } else { a = new ExpandAnimation(mCollapsedHeight, mContentHeight); } a.setDuration(500); mContent.startAnimation(a); mExpanded = !mExpanded; } } private class ExpandAnimation extends Animation { private final int mStartHeight; private final int mDeltaHeight; public ExpandAnimation(int startHeight, int endHeight) { mStartHeight = startHeight; mDeltaHeight = endHeight - startHeight; } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { android.view.ViewGroup.LayoutParams lp = mContent.getLayoutParams(); lp.height = (int) (mStartHeight + mDeltaHeight * interpolatedTime); mContent.setLayoutParams(lp); } @Override public boolean willChangeBounds() { // TODO Auto-generated method stub return true; } } } </code></pre> <p>Here's <code>res/values/attrs.xml</code>:</p> <pre><code>&lt;?xml version="1.0" encoding="utf-8"?&gt; &lt;resources&gt; &lt;declare-styleable name="ExpandablePanel"&gt; &lt;attr name="handle" format="reference" /&gt; &lt;attr name="content" format="reference" /&gt; &lt;attr name="collapsedHeight" format="dimension" /&gt; &lt;/declare-styleable&gt; &lt;/resources&gt; </code></pre> <p>And here's how I use it in layout:</p> <pre><code>&lt;com.example.androidapp.widgets.ExpandablePanel android:orientation="vertical" android:layout_height="wrap_content" android:layout_width="fill_parent" example:handle="@+id/expand" example:content="@+id/value" example:collapsedHeight="50dip"&gt; &lt;TextView android:id="@id/value" android:layout_width="fill_parent" android:layout_height="wrap_content" android:maxHeight="50dip" /&gt; &lt;Button android:id="@id/expand" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="More" /&gt; &lt;/com.example.androidapp.widgets.ExpandablePanel&gt; </code></pre>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload