2012-10-25

【Android】CheckedTextView - 用於 ListView 的 CheckBox

當想要在 ListView 呈現具 CheckBox 的選項列表時,卻得不到預期的效果 -- 在 打勾方框 點選 和 在列表空白處點選所引發的是不同的 event,而點在 CheckBox 上的,好像也無法得知是在 ListView 內的哪個 item......踹了好久,才發現有 CheckedTextView 這個元件。

先建立主 Activitylayout(檔名: activity_main.xml):
  1. <LinearLayout
  2. xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:orientation="vertical" >
  7.  
  8. <ListView
  9. android:id="@+id/listView1"
  10. android:layout_width="match_parent"
  11. android:layout_height="match_parent"
  12. android:layout_weight="1" >
  13. </ListView>
  14.  
  15. </LinearLayout>


然後建立 ListViewitemlayout(檔名: list_item.xml):
  1. <?xml version="1.0" encoding="utf-8"?>
  2.  
  3. <LinearLayout
  4. xmlns:android="http://schemas.android.com/apk/res/android"
  5. android:layout_width="match_parent"
  6. android:layout_height="wrap_content"
  7. android:orientation="horizontal" >
  8.  
  9. <CheckedTextView
  10. android:id="@+id/check1"
  11. android:layout_width="match_parent"
  12. android:layout_height="wrap_content"
  13. android:gravity="center_vertical"
  14. android:checkMark="?android:attr/listChoiceIndicatorMultiple"
  15. android:checked="true"
  16. android:text="CheckedTextView"
  17. android:textAppearance="?android:attr/textAppearanceMedium" />
  18. </LinearLayout>

上面那個 checkMark,會讓打勾方框在最右邊,如下圖:

如果要讓打勾方框在文字左邊,則將
android:checkMark="?android:attr/listChoiceIndicatorMultiple" 改成:
android:drawableLeft="?android:attr/listChoiceIndicatorMultiple"
android:drawableRight="?android:attr/listChoiceIndicatorMultiple" 效果則等同 android:checkMark="?android:attr/listChoiceIndicatorMultiple"
另外還有 drawableTopdrawableBottom 可指定



建立主程式(檔名: MainActivity.java) :
  1. package com.example.chktextv;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. import android.os.Bundle;
  7. import android.app.Activity;
  8. import android.util.Log;
  9. import android.view.View;
  10. import android.widget.AdapterView;
  11. import android.widget.CheckedTextView;
  12. import android.widget.ListView;
  13. import android.widget.AdapterView.OnItemClickListener;
  14. import android.widget.Toast;
  15.  
  16. public class MainActivity extends Activity
  17. {
  18. private static final String TAG = "MainActivity";
  19. List<String> list;
  20. ListView listview;
  21. List<Boolean> listShow; // 這個用來記錄哪幾個 item 是被打勾的
  22. @Override
  23. public void onCreate(Bundle savedInstanceState)
  24. {
  25. super.onCreate(savedInstanceState);
  26. setContentView(R.layout.activity_main);
  27. listview = (ListView) findViewById(R.id.ListView1);
  28. listview.setOnItemClickListener(new OnItemClickListener()
  29. {
  30. public void onItemClick(AdapterView<?> parent, View v, int position, long id)
  31. {
  32. CheckedTextView chkItem = (CheckedTextView) v.findViewById(R.id.check1);
  33. chkItem.setChecked(!chkItem.isChecked());
  34. Toast.makeText(MainActivity.this, "您點選了第 "+(position+1)+" 項", Toast.LENGTH_SHORT).show();
  35. listShow.set(position, chkItem.isChecked());
  36. }
  37. }
  38. );
  39.  
  40. listShow = new ArrayList<Boolean>();
  41. list = new ArrayList<String>();
  42. for(int x=1;x<11;x++)
  43. {
  44. list.add("項目"+x);
  45. listShow.add(true);
  46. }
  47. ListAdapter adapterItem = new ListAdapter(this, list);
  48. listview.setAdapter(adapterItem);
  49. }
  50.  
  51. ...
  52. ...
  53.  
  54. // 要離開前檢查哪些 item 是打勾的
  55. @Override
  56. public void onBackPressed()
  57. {
  58. for(int x=0;x<listShow.size();x++)
  59. {
  60. Log.d(TAG,"listShow("+x+") is "+listShow.get(x));
  61. if(listShow.get(x)==true)
  62. {
  63. // 寫下您對打勾 item 的對應處理
  64. ...
  65. ...
  66. }
  67. }
  68. finish();
  69. }
  70. }

建立 ListAdapter.java
  1. package com.example.chktextv;
  2.  
  3. import java.util.List;
  4.  
  5. import android.app.Activity;
  6. import android.content.Context;
  7. import android.view.LayoutInflater;
  8. import android.view.View;
  9. import android.view.ViewGroup;
  10. import android.widget.BaseAdapter;
  11. import android.widget.CheckedTextView;
  12.  
  13. public class ListAdapter extends BaseAdapter
  14. {
  15. private Activity activity;
  16. private List<String> mList;
  17. private static LayoutInflater inflater = null;
  18. public ListAdapter(Activity a, List<String> list)
  19. {
  20. activity = a;
  21. mList = list;
  22. inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
  23. }
  24.  
  25. public int getCount()
  26. {
  27. return mList.size();
  28. }
  29.  
  30. public Object getItem(int position)
  31. {
  32. return position;
  33. }
  34.  
  35. public long getItemId(int position)
  36. {
  37. return position;
  38. }
  39. public View getView(int position, View convertView, ViewGroup parent)
  40. {
  41. View vi = convertView;
  42. if(convertView==null)
  43. {
  44. vi = inflater.inflate(R.layout.list_item, null);
  45. }
  46. CheckedTextView chkBshow = (CheckedTextView) vi.findViewById(R.id.check1);
  47. chkBshow.setText(mList.get(position).toString());
  48. return vi;
  49. }
  50. }

9 則留言:

  1. 您好

    我嘗試做這個範例

    一直失敗

    卡在MainActivity.java

    不知道是否可以提供這個範例的Source Code

    謝謝

    回覆刪除
  2. 謝謝指正,已修改,您再試試。

    回覆刪除
  3. 不好意思!我再項目超過10個的時候。
    點第0項,第9項也會被選取?
    這是我哪邊設定部正確呢。

    回覆刪除
  4. 不好意思!我再項目超過10個的時候。
    點第0項,第9項也會被選取?
    這是我哪邊設定部正確呢。

    不知道,我試著新建一個專案,再將上面各個檔案內容複製、貼上。執行後,並不會有您說的情形。

    另外,我的是從第 1 項開始,並沒有第 0 項,您再對照看看,您的程式碼 和 本筆記的 哪裡不同。

    回覆刪除
  5. 謝謝您的幫助
    原本還想說要用checkbox原來有這麼好用的東西
    謝謝 :D

    回覆刪除
  6. >Peter
    正確來說是超過listview範圍position就會重複選取
    我在專案中選了第一個會連同第八個一起選到
    選第二個會連第九個一起選到

    判斷是沒有找到正確的CheckedTextView
    我也有同樣的問題 有解嗎?

    msg_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    public void onItemClick(AdapterView arg0, View v, int position, long id) {
    Toast.makeText(getApplicationContext(), "您點選了第 " + (position + 1) + " 項", Toast.LENGTH_SHORT).show();
    final CheckedTextView chkItem = (CheckedTextView)v.findViewById(R.id.check);
    if (listShow.get(position) == false) {
    listShow.set(position, true);
    chkItem.setChecked(true);
    Log.e("mainToPost", "T" + position);//Debug用
    } else {
    listShow.set(position, false);
    chkItem.setChecked(false);
    Log.e("mainToPost", "F" + position);//Debug用
    }
    adapter.notifyDataSetChanged();
    }
    });

    回覆刪除
    回覆
    1. 建議將ListAdapter.java修改如下:

      package com.example.mylistview;

      import java.util.List;

      import android.app.Activity;
      import android.content.Context;
      import android.util.Log;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.BaseAdapter;
      import android.widget.CheckedTextView;

      public class ListAdapter extends BaseAdapter
      {
      private Activity activity;
      private List mList;
      private List mListShow; // 這個用來記錄哪幾個 item 是被打勾的

      private static LayoutInflater inflater = null;

      public ListAdapter(Activity a, List list, List listShow)
      {
      activity = a;
      mList = list;
      mListShow = listShow;
      inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      }

      public int getCount()
      {
      return mList.size();
      }

      public Object getItem(int position)
      {
      return position;
      }

      public long getItemId(int position)
      {
      return position;
      }

      public View getView(int position, View convertView, ViewGroup parent)
      {

      View vi = convertView;
      if(convertView==null)
      {
      vi = inflater.inflate(R.layout.list_item, null);
      }

      CheckedTextView chkBshow = (CheckedTextView) vi.findViewById(R.id.check1);

      chkBshow.setText(mList.get(position).toString());

      //設定是否打勾
      chkBshow.setChecked(mListShow.get(position));

      Log.d("KK", "getView(" + position + ", convertView, ViewGroup parent)");

      return vi;
      }
      }

      刪除
    2. 建議將ListAdapter.java修改如下:

      package com.example.mylistview;

      import java.util.List;

      import android.app.Activity;
      import android.content.Context;
      import android.util.Log;
      import android.view.LayoutInflater;
      import android.view.View;
      import android.view.ViewGroup;
      import android.widget.BaseAdapter;
      import android.widget.CheckedTextView;

      public class ListAdapter extends BaseAdapter
      {
      private Activity activity;
      private List mList;
      private List mListShow; // 這個用來記錄哪幾個 item 是被打勾的

      private static LayoutInflater inflater = null;

      public ListAdapter(Activity a, List list, List listShow)
      {
      activity = a;
      mList = list;
      mListShow = listShow;
      inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
      }

      public int getCount()
      {
      return mList.size();
      }

      public Object getItem(int position)
      {
      return position;
      }

      public long getItemId(int position)
      {
      return position;
      }

      public View getView(int position, View convertView, ViewGroup parent)
      {

      View vi = convertView;
      if(convertView==null)
      {
      vi = inflater.inflate(R.layout.list_item, null);
      }

      CheckedTextView chkBshow = (CheckedTextView) vi.findViewById(R.id.check1);

      chkBshow.setText(mList.get(position).toString());

      //設定是否打勾
      chkBshow.setChecked(mListShow.get(position));

      Log.d("KK", "getView(" + position + ", convertView, ViewGroup parent)");

      return vi;
      }
      }

      刪除
    3. 並將MainActivity.java
      ListAdapter adapterItem = new ListAdapter(this, list);
      更改為
      ListAdapter adapterItem = new ListAdapter(this, list, listShow);
      如下:

      package com.example.mylistview;

      import java.util.ArrayList;
      import java.util.List;

      import android.os.Bundle;
      import android.app.Activity;
      import android.util.Log;
      import android.view.View;
      import android.widget.AdapterView;
      import android.widget.CheckedTextView;
      //import android.widget.ListAdapter;
      import android.widget.ListView;
      import android.widget.AdapterView.OnItemClickListener;
      import android.widget.Toast;

      public class MainActivity extends Activity
      {
      private static final String TAG = "MainActivity";
      List list;
      ListView listview;
      List listShow; // 這個用來記錄哪幾個 item 是被打勾的

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

      listview = (ListView) findViewById(R.id.listView1);
      listview.setOnItemClickListener(new OnItemClickListener()
      {
      public void onItemClick(AdapterView parent, View v, int position, long id)
      {
      CheckedTextView chkItem = (CheckedTextView) v.findViewById(R.id.check1);
      chkItem.setChecked(!chkItem.isChecked());
      Toast.makeText(MainActivity.this, "您點選了第 "+(position+1)+" 項", Toast.LENGTH_SHORT).show();
      listShow.set(position, chkItem.isChecked());
      }
      }
      );

      listShow = new ArrayList();
      list = new ArrayList();
      for(int x=1;x<81;x++)
      {
      list.add("項目"+x);
      listShow.add(true);
      }

      ListAdapter adapterItem = new ListAdapter(this, list, listShow);
      listview.setAdapter(adapterItem);
      }

      // 要離開前檢查哪些 item 是打勾的
      @Override
      public void onBackPressed()
      {
      for(int x=0;x<listShow.size();x++)
      {
      Log.d(TAG,"listShow("+x+") is "+listShow.get(x));
      if(listShow.get(x)==true)
      {
      // 寫下您對打勾 item 的對應處理
      }
      }
      finish();
      }
      }

      刪除