상세 컨텐츠

본문 제목

안드로이드 ListView CHOICE_MODE_MULTIPLE 멀티초이스(리스트뷰 체크박스 같이 연동) 관련

스마트기기개발관련/안드로이드 개발

by AlrepondTech 2011. 6. 17. 10:39

본문

반응형
728x170



출처: http://huewu.blog.me/110088703990

Android Playing With ListView Choice Mode
샘플 코드:

 안 드로이드 GUI 개발에 있어서 가장 요긴하게 사용되는 View 중에 하나는 역시 ListView 입니다. 사용하기 간결하면서도 다양한 기능을 지원하고, 개발자 입맛대로 유연하게 변경해서 사용할 수 있는데다가, 성능도 훌륭합니다. 안드로이드 SDK 가 처음 공개되었을 때, 비록 아이폰에는 미치지 못했지만, 기존 스마트폰 플랫폼인 심비안이나 윈도우즈 모바일 계열과 비교하면 압도적으로 부드러운 스크롤 성능을 보여주었던 안드로이드의 기본 ListView 에 감탄한 기억이 있습니다. 

<ListView 는 세 가지 CHOICE_MODE 를 제공해 줍니다>

 안 드로이드 ListView 는 여러가지 다양한 기능들을 제공해주는데, 사용하는 방법이 아리송 한 것들이 있습니다. 그 중에 대표적인 하나가 바로 아이템을 선택과 관련된, 'CHOICE_MODE' 기능이 아닐까 생각합니다. (혹시 다른분들은 다 알고 계신데, 저만 모르고 있었는지도;;;) 예제로 볼 때는 잘 작동하는거 같은데 실재로 써먹으려고 하니 왠지 이상하다고 할까요? 얼마전에 관련된 기능을 활용할 필요가 있어서 이리 저리 궁리한 끝에 몇 가지 알게된 사실이 있어서 공유해 봅니다. 

 우선 SDK 문서를 살펴보겠습니다. 

public void setChoiceMode (int choiceMode)

Since: API Level 1

Defines the choice behavior for the List. By default, Lists do not have any choice behavior (CHOICE_MODE_NONE). By setting the choiceMode toCHOICE_MODE_SINGLE, the List allows up to one item to be in a chosen state. By setting the choiceMode to CHOICE_MODE_MULTIPLE, the list allows any number of items to be chosen.


 훌 륭합니다. ListView 에서 아이템을 선택하는 행동에 관해 설정할 수 있도록 해준다고 되어 있네요. 기본적으로 CHOICE_MODE_NONE 으로 아무런 행동도 수행되지 않지만, 사용자가 적절한 플래그 값을 설정하게 되면, 하나의 아이템만 선택 가능하도록 하거나, 하나 이상의 아이템을 선택 할 수 있도록 ListView 를 작성할 수 있다고 합니다. SDK 에 포함된 Sample 중에도 관련된 내용이 있습니다. 다음과 같이 간편하게 사용 할 수 있다고 되어 있네요. 

/**
 * This example shows how to use choice mode on a list. This list is 
 * in CHOICE_MODE_SINGLE mode, which means the items behave like
 * checkboxes.
 */

public class List10 extends ListActivity {
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setListAdapter(new ArrayAdapter<String>(this,
                android.R.layout.simple_list_item_single_choice, GENRES));
        
        final ListView listView = getListView();

        listView.setItemsCanFocus(false);
        listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    }


    private static final String[] GENRES = new String[] {
        "Action", "Adventure", "Animation", "Children", "Comedy", "Documentary", "Drama",
        "Foreign", "History", "Independent", "Romance", "Sci-Fi", "Television", "Thriller"
    };
}

 음... 잘 작동합니다. 그런데 혹시 위 예제에서 처럼 안드로이드에서 기본적으로 제공해주는 'simple_list_item_single_choice' 대신, 나름의 Layout 을 이용해서 'CHOICE_MODE' 를 적용해본 적이 있으신가요? 얼마전에 제가 한번 해보니, 전혀 작동하지 않더군요. 뭐가 문제인걸까요? 'CHOICE_MODE' 는 그저 정해진 Layout 을 활용하는 경우에만 사용가능한 잉여인걸까요? (저는 사실 그런가 보다하고 생각 했습니다...)


<simple_list_item_single_choice Layout 을 사용한 ListView>

 결론 부터 이야기하자면... SDK 에는 잘 나와있지 않은 한 가지 비밀(?)이 있습니다. 바로 'CHOICE_MODE' 기능이 정상적으로 수행되기 위해서는, ListView 의 개별 아이템을 구성하는 View 가 Checkable 인터페이스를 구현하고 있어야 된다는 점입니다. ListView 내부에서 'CHOICE_MODE' 기능을 구현할 때, 해당 인터페이스에서 제공하는 메서드들을 활용하고 있기 때문입니다. 참고로 안드로이드 예제에서 사용하고 있는 'simple_list_item_single_choice' Layout 은 하나의 'CheckedTextView' 로 구성된 Layout 인데, CheckedTextView 클래스는 Checkable 인터페이스를 구현하고 있습니다. 

 따라서, 나름의 View Layout 을 이용하면서도, 'CHOICE_MODE' 를 활용하기 위해서는 해당 View 의 Layout 이 Checkable 인터페이스를 구현하고 있어야 하는 셈입니다. 어떻게 하면 좋을까요?  저 는 단순하게 해결했습니다. 제가 만든 View Layout 에 사용된 ViewGroup (이 경우 RelativieLayout ) 을 상속받아 Checkabe 인터페이스를 구현한 새로운 ViewGroup 을 하나 만들어 사용하였습니다.

public class CheckableRelativeLayout extends RelativeLayout implements Checkable{
	
	final String NS = "http://schemas.android.com/apk/res/com.huewu.example.checkable";
	final String ATTR = "checkable";

	int checkableId;
	Checkable checkable;

	public CheckableRelativeLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
		checkableId = attrs.getAttributeResourceValue(NS, ATTR, 0);
	}
	
	@Override
	public boolean isChecked() {
		checkable = (Checkable) findViewById(checkableId);
		if(checkable == null)
			return false;
		return checkable.isChecked();
	}

	@Override
	public void setChecked(boolean checked) {
		checkable = (Checkable) findViewById(checkableId);
		if(checkable == null)
			return;
		checkable.setChecked(checked);
	}

	@Override
	public void toggle() {
		checkable = (Checkable) findViewById(checkableId);
		if(checkable == null)
			return;
		checkable.toggle();
	}
}//end of class

 코 드는 간단합니다. RelativeLayout 을 통채로 상속 받은 후, Checkable 인터페이스의 대상으로 사용될 자식 View 의 ID 값을 'checkable' 이라는 Attribute 로 넘겨 받을 수 있게 구현하였습니다. 이 후 Checkable 인터페이스의 구현은 자식 View 의 Checkable 인터페이스를 곧 바로 호출 하도록 연결해 주었습니다. 새롭게 만든 CheckableRelativeLayout 을 이용해서 아래와 같이 하나의 ImageView 와 두 개의 TextView 그리고 Checkable 인터페이스를 갖는 RadioButton 으로 구성된 나름의 Layout 을 만들어 보았습니다.
 
<?xml version="1.0" encoding="utf-8"?>
<com.huewu.example.checkable.CheckableRelativeLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	xmlns:huewu="http://schemas.android.com/apk/res/com.huewu.example.checkable"
	android:orientation="horizontal" android:layout_width="fill_parent"
	android:layout_height="fill_parent" huewu:checkable="@+id/radio">
	
	<ImageView android:id="@+id/img" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_alignParentLeft="true"
		android:layout_alignParentTop="true" android:layout_alignParentBottom="true"
		android:src="@drawable/icon" />
		
	<RadioButton android:id="@id/radio" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_alignParentRight="true"
		android:layout_alignParentTop="true" android:layout_alignParentBottom="true"
		android:layout_marginRight="5dip" android:focusable="false" android:clickable="false"/>
		
	<TextView android:id="@android:id/text1" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_toRightOf="@id/img"
		android:layout_alignParentTop="true" />
		
	<TextView android:id="@android:id/text2" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:layout_toRightOf="@id/img"
		android:layout_below="@android:id/text1"
		android:layout_alignParentBottom="true" android:text="This is my list item layout" />
</com.huewu.example.checkable.CheckableRelativeLayout>
 Layout 을 살펴보시면, 'checkable' 속성 값으로 RadioButton 을 가리키고 있는 것을 확인 하실 수 있습니다. RadioButton 이 부모 Layout 보다 먼저 터치 이벤트를 처리하지 못하도록, 'focusable' 속성 값을 'false' 로 둔 부분도 확인하시면 좋습니다. 휴.. 작업 완료입니다. 새로운 Layout 이용하해서 ListView 를 구성하면 아래와 같은 결과를 얻을 수 있습니다. 


<my_list_item Layout 을 사용한 ListView. CHOICE_MODE 가 정상적으로 작동중>

 Checkable 인터페이스를 갖는 ViewGroup 을 만들어 사용하는 것이 약간 귀찮아 보일 수도 있지만, 그래도 시간을 투자할 만한 가치가 있습니다. 별다른 추가 코드 없이도 ListView 에서 설정한 'CHOICE_MODE' 에 따라 정확하게 작동하며,선택된 아이템 목록을 가져오기 위해서 ListView 에서 제공해는 getCheckedItemIds() 류의 API 도 정상적으로 활용하실 수 있습니다. (구글에서 짠 소스니 믿을만 하고 말이지요.) 

 안 드로이드에서 제공해주는 SDK 는 분명히 기능이 강력하지만, 다큐먼트만 보고 API 를 사용하려고 할 때는 가끔씩 고개를 갸웃거리게 됩니다. 그런 경우, 프레임워크 소스를 직접 살펴보면 이외의 해결책하는 경우가 있더군요. 앞으로도 간단하더라도 나름의 팁을 알게되면 종종 포스트 해보려고 하니 기대해 주세요.

PS. 이번 포스트에서 사용된 전체 프로젝트 코드는 코드 구글 사이트에 업로드 해 두었습니다.

//////////////////////////////////////////////////////////////////////////////////////////////


//출처 : http://cafe.naver.com/androidheaven.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=153&

CheckBox 가 들어간 ListView를 만들고 싶다면

먼저, 추가 할 구문이 있습니다.

listview. setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

CheckBox를 ListView에 넣게 되면 CheckBoxr가 focus 를 가져가기 때문에

아마 listview를 클릭해도 이벤트가 안 먹힐 겁니다.

그렇기 때문에, CheckBox 가 선언된 custom Adapter 안의 getView()메소드 안에서,

CheckBox checkbox = convertView.findViewId(R.id.cb);

checkbox.setFocusable(false);

위와 같이 선언 해주서 focus를 뺏기지 않습니다.

그런다음,

checkbox.setClickable(false)

checkbox.setChecked(((ListView)parent).isItemChecked(position));

위와 같이 선언합니다.

왜냐하면 리스트뷰를 클릭했을 때 CheckBox가 반응 하게 하기 위함이죠. 
반응형
그리드형


관련글 더보기

댓글 영역