티스토리 뷰



안드로이드/Android ExpandableListView 만들기


안드로이드에서 ListView는 많이들 써보셨을 텐데요. ListView안에 또다른 List가 들어있는 ExpandableListView라는 녀석이 존재 합니다. 기본적으로 ListView를 상속받아 구현한 클래스기 때문에 ListView의 속성과 거의 유사하다고 생각 하시면 됩니다.


생각하실것은 기존의 ListView는 getView하나로 Row를 만들고 사용하였는데 ExpandableListView는 Group 과 Child 의 getView를 각각 구현해 준다는 점만 기억하시면 만드시는데는 큰 문제가 없을 거라고 생각합니다.


자 그럼, ExpandableListView 의 예제를 살펴 보겠습니다.


public class TestExpandableListViewActivity extends Activity {

	private ArrayList mGroupList = null;
	private ArrayList> mChildList = null;
	private ArrayList mChildListContent = null;

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

		setLayout();

		mGroupList = new ArrayList();
		mChildList = new ArrayList>();
		mChildListContent = new ArrayList();

		mGroupList.add("가위");
		mGroupList.add("바위");
		mGroupList.add("보");

		mChildListContent.add("1");
		mChildListContent.add("2");
		mChildListContent.add("3");

		mChildList.add(mChildListContent);
		mChildList.add(mChildListContent);
		mChildList.add(mChildListContent);

		mListView.setAdapter(new BaseExpandableAdapter(this, mGroupList, mChildList));
		
		// 그룹 클릭 했을 경우 이벤트
		mListView.setOnGroupClickListener(new OnGroupClickListener() {
			@Override
			public boolean onGroupClick(ExpandableListView parent, View v,
					int groupPosition, long id) {
				Toast.makeText(getApplicationContext(), "g click = " + groupPosition, 
						Toast.LENGTH_SHORT).show();
				return false;
			}
		});
		
		// 차일드 클릭 했을 경우 이벤트
		mListView.setOnChildClickListener(new OnChildClickListener() {
			@Override
			public boolean onChildClick(ExpandableListView parent, View v,
					int groupPosition, int childPosition, long id) {
				Toast.makeText(getApplicationContext(), "c click = " + childPosition, 
						Toast.LENGTH_SHORT).show();
				return false;
			}
		});
		
		// 그룹이 닫힐 경우 이벤트
		mListView.setOnGroupCollapseListener(new OnGroupCollapseListener() {
			@Override
			public void onGroupCollapse(int groupPosition) {
				Toast.makeText(getApplicationContext(), "g Collapse = " + groupPosition, 
						Toast.LENGTH_SHORT).show();
			}
		});
		
		// 그룹이 열릴 경우 이벤트
		mListView.setOnGroupExpandListener(new OnGroupExpandListener() {
			@Override
			public void onGroupExpand(int groupPosition) {
				Toast.makeText(getApplicationContext(), "g Expand = " + groupPosition, 
						Toast.LENGTH_SHORT).show();
			}
		});
	}

	/*
	 * Layout
	 */
	private ExpandableListView mListView;

	private void setLayout(){
		mListView = (ExpandableListView) findViewById(R.id.elv_list);
	}
}
//


자 그럼 Adapter 코드도 한번 살펴 보겠습니다.
public class BaseExpandableAdapter extends BaseExpandableListAdapter{
	
	private ArrayList groupList = null;
	private ArrayList> childList = null;
	private LayoutInflater inflater = null;
	private ViewHolder viewHolder = null;
	
	public BaseExpandableAdapter(Context c, ArrayList groupList, 
			ArrayList> childList){
		super();
		this.inflater = LayoutInflater.from(c);
		this.groupList = groupList;
		this.childList = childList;
	}
	
	// 그룹 포지션을 반환한다.
	@Override
	public String getGroup(int groupPosition) {
		return groupList.get(groupPosition);
	}

	// 그룹 사이즈를 반환한다.
	@Override
	public int getGroupCount() {
		return groupList.size();
	}

	// 그룹 ID를 반환한다.
	@Override
	public long getGroupId(int groupPosition) {
		return groupPosition;
	}

	// 그룹뷰 각각의 ROW 
	@Override
	public View getGroupView(int groupPosition, boolean isExpanded,
			View convertView, ViewGroup parent) {
		
		View v = convertView;
		
		if(v == null){
			viewHolder = new ViewHolder();
			v = inflater.inflate(R.layout.list_row, parent, false);
			viewHolder.tv_groupName = (TextView) v.findViewById(R.id.tv_group);
			viewHolder.iv_image = (ImageView) v.findViewById(R.id.iv_image);
			v.setTag(viewHolder);
		}else{
			viewHolder = (ViewHolder)v.getTag();
		}
		
		// 그룹을 펼칠때와 닫을때 아이콘을 변경해 준다.
		if(isExpanded){
			viewHolder.iv_image.setBackgroundColor(Color.GREEN);
		}else{
			viewHolder.iv_image.setBackgroundColor(Color.WHITE);
		}
		
		viewHolder.tv_groupName.setText(getGroup(groupPosition));
		
		return v;
	}
	
	// 차일드뷰를 반환한다.
	@Override
	public String getChild(int groupPosition, int childPosition) {
		return childList.get(groupPosition).get(childPosition);
	}
	
	// 차일드뷰 사이즈를 반환한다.
	@Override
	public int getChildrenCount(int groupPosition) {
		return childList.get(groupPosition).size();
	}

	// 차일드뷰 ID를 반환한다.
	@Override
	public long getChildId(int groupPosition, int childPosition) {
		return childPosition;
	}

	// 차일드뷰 각각의 ROW
	@Override
	public View getChildView(int groupPosition, int childPosition,
			boolean isLastChild, View convertView, ViewGroup parent) {
		
		View v = convertView;
		
		if(v == null){
			viewHolder = new ViewHolder();
			v = inflater.inflate(R.layout.list_row, null);
			viewHolder.tv_childName = (TextView) v.findViewById(R.id.tv_child);
			v.setTag(viewHolder);
		}else{
			viewHolder = (ViewHolder)v.getTag();
		}
		
		viewHolder.tv_childName.setText(getChild(groupPosition, childPosition));
		
		return v;
	}

	@Override
	public boolean hasStableIds() {	return true; }

	@Override
	public boolean isChildSelectable(int groupPosition, int childPosition) { return true; }
	
	class ViewHolder {
		public ImageView iv_image;
		public TextView tv_groupName;
		public TextView tv_childName;
	}

}
//



그리고 XML의 ExpandableListView 속성에 android:groupIndicator="@null" 속성을 추가해 줘서 기본적으로 안드로이드가 제공하는 그룹뷰 아이콘을 제거해 줍니다.

<ExpandableListView  
      android:id="@+id/elv_list"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:groupIndicator="@null"
      />


자 그럼 이렇게 해서 ExpandableListView 에 대해서 알아봤는데요. 디테일한 설명이 없더라도 중간중간에 주석을 달아서 보시는데 크게 어려움은 없을 거라고 생각 합니다. 그럼 ExpandableListView 를 활용한 멋진 UI 작업을 하시길 바라겠습니다.


Android Developers 사이트를 이용하시면 ExpandableListView 에 대한 속성 및 이벤트들을 더 많이 활용할 수 있습니다.


참고 : http://developer.android.com/reference/android/widget/ExpandableListView.html



파일첨부 : 


TestExpandableListView.zip



스크린샷 :










댓글
  • 프로필사진 지나가던행인 좋은정보 감사합니다^^ 2012.08.06 17:26
  • 프로필사진 아라비안왕자 네^^ 좋은정보라니 다행이네요 ㅎ^^ 2012.08.06 22:02 신고
  • 프로필사진 전국제패 많은 도움이 되었습니다. 감사합니다~~ 2012.08.23 15:28
  • 프로필사진 아라비안왕자 네 꼭 전국제패 하시길 바랍니다^^ 2012.08.23 18:33 신고
  • 프로필사진 굿 깜끔하게 정리 잘 해두셨네요.. 감사히 잘 보고 갑니다. ~ 2012.09.04 11:00
  • 프로필사진 아라비안왕자 네 감사합니다.^^ 2012.09.07 14:13 신고
  • 프로필사진 자올 y.j.m 친절한 샘플 감사합니다.
    혹시 자식 리스트도 터치한 색깔을 동적으로 바꾸게 할순 없는지요.?

    부모쪽 터치하는하는 알고리즘을 분석해서 했는데 잘 안되네요..ㅠㅠ
    2012.09.08 22:11
  • 프로필사진 아라비안왕자 예제코드의 mListView.setOnChildClickListener 이부분을 보시기 바랍니다. 자식을 클릭 했을 경우 이벤트를 처리 할 수 있는 부분입니다.^^

    그리고 URL 주소창에
    http://arabiannight.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9CAndroid-AdapterViewonItemClickListener-%EC%82%AC%EC%9A%A9%EB%B2%95

    이 포스팅 내용을 참조 하시면 원하는 결과를 얻으 실 수 있을것 같네요^^
    2012.09.10 23:51 신고
  • 프로필사진 좋은 정보 감사합니다~!!

    혹시 자식선택시 다른 액티비티로 전환되게 할 수도 있나요 ㅠ.ㅠ?
    2012.09.28 18:37
  • 프로필사진 아라비안왕자 setOnChildClickListener 를 참고하시기 바랍니다.^^ 2012.10.13 10:12 신고
  • 프로필사진 질문있습니다 확장리스트뷰는 부모 그룹 클릭하면 자식 그룹이 나오는데
    이때 자식 그룹을 클릭할경우에도 그아래 또 자식 그룹을 둘수도 있나요??
    2013.01.02 10:44
  • 프로필사진 아라비안왕자 음.. 저도 그렇게 개발 할려고 했는데 Listview를 2개 쓰거나, Scrollview와 Listview를 동시에 쓰면 안드로이드 자체 스크롤 버그가 생겼던 걸로 기억하네요^^ 꼭 쓸일이 있다면

    레이아웃으로 1depth
    expandable 부모 2depth
    expandable 자식 3depth

    로 해보시길 바랄게요^^
    2013.01.07 18:15 신고
  • 프로필사진 질문이 하나 있습니다. 첨부한 코드를 이용하여 조금 수정을 하면서 익히고 있습니다.

    그런데, List 배경 화면을 회색으로 지정되어진 상태에서, List의 내용이 한 Page가 넘어가거나, Drag를 하다보면, 배경 화면이 검은색으로 바뀌는 경우가 있습니다.
    이런 경우는 어떻게 해결 해야 하나요? ㅠㅠ
    2013.01.29 01:50
  • 프로필사진 아라비안왕자 List 배경 회색으로 지정한 부분이랑 apdater의 getview 부분 코드좀 보여 주시겠어요? 2013.01.31 03:39 신고
  • 프로필사진 질문이요!! 안녕하세요 안드로이드 공부하고있는 학생입니다!
    소스내용을 수정하면서 공부하고있는데요! 질문이있어서 댓글남깁니다!

    제가 자식 뷰를 커스터마이징해서 텍스트와 함께 체크박스를 넣었는데요

    부모 클래스를 선택할 때마다 체크박스에 체크된 부분이 변경이 되더라고요..

    예를 들어 부모 리스트가 3개 있고 각각의 부모리스트에 자식리스트를 3개씩 넣었을 때

    첫째 부모리스트의 첫째 자식리스트의 체크박스를 선택하면

    두번째 부모리스트를 펼쳤을 때 첫째 자식리스트의 선택된 체크박스가 사라지고 셋째 자식리스트의 체크박스가 선택되더라고요

    다른 부모리스트를 펼치면 또 체크된 체크박스가 바뀌고요 체크박스가 바뀌지 않게 해결할 수 있는 부분이 있을까요? ㅠㅠ
    2013.02.08 13:48
  • 프로필사진 질문이요!! 아니면.. 자식 리스트의 xml을 다르게 각각 연결시키는 방법이 있나요? 2013.02.12 11:36
  • 프로필사진 아라비안왕자 2개나 질문해주셨는데 답변이 늦어서 죄송합니다. 너무 바뻐서 지금도 짬네서 답변해 드리는 거라 디테일하게 못들어가는 점 죄송합니다.

    일단 첫번째 질문은 position이 꼬이는 것 같은데. getTag, setTag를 이용해서 코딩해 보세요. 자세한 예제는

    http://arabiannight.tistory.com/entry/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9CAndroid-ArrayAdapter-BaseAdapter%EB%A5%BC-%EC%82%AC%EC%9A%A9%ED%95%9C-ListView-%EA%B5%AC%ED%98%84

    요기 참고해 보시구요.

    두번재 질문은 여기에서 xml을 infalte 해주는 부분을 잘 활용 하셔서 차일드마다 다른 xml을 설정해 주시면 됩니다.

    // 차일드뷰 각각의 ROW
    @Override
    public View getChildView(int groupPosition, int childPosition,
    boolean isLastChild, View convertView, ViewGroup parent) {

    View v = convertView;

    if(v == null){
    viewHolder = new ViewHolder();
    v = inflater.inflate(R.layout.list_row, null);
    viewHolder.tv_childName = (TextView) v.findViewById(R.id.tv_child);
    v.setTag(viewHolder);
    }else{
    viewHolder = (ViewHolder)v.getTag();
    }

    viewHolder.tv_childName.setText(getChild(groupPosition, childPosition));

    return v;
    }
    2013.02.13 02:24 신고
  • 프로필사진 안드로이드어렵다 안녕하세요,
    예제를 잘 보고 따라했습니다.ㅠㅠ
    다름이 아니라 각 그룹마다 차일드의 내용이 다 똑같은데
    다르게하려면 어떻게 해야하나요?ㅠㅠ 알려주시면 감사하겠씁니당!!!
    2013.02.18 12:07
  • 프로필사진 해림빌라 mChildList.add(mChildListContent);
    mChildList.add(mChildListContent);
    mChildList.add(mChildListContent);

    이부분에 전달하는 ArrayList가 같아서그래요

    다른 ArrayList를 전달하면됨니다
    2013.02.19 16:56
  • 프로필사진 아라비안왕자 네^^ 해림빌라님 말씀처럼 mChildListContent 별로 차일드 뷰에 설정할 내용을 다르게 만들어 주시면 됩니다.^^ 2013.02.20 03:03 신고
  • 프로필사진 안녕하세요 혹시 부모 리스트를 펼쳤을 때 하나만 펼쳐지고 다른것을 펼쳤을때 자동으로 펼쳤던 것이 접히게 할 수는 있나요?
    어느 쪽을 수정해야하는지 궁금합니다 !!
    2013.02.24 15:47
  • 프로필사진 아라비안왕자 이렇게 사용해 보세요^^

    mListView.setOnGroupExpandListener(new OnGroupExpandListener() {

    @Override
    public void onGroupExpand(int groupPosition) {
    int groupCount = mBaseExpandableAdapter.getGroupCount();

    for (int i = 0; i < groupCount; i++) {
    if (!(i == groupPosition))
    mListView.collapseGroup(i);
    }
    }
    });
    2013.02.25 03:08 신고
  • 프로필사진 아라비안왕자 어댑터를 변수로 하나 빼시구요.

    ExpandListener를 다신다음에 아래와 같이 추가해 주시면 됩니다.^^
    2013.02.25 03:08 신고
  • 프로필사진 아라비안왕자 그래도 안되시면

    http://arabiannight.tistory.com/entry/360

    참고해 보시기 바랍니다.^^
    2013.02.25 03:44 신고
  • 프로필사진 안녕하세요 감사합니다 ^^ 2013.02.25 11:26
  • 프로필사진 아주 아주좋네요 자주 들려서 많이배울께요~^^감사합니다 2013.04.05 16:53
  • 프로필사진 아라비안왕자 감사합니다.^^ 2013.04.08 23:07 신고
  • 프로필사진 무대포개발자 좋은 자료입니다! 퍼갈게요 2013.04.09 20:18 신고
  • 프로필사진 촤일드 자식리스트를 일반리스트뷰와 디비연동처럼 디비의 내용을 불러올 수 있게 구현하려면 어떻게해야 하나요? ㅠㅠㅠ 2013.05.06 18:50
  • 프로필사진 블랙로즈 자식 리스트를 선택시 따로따로 이벤트를 만들고 싶은데

    (예를 들어 1번을 클릭하면 웹창이 켜지고 2번을 클릭하면 지도가 쳐지는 ....)

    어떻게 하면 되나요??
    2013.05.23 20:48
  • 프로필사진 findanswer 덕분에 app 제작 공부하고 있습니다. 제 블로그에 참고자료로 트랙백하려고 하는데 괜찮을까요? 2015.12.02 21:09 신고
댓글쓰기 폼