본문 바로가기

Android/View

[Android] CheckBox(체크박스)가 들어있는 리스트에서 체크박스 에러!!스

 체크박스를 포함한 리스트를 작성하였을 때(CheckedTextView가 아닌...) 체크된 아이템의 위치가 이상하게 꼬이는 현상이 발생하곤한다. 예를들어 나는 분명히 제일 위의 1번만 선택했을 뿐인데 아래로 내려가보면 8번... 16번... 이런식으로 체크되지 않았던 영역까지 체크가 되어 있는것을 볼 수 있다. 이는 리스트에서 view를 재사용 하기때문에 발생하는 문자라고 하는데... 자세히는 모르겠다.. 일단 문제가 있으니 해결해보자..

 먼저 현상을 재현해 볼 간단한 어플리케이션을 만들어본다. 필자는 CheckBoxErrorTest라는 프로젝트를 생성하여 checkbox.test라는 패키지를 만들고 Activity는 역시 CheckBoxErrorTest라고 만들었다.

 우선 소스부터...

CheckBoxErrorTest.java

 
import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;

public class CheckBoxErrorTest extends ListActivity {
	
	private String[] testStringArray;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		testStringArray = new String[100];
		for (int i = 0; i < testStringArray.length; i++) {
			testStringArray[i] = "test No. = " + i;
		}
		
		TestAdapter ta = new TestAdapter(this, 0, testStringArray);
		setListAdapter(ta);
	}

	private class TestAdapter extends ArrayAdapter {

		public TestAdapter(Context context, int textViewResourceId,
				String[] items) {
			super(context, textViewResourceId, items);
			listItem = new ArrayList();
		}
		
		public int getCount() {
			return testStringArray.length;
		}
		
		public View getView(int position, View convertView, ViewGroup parent) {
			View v = convertView;

			if (v == null) {
				LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				v = vi.inflate(R.layout.listitemlayout, null);
			}
			
			String str = testStringArray[position];
			
			if (str != null) {
				TextView text = (TextView) v.findViewById(R.id.text);
				
				if (text != null) {
					text.setText(str);
				}
			}
			
			return v;
		}
	}
}


main.xml

 


	



listitemlayout.xml

// 
 


	
	



어플이 완성되었다면 실행을 시켜보자.. 우선 아무것도 체크되지않은 체크박스가 포함된 리스트가 보여질것이다. 제대로 실행이 된다면 현상 재현을 위해 가장위의 체크박스에 체크표시를 해본다.


흠... 잘 되는군.. 여기까지는 아무런 문제가 없어보인다.. 하지만!!! 밑으로 스크롤을 해보자..


엥!! 8번에는 체크한일이 없는데??? 근데 왜??? 계속 밑으로 내려보자... 어느정도 주기로 체크박스가 체크가 되어있는것이 보일것이다.. 바로 이것이 문제다. view를 재사용 하면서 체크가 되어있는 view까지 재사용이 되므로 재사용 주기마다 체크표시가 나타나는 것이다. 그렇다면 다시 가장 위로 올라가보자...


황당황당... 내가 체크했던 0번은 체크가 해제되어있고 5번이 체크되어있다... 이 역시 재사용으로 인한 버그... 그렇다면 이제 고쳐보도록 하자..

 
package checkbox.test;

import java.util.ArrayList;

import android.app.ListActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.CompoundButton.OnCheckedChangeListener;

public class CheckBoxErrorTest extends ListActivity {
	
	private String[] testStringArray;
	
	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		testStringArray = new String[100];
		for (int i = 0; i < testStringArray.length; i++) {
			testStringArray[i] = "test No. = " + i;
		}
		
		TestAdapter ta = new TestAdapter(this, 0, testStringArray);
		setListAdapter(ta);
	}

	private class TestAdapter extends ArrayAdapter {
		
		// 체크된 아이템들을 저장할 List
		private ArrayList listItem;
		
		public TestAdapter(Context context, int textViewResourceId,
				String[] items) {
			super(context, textViewResourceId, items);
			listItem = new ArrayList();
		}
		
		public int getCount() {
			return testStringArray.length;
		}
		
		public View getView(int position, View convertView, ViewGroup parent) {
			// 리스너에서 사용할 포지션 변수.
			final int checkBoxPosition = position;
			View v = convertView;

			if (v == null) {
				LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
				v = vi.inflate(R.layout.listitemlayout, null);
			}
			
			String str = testStringArray[position];
			
			if (str != null) {
				TextView text = (TextView) v.findViewById(R.id.text);
				//체크박스를 얻어온다.
				CheckBox cb = (CheckBox) v.findViewById(R.id.check);
				
				if (text != null) {
					text.setText(str);
				}
				// 체크박스가 null이 아니면...
				if (cb != null) {
					// 체크박스의 상태 변화를 체크한다.
					cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
						public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
							// 체크를 할 때
							if (isChecked) {
								for (int i = 0; i < listItem.size(); i++) {
									if (listItem.get(i) == checkBoxPosition) {
										return;
									}
								}
								listItem.add(checkBoxPosition);
							// 체크가 해제될 때
							} else {
								for (int i =0; i < listItem.size(); i++) {
									if (listItem.get(i) == checkBoxPosition) {
										listItem.remove(i);
										break;
									}
								}
							}
						}
					});
					// 체크된 아이템인지 판단할 boolean변수
					boolean isChecked = false;
					for (int i = 0; i < listItem.size(); i++) {
						// 만약 체크되었던 아이템이라면
						if (listItem.get(i) == checkBoxPosition) {
							// 체크를 한다.
							cb.setChecked(true);
							isChecked = true;
							break;
						}
					}
					// 아니라면 체크 안함!
					if (!isChecked) {
						cb.setChecked(false);
					}
				}
			}
			
			return v;
		}
	}
}


자!! 멋지게 수정되었다!! 참... 항상 생각지도 못한곳에서 문제가 발생한다... 그래도 조금씩 실력이 늘어가는것을 느낀다!! 뿌듯...