Thursday 7 July 2011

Android Simple Accordion or Collapse Panel

After a wise looking on internet I accept that fact , creating an Accordion component  in android which can be easily comparable with HTML/Java Script counterpart is never an easy task.
So, I have tried to develop such a component which may fulfill your requirement. This may not be the best way but one of the ways , for sure.

Here are couple of screen shots of the example :


First create a layout xml and give it the name "accordian.xml". Put the bellow code in it :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent" android:background="#ffffff"
android:layout_height="fill_parent">
<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center">
<TextView android:text="Location" android:id="@+id/text1" android:padding="5dip" android:background="#005555"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/panel1" android:visibility="gone"  android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_margin="2dip" android:text="Item1" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item2" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item3" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>
    </LinearLayout>
    <LinearLayout android:layout_marginTop="2dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center">
<TextView android:text="Symptons" android:id="@+id/text2" android:padding="5dip" android:background="#005555"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/panel2" android:visibility="gone" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_margin="2dip" android:text="Item1" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item2" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item3" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>
    </LinearLayout>
    <LinearLayout android:layout_marginTop="2dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center">
<TextView android:text="Location" android:id="@+id/text3" android:padding="5dip" android:background="#005555"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/panel3" android:visibility="gone" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_margin="2dip" android:text="Item1" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item2" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item3" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>
    </LinearLayout>
    <LinearLayout android:layout_marginTop="2dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center">
<TextView android:text="Character" android:id="@+id/text4" android:padding="5dip" android:background="#005555"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/panel4" android:visibility="gone" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_margin="2dip" android:text="Item1" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item2" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item3" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>
    </LinearLayout>
    <LinearLayout android:layout_marginTop="2dip" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_gravity="center">
<TextView android:text="Factors" android:id="@+id/text5" android:padding="5dip" android:background="#005555"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/panel5" android:visibility="gone" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content">
<TextView android:layout_margin="2dip" android:text="Item1" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item2" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:layout_margin="2dip" android:text="Item3" android:padding="5dip" android:background="#777777"  android:layout_width="fill_parent" android:layout_height="wrap_content" />
</LinearLayout>
    </LinearLayout>
<ListView android:id="@android:id/list" android:layout_width="fill_parent" android:visibility="gone"
android:layout_height="340dip"  android:drawSelectorOnTop="false"  android:dividerHeight="0px"/>
</LinearLayout>


Create java file and name it "AccordianSampleActivity.java" and put the bellow code in it :


import android.app.ListActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.animation.ScaleAnimation;
import android.view.animation.Transformation;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.LinearLayout.LayoutParams;

public class AccordianSampleActivity extends ListActivity implements OnClickListener
{
public OnLongClickListener longClickListner;
LinearLayout panel1,panel2,panel3,panel4,panel5;
TextView text1,text2,text3,text4,text5;
View openLayout;

@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.accordian);

panel1 = (LinearLayout) findViewById(R.id.panel1);
panel2 = (LinearLayout) findViewById(R.id.panel2);
panel3 = (LinearLayout) findViewById(R.id.panel3);
panel4 = (LinearLayout) findViewById(R.id.panel4);
panel5 = (LinearLayout) findViewById(R.id.panel5);

//panel1.setVisibility(View.VISIBLE);

//panel1.setVisibility(View.VISIBLE);

//Log.v("CZ","height at first ..." + panel1.getMeasuredHeight());

text1 = (TextView) findViewById(R.id.text1);
text2 = (TextView) findViewById(R.id.text2);
text3 = (TextView) findViewById(R.id.text3);
text4 = (TextView) findViewById(R.id.text4);
text5 = (TextView) findViewById(R.id.text5);

text1.setOnClickListener(this);
text2.setOnClickListener(this);
text3.setOnClickListener(this);
text4.setOnClickListener(this);
text5.setOnClickListener(this);

}
@Override
public void onClick(View v)
{
hideOthers(v);
}
private void hideThemAll()
{
if(openLayout == null) return;
if(openLayout == panel1)
panel1.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f, 500, panel1, true));
if(openLayout == panel2)
panel2.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f, 500, panel2, true));
if(openLayout == panel3)
panel3.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f, 500, panel3, true));
if(openLayout == panel4)
panel4.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f, 500, panel4, true));
if(openLayout == panel5)
panel5.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f, 500, panel5, true));
}
private void hideOthers(View layoutView)
{
{
int v;
if(layoutView.getId() == R.id.text1)
{
v = panel1.getVisibility();
if(v != View.VISIBLE)
{
panel1.setVisibility(View.VISIBLE);
Log.v("CZ","height..." + panel1.getHeight());
}

//panel1.setVisibility(View.GONE);
//Log.v("CZ","again height..." + panel1.getHeight());
hideThemAll();
if(v != View.VISIBLE)
{
panel1.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f, 0.0f, 500, panel1, true));
}
}
else if(layoutView.getId() == R.id.text2)
{
v = panel2.getVisibility();
hideThemAll();
if(v != View.VISIBLE)
{
panel2.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f, 0.0f, 500, panel2, true));
}
}
else if(layoutView.getId() == R.id.text3)
{
v = panel3.getVisibility();
hideThemAll();
if(v != View.VISIBLE)
{
panel3.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f, 0.0f, 500, panel3, true));
}
}
else if(layoutView.getId() == R.id.text4)
{
v = panel4.getVisibility();
hideThemAll();
if(v != View.VISIBLE)
{
panel4.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f, 0.0f, 500, panel4, true));
}
}
else if(layoutView.getId() == R.id.text5)
{
v = panel5.getVisibility();
hideThemAll();
if(v != View.VISIBLE)
{
panel5.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f, 0.0f, 500, panel5, true));
}
}
}
}

public class ScaleAnimToHide extends ScaleAnimation
{

       private View mView;

       private LayoutParams mLayoutParams;

       private int mMarginBottomFromY, mMarginBottomToY;

       private boolean mVanishAfter = false;

       public ScaleAnimToHide(float fromX, float toX, float fromY, float toY, int duration, View view,boolean vanishAfter)
       {
           super(fromX, toX, fromY, toY);
           setDuration(duration);
           openLayout = null;
           mView = view;
           mVanishAfter = vanishAfter;
           mLayoutParams = (LayoutParams) view.getLayoutParams();
           int height = mView.getHeight();
           mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin - height;
           mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) - height;
         
           Log.v("CZ","height..." + height + " , mMarginBottomFromY...." + mMarginBottomFromY  + " , mMarginBottomToY.." +mMarginBottomToY);
       }

       @Override
       protected void applyTransformation(float interpolatedTime, Transformation t)
       {
           super.applyTransformation(interpolatedTime, t);
           if (interpolatedTime < 1.0f)
           {
               int newMarginBottom = mMarginBottomFromY + (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime);
               mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,mLayoutParams.rightMargin, newMarginBottom);
               mView.getParent().requestLayout();
               //Log.v("CZ","newMarginBottom..." + newMarginBottom + " , mLayoutParams.topMargin..." + mLayoutParams.topMargin);
           }
           else if (mVanishAfter)
           {
               mView.setVisibility(View.GONE);
           }
       }
}
public class ScaleAnimToShow extends ScaleAnimation
{

       private View mView;

       private LayoutParams mLayoutParams;

       private int mMarginBottomFromY, mMarginBottomToY;

       private boolean mVanishAfter = false;

       public ScaleAnimToShow(float toX, float fromX, float toY, float fromY, int duration, View view,boolean vanishAfter)
       {
           super(fromX, toX, fromY, toY);
           openLayout = view;
           setDuration(duration);
           mView = view;
           mVanishAfter = vanishAfter;
           mLayoutParams = (LayoutParams) view.getLayoutParams();
           mView.setVisibility(View.VISIBLE);
           int height = mView.getHeight();
           //mMarginBottomFromY = (int) (height * fromY) + mLayoutParams.bottomMargin + height;
           //mMarginBottomToY = (int) (0 - ((height * toY) + mLayoutParams.bottomMargin)) + height;
         
           mMarginBottomFromY = 0;
           mMarginBottomToY = height;
         
           Log.v("CZ",".................height..." + height + " , mMarginBottomFromY...." + mMarginBottomFromY  + " , mMarginBottomToY.." +mMarginBottomToY);
       }

       @Override
       protected void applyTransformation(float interpolatedTime, Transformation t)
       {
           super.applyTransformation(interpolatedTime, t);
           if (interpolatedTime < 1.0f)
           {
               int newMarginBottom = (int) ((mMarginBottomToY - mMarginBottomFromY) * interpolatedTime) - mMarginBottomToY;
               mLayoutParams.setMargins(mLayoutParams.leftMargin, mLayoutParams.topMargin,mLayoutParams.rightMargin, newMarginBottom);
               mView.getParent().requestLayout();
               //Log.v("CZ","newMarginBottom..." + newMarginBottom + " , mLayoutParams.topMargin..." + mLayoutParams.topMargin);
           }
       }

}
}


Before compilation , make sure you have made an entry in the manifest file for the new activity.

In this app , I have implemented the accordion panel what I just said..
https://market.android.com/details?id=com.lcs.mmp.lite

21 comments:

  1. Thank you for sample...can we fill generic item? for example we get item in database then add into accordion

    ReplyDelete
  2. why not ?? Then you need to add items dynamically instead of from xml..

    ReplyDelete
  3. hello, could you help me? ... I would move that the third with a TextView animation above and replace the first two (in practice translate into first position and making her and see only the fourth TextView) ... you know how to do? I have problems :-( thank you very much

    ReplyDelete
  4. How can I use a scroll bar to the accordian in this code if I have lot of data into the panels?

    ReplyDelete
  5. There is some difference in the animation that has been used in the app and the code above . Using the code above when the panel is being hidden it gives an effect of being squeezed .However in the application Manage My Pain Lite , it is not so. Can you tell me how did you do that?

    ReplyDelete
  6. Hello!

    I have a problem on ScaleAnimToShow(),

    mLayoutParams = (LayoutParams) view.getLayoutParams();

    error:
    06-11 06:13:00.448: E/AndroidRuntime(17555): java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams cannot be cast to android.app.ActionBar$LayoutParams

    ReplyDelete
    Replies
    1. how did you fixed this issue sir?

      Delete
  7. Awesome code dude. Thanks!

    ReplyDelete
  8. Dude.. You're code is awesome! Thanks you seriously helped a lot. At the moment I am trying to make it so I can have more than 1 panel open at a time, but I thought I'd take a break to show my appreciation. Thanks again bro.

    ReplyDelete
  9. Code is definitely not the same as "Manage My Pain Lite". On first open the panels jump, once they've all being opened and closed works ok, but initial show is buggy. Any chance you could post the bugfix?

    ReplyDelete
  10. I am new to android especially in animation.. i have one question, why dont you called the applytransformation() method in your program?

    ReplyDelete
  11. Thank u sir your code is awesome........

    ReplyDelete
  12. tnx god we have someone whos known really known android. you ARE THE BEST

    ReplyDelete
  13. Thank you so much for the your great effort, you saved a lot of time for me and you did a great work,Thanks :)

    ReplyDelete
  14. Version mejorada!!!

    Vector listTitle;
    Vector listPanel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.accordian);

    ViewGroup acordian = (ViewGroup) findViewById(R.id.acordian);
    listTitle = new Vector();
    listPanel = new Vector();

    ViewGroup tab;LinearLayout panel= null;TextView title = null ;
    int count = acordian.getChildCount();

    for (int i = 0; i < count; i++) {
    tab = (ViewGroup)acordian.getChildAt(i);

    if (tab instanceof LinearLayout)

    title = (TextView) tab.getChildAt(0);
    panel = (LinearLayout) tab.getChildAt(1);
    title.setOnClickListener(this);

    if(title!=null && panel!=null){
    listTitle.add(title);
    listPanel.add(panel);
    }
    }
    panel=listPanel.get(0);
    panel.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f,0.0f, 500, panel, true));

    }

    @Override
    public void onClick(View vt) {

    LinearLayout panel= null;TextView title = null;

    for (int i = 0; i < listTitle.size(); i++) {
    title=listTitle.get(i);
    panel=listPanel.get(i);

    if(title.getId()==vt.getId()){
    if (panel.getVisibility() != View.VISIBLE) {
    panel.startAnimation(new ScaleAnimToShow(1.0f, 1.0f, 1.0f,
    0.0f, 500, panel, true));
    }
    }else{
    panel.startAnimation(new ScaleAnimToHide(1.0f, 1.0f, 1.0f, 0.0f,
    500, panel, true));
    }
    }
    }

    ReplyDelete