차근차근/Android
[ study ] 다이어리 어플 만들어보기 2 - 부가설명3
안드로이드 listview checkbox
ListView와 CheckBox 연동
http://blog.daum.net/satomail/68
ListView에서 adapter로 custom view를 넣을 때 그 custom view 안의 checkbox와 ListView의 choice mode를 연동하는 방법.
먼저 리스트뷰에서는 다음처럼 설정한다.
- listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
그러면 이제 체크 상태를 ListView가 관리하게 된다. 그런데, custom view를 쓰면 ListView가 그 안에 있는 뷰 중 Checkable이 있으면 checked 상태를 바꾼다. 이 점을 이용하면 checkbox와 연동하게 할 수 있다.
getView에서는 checkbox를 다음과 같이 설정한다. 안드로이드의 ListView에서는 focusable 뷰가 포함되어 있으면 onItemClick을 비롯한 여러 가지 ListView의 기본 동작이 안 먹는다. 그래서 CheckBox에서 focusable을 빼는 것. 그러면 리스트의 항목을 클릭할 때 checkbox가 연동되게 된다. 그런데, 여기까지만 하면 checkbox를 직접 클릭할 때는 그 상태가 ListView와 연동되지 않으므로 checkbox의 clickable을 빼야 한다.
- CheckBox checkbox = (CheckBox) convertView.findViewById(R.id.Checkbox);
checkbox.setChecked(((ListView)parent).isItemChecked(position));
checkbox.setFocusable(false);
checkbox.setClickable(false);
리스트뷰 체크박스 연동
커스텀 리스트뷰에 체크박스를 넣어 뿌려주려고 아래와같이 설정 했는데요, 클릭시 정상적으로 클릭한 체크박스의 값이
private AdapterView.OnItemClickListener mItemClickListener = new AdapterView.OnItemClickListener() {
@Override
[안드로이드]리스트뷰 체크박스 연결
TestListActivity.java
1 2 3 4 5 | TestListAdapter mCustomAdapter; mCustomAdapter = new TestListAdapter(TestNews. this , lDataList, R.layout.test_datalist, from, to); mLv_TestList.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); mLv_TestList.setAdapter(mCustomAdapter); |
TestListAdapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | public class TestListAdapter extends SimpleAdapter { private List<Map<String, String>> mArrayList; private int mResource; // Layout ID private Context mContext; ViewHolder holder; ArrayList<Boolean> array; public TestListAdapter(Context context, List<Map<String, String>> lDataListFix, int resource, String[] from, int [] to) { super (context, lDataListFix, resource, from, to); mArrayList = lDataListFix; mResource = resource; mContext = context; } @Override public int getCount() { return mArrayList.size(); } @Override public Object getItem( int position) { return mArrayList.get(position).get( "msg" ) + "<" + mArrayList.get(position).get( "date" ) + ">" ; } @Override public long getItemId( int position) { return position; } @Override public View getView( final int position, View convertView, ViewGroup parent) { if (convertView == null ) { LayoutInflater vi = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = vi.inflate(mResource, null ); holder = new ViewHolder(); holder.msg = (TextView) convertView.findViewById(R.id.msg); holder.date = (TextView) convertView.findViewById(R.id.date); holder.cb_checkbox = (CheckBox) convertView.findViewById(R.id.cb_checkbox); convertView.setTag(holder); } else holder = (ViewHolder) convertView.getTag(); Map<String, String> iItem = mArrayList.get(position); holder.tv_Msg.setText(iItem.get( "msg" )); holder.tv_CreateDate.setText(iItem.get( "date" )); holder.cb_checkbox.setChecked( false ); holder.cb_checkbox.setChecked(((ListView)parent).isItemChecked(position)); return convertView; } } class ViewHolder { TextView msg; TextView date; CheckBox cb_checkbox; } |
여기까지 별반 다르지 않았다. 체크박스 까지는 무난하게 만들지만 리스트가 늘어나면 문제가 생겼다.
체크하지 않았는데도 체크가되거나 스크롤 내리면 체크한 리스트에서 체크가 해제되어 보여졌다.
실제로는 체크가되어있었다.
인터넷에 많이 찾아봤지만 명쾌한 답이잘 없다. 결국 해결한건
holder.cb_checkbox.setChecked(((ListView)parent).isItemChecked(position));
한줄이다.
TestListActivity.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | <? xml version = "1.0" encoding = "utf-8" ?> android:layout_width = "fill_parent" android:layout_height = "fill_parent" android:orientation = "horizontal" > < LinearLayout android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:layout_weight = "1" android:orientation = "vertical" > < TextView android:id = "@+id/msg" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginLeft = "10dip" android:layout_marginRight = "10dip" android:textColor = "@color/black" android:textSize = "17dp" android:textStyle = "bold" /> < TextView android:id = "@+id/date" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginLeft = "10dip" android:layout_marginRight = "10dip" android:textColor = "@color/text3" /> </ LinearLayout > < CheckBox android:id = "@+id/cb_checkbox" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_marginRight = "10dp" android:focusable = "false" android:clickable = "false" /> </ LinearLayout > |
Android Custom ListView with CheckBox
안드로이드용 앱을 개발하면서 많은 샘플을 찾아보고 그것을 적용하여 새로운 무엇인가를 만드는 과정중에 찾기가
너무나 힘든것이 하나 있는데, 그것은 CheckBox를 가지고 있는 ListView 를 구현하는 방법이다.
처음에는 별에별 편법을 다 동원해서 해보았는데 아무리 봐도 정석은 아닌듯....
그래서 초심으로 돌아간 마음으로 새롭게 접근해보았습니다.
일단은 Android Playing With ListView Choice Mode[출처] 안드로이드사이드 - http://www.androidside.com/B46/11861 로 부터의 샘플을 가지고 구현해보았습니다.
LVSample1 은 RadioButton 을 사용한 ArrayAdapter 로 구현한 ListView 샘플입니다.
LVSample2 는 CheckBox를 사용한 ArrayAdapter 로 구현한 ListView 샘플입니다.
마지막 LVSample3은 BaseAdapter 를 상속한 Custom Adapter와 Checkable Interface를 이용한 ListView 구현 샘플입니다.
마지막 LVSample3은 버튼 컨트롤을 사용한 ListView 데이터에 접근하여 조회/추가/삭제를 하는 기능을 보여줍니다.
간단한 LVSample3의 중요 소스는 다음과 같습니다.
* LVSample3.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | public class LVSample3 extends Activity implements OnClickListener { private Button btnSearch; private Button btnAdd; private Button btnDelete; private ListView lv; private List<lvsample3item> dataSources; private ListAdapter adapter; private DialogInterface.OnClickListener deleteYesListener; private DialogInterface.OnClickListener deleteNoListener; @Override public void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); this .setContentView(R.layout.main); // For Buttons btnSearch = (Button) findViewById(R.id.btnSearch); btnSearch.setOnClickListener( this ); btnAdd = (Button) findViewById(R.id.btnAdd); btnAdd.setOnClickListener( this ); btnDelete = (Button) findViewById(R.id.btnDelete); btnDelete.setOnClickListener( this ); // For Custom ListView dataSources = getDataSource(); adapter = new LVSample3Adapter(dataSources, this ); this .lv = (ListView) findViewById(R.id.listview); this .lv.setAdapter(adapter); this .lv.setItemsCanFocus( false ); this .lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // lv.setChoiceMode(ListView.CHOICE_MODE_SINGLE); this .lv.setOnItemClickListener( new OnItemClickListener() { @Override public void onItemClick(AdapterView<!--?--> adapt, View view, int position, long id) { // TODO Auto-generated method stub TextView tvTitle = (TextView) view.findViewById(R.id.title); TextView tvSummary = (TextView) view.findViewById(R.id.summary); String message = "Title: " + tvTitle.getText() + "\n" + "Summary:" + tvSummary.getText(); ShowMessageBox(LVSample3. this , message); } }); } @Override public void onClick(View view) { if (view.getId() == R.id.btnSearch) { // long[] checkedItems = this.lv.getCheckItemIds(); Long[] checkedItems = ((LVSample3Adapter) adapter).getCheckItemIds(); if (checkedItems == null || checkedItems.length == 0 ) { ShowMessageBox( this , "Selected Items is Nothing." ); return ; } String message = "" ; for ( int index = 0 ; index < checkedItems.length; index++) { long pos = checkedItems[index]; LVSample3Item item = dataSources.get(( int ) pos); message += String.format( "%d[%s, %s]\n" , pos, item.getTitle(), item.getSummary()); } ShowMessageBox( this , message); } else if (view.getId() == R.id.btnAdd) { addDataSource( this .dataSources); ((LVSample3Adapter) adapter).notifyDataSetChanged(); ShowMessageBox( this , String.format( "All items count is %d." , dataSources.size())); } else if (view.getId() == R.id.btnDelete) { // long[] checkedItems = this.lv.getCheckItemIds(); Long[] checkedItems = ((LVSample3Adapter) adapter).getCheckItemIds(); if (checkedItems == null || checkedItems.length == 0 ) { ShowMessageBox( this , "Selected items is Nothing." ); return ; } // Dialog Setting deleteYesListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // long[] checkedItems = lv.getCheckItemIds(); Long[] checkedItems = ((LVSample3Adapter) adapter).getCheckItemIds(); for ( int index = checkedItems.length - 1 ; index >= 0 ; index--) { long pos = checkedItems[index]; dataSources.remove(( int ) pos); } // lv.clearChoices(); ((LVSample3Adapter) adapter).clearChoices(); ((LVSample3Adapter) adapter).notifyDataSetChanged(); ShowMessageBox(LVSample3. this , String.format( "All items count is %d." , dataSources.size())); } }; deleteNoListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.cancel(); } }; ShowMessageYesNo( this , "Delete" , "Are you sure you want to delete selected item(s)?" , deleteYesListener, deleteNoListener); } } private static List<lvsample3item> getDataSource() { List<lvsample3item> lstItems = new ArrayList<lvsample3item>(); for ( int i = 0 ; i < 20 ; i++) { LVSample3Item item = new LVSample3Item( "Action" + i, "This is a test mssage." ); lstItems.add(item); } return lstItems; } private static List<lvsample3item> addDataSource(List<lvsample3item> dataSources) { LVSample3Item item = new LVSample3Item( "Added" + dataSources.size(), "This is a added mssage." ); dataSources.add(item); return dataSources; } private static void ShowMessageBox(Context context, String message) { Toast.makeText(context, message, Toast.LENGTH_LONG).show(); } public static void ShowMessageYesNo(Context context, String title, String message, DialogInterface.OnClickListener YesListener, DialogInterface.OnClickListener NoListener) { AlertDialog.Builder alterBuilder = new AlertDialog.Builder(context); alterBuilder.setMessage(message).setCancelable( false ).setPositiveButton (android.R.string.yes, YesListener) .setNegativeButton(android.R.string.no, NoListener); AlertDialog alert = alterBuilder.create(); // Title for AlertDialog alert.setTitle(title); // Icon for AlertDialog alert.setIcon(R.drawable.icon); alert.show(); } } </lvsample3item></lvsample3item></lvsample3item></lvsample3item></lvsample3item></lvsample3item> |
* LVSample3Adapter.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | public class LVSample3Adapter extends BaseAdapter { private List<lvsample3item> itemList; private Context context; public Hashtable<integer, view= "" > hashConvertView = new Hashtable<integer, view= "" >(); public LVSample3Adapter(List<lvsample3item> itemList, Context context) { this .itemList = itemList; this .context = context; } public int getCount() { return itemList.size(); } public LVSample3Item getItem( int position) { return itemList.get(position); } public long getItemId( int position) { return itemList.get(position).getId(); } public View getView( int position, View convertView, ViewGroup parent) { LVSample3Item item = itemList.get(position); ViewHolder holder; if (hashConvertView.containsKey(position) == false ) { convertView = (com.nalsil.ListViewSample.CheckableRelativeLayout) LayoutInflater.from(context).inflate( R.layout.my_list_item3, parent, false ); holder = new ViewHolder(); holder.tvTitle = (TextView) convertView.findViewById(R.id.title); holder.tvSummary = (TextView) convertView.findViewById(R.id.summary); holder.chk = (CheckBox) convertView.findViewById(R.id.check); holder.chk.setId(position); holder.chk.setOnClickListener(listener); convertView.setTag(holder); hashConvertView.put(position, convertView); } else { convertView = (View) hashConvertView.get(position); holder = (ViewHolder) convertView.getTag(); } holder.tvTitle.setText(item.getTitle()); holder.tvSummary.setText(item.getSummary()); return convertView; } private final OnClickListener listener = new OnClickListener() { public void onClick(View v) { if (v instanceof CheckBox) { CheckBox chk = (CheckBox) v; // switch (chk.getId()) { // } notifyDataSetChanged(); // notifyDataSetInvalidated(); } } }; public Long[] getCheckItemIds() { if (hashConvertView == null || hashConvertView.size() == 0 ) return null ; Integer key; View view; List< long > lstIds = new ArrayList< long >(); Enumeration<integer> e = hashConvertView.keys(); long index = 0 ; while (e.hasMoreElements()) { key = (Integer) e.nextElement(); view = (View) hashConvertView.get(key); ViewHolder holder = (ViewHolder) view.getTag(); if (holder.chk.isChecked()) { lstIds.add(index); } index++; } Long[] arrLong = (Long[]) lstIds.toArray( new Long[ 0 ]); return arrLong; } public void clearChoices() { if (hashConvertView == null || hashConvertView.size() == 0 ) return ; Integer key; View view; List< long > lstIds = new ArrayList< long >(); Enumeration<integer> e = hashConvertView.keys(); long index = 0 ; while (e.hasMoreElements()) { key = (Integer) e.nextElement(); view = (View) hashConvertView.get(key); ViewHolder holder = (ViewHolder) view.getTag(); if (holder.chk.isChecked()) { holder.chk.setChecked( false ); } index++; } } static class ViewHolder { CheckBox chk; TextView tvTitle; TextView tvSummary; } } </integer></ long ></ long ></integer></ long ></ long ></lvsample3item></integer,> </integer,></lvsample3item> |
* LVSample3Item.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public class LVSample3Item implements Serializable { private static final long serialVersionUID = -1586987653093943671L; private static long index = 0 ; private long Id = 0 ; private String title; private String summary; public LVSample3Item(String title, String summary) { Id = index++; this .title = title; this .summary = summary; } public long getId() { return Id; } public void setId( long Id) { this .Id = Id; } public String getTitle() { return title; } public void setTitle(String title) { this .title = title; } public String getSummary() { return summary; } public void setSummary(String summary) { this .summary = summary; } } |
* main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <!--?xml version="1.0" encoding="utf-8"?--> android:orientation = "vertical" android:layout_width = "fill_parent" android:layout_height = "fill_parent" > < linearlayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "wrap_content" > < button android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "Search" android:id = "@+id/btnSearch" android:layout_weight = "1" >></ button > < button android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "Add" android:id = "@+id/btnAdd" android:layout_weight = "1" >></ button > < button android:layout_width = "fill_parent" android:layout_height = "wrap_content" android:text = "Delete" android:id = "@+id/btnDelete" android:layout_weight = "1" >></ button > </ linearlayout > < listview android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/listview" ></ listview > </ linearlayout > |
* my_list_item3.xml
1 2 3 4 5 6 7 8 9 10 11 | <!--?xml version="1.0" encoding="utf-8"?--> < com.nalsil.listviewsample.checkablerelativelayout android:orientation = "horizontal" android:layout_width = "fill_parent" android:layout_height = "fill_parent" nalsil:checkable = "@+id/check" > < imageview android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/img" android:layout_alignparentleft = "true" android:layout_alignparenttop = "true" android:layout_alignparentbottom = "true" android:src = "@drawable/icon" android:focusable = "false" > < checkbox android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@id/check" android:layout_alignparenttop = "true" android:layout_alignparentbottom = "true" android:focusable = "false" android:layout_alignparentright = "true" android:layout_marginright = "5dip" android:clickable = "true" > < textview android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:id = "@+id/title" android:layout_alignparenttop = "true" android:focusable = "false" android:layout_torightof = "@id/img" > < textview android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:text = "This is my list item layout" android:id = "@+id/summary" android:layout_alignparentbottom = "true" android:focusable = "false" android:layout_torightof = "@id/img" android:layout_below = "@id/title" > </ textview ></ textview ></ checkbox ></ imageview ></ com.nalsil.listviewsample.checkablerelativelayout >
|
'차근차근 > Android' 카테고리의 다른 글
ListView 소스 (0) | 2015.05.06 |
---|---|
[ study ] 다이어리 어플 만들어보기 2 - 부가설명4 (0) | 2015.05.06 |
[ study ] 다이어리 어플 만들어보기 2 - 부가설명2 (0) | 2015.05.04 |
[ study ] 다이어리 어플 만들어보기 2 - 부가설명1 (0) | 2015.05.04 |
[ study ] 다이어리 어플 만들어보기 1 - 부가 설명 6 (0) | 2015.04.29 |
'차근차근/Android'의 다른글
- 현재글[ study ] 다이어리 어플 만들어보기 2 - 부가설명3