2012-10-25

【Android】CheckedTextView - 用於 ListView 的 CheckBox

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

先建立主 Activitylayout(檔名: activity_main.xml):
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <ListView
        android:id="@+id/listView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" >
    </ListView>

</LinearLayout>


然後建立 ListViewitemlayout(檔名: list_item.xml):
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:orientation="horizontal" >

    <CheckedTextView
      android:id="@+id/check1"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:gravity="center_vertical"
      android:checkMark="?android:attr/listChoiceIndicatorMultiple"
      android:checked="true"
      android:text="CheckedTextView"
      android:textAppearance="?android:attr/textAppearanceMedium" />
    
</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) :
package com.example.chktextv;

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.ListView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Toast;

public class MainActivity extends Activity
{
    private static final String TAG = "MainActivity";
    List<String> list;
    ListView listview;
    List<Boolean> 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<Boolean>();
        list = new ArrayList<String>();
        for(int x=1;x<11;x++)
        {
             list.add("項目"+x);
             listShow.add(true);
        }
        ListAdapter adapterItem = new ListAdapter(this, list);
        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();
    }
}

建立 ListAdapter.java
package com.example.chktextv;

import java.util.List;

import android.app.Activity;
import android.content.Context;
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<String> mList;
    
    private static LayoutInflater inflater = null;
    
    public ListAdapter(Activity a, List<String> list)
    {
        activity = a;
        mList = list;
        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());
        
        return vi;
    }
}

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();
      }
      }

      刪除