2011年10月6日木曜日

Android SimpleCursorTreeAdapter を使う

1段のリストを作る ListView 用の Adapter として、 Object配列やリストをひもづける SimpleAdapter や データベースを検索して得られる Cursor をひもづける SimpleCursorAdapter などがあります。

同じように、2段のリストを作る ExpandableListView 用の Adapter として、Object の配列やリストをひもづける SimpleExpandableListAdapter というのがあります。 実は、Cursor データを ExpandableListView にひもづけるための実装クラスの Adapter はありません。変わりに抽象クラスの SimpleCursorTreeAdapter というものがあります。

# SimpleExpandableListAdapter の使い方は API DemosExpandableList3.java がわかりやすいです。

このクラスを継承した実装クラスを作って利用します。

  1. public class SimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity {  
  2.   
  3.     private static final String[] CONTACTS_PROJECTION = new String[] {  
  4.         Contacts._ID,  
  5.         Contacts.DISPLAY_NAME  
  6.     };  
  7.       
  8.     private static final int GROUP_ID_COLUMN_INDEX = 0;  
  9.       
  10.     private static final String[] PHONE_PROJECTION = new String[] {  
  11.         Phone._ID,  
  12.         Phone.TYPE,  
  13.         Phone.NUMBER  
  14.     };  
  15.   
  16.     public class ExpandableListAdapter extends SimpleCursorTreeAdapter {  
  17.   
  18.         public ExpandableListAdapter(Context context, Cursor c,  
  19.                 int groupLayout, String[] groupFrom, int[] groupTo,   
  20.                 int childLayout, String[] childrenFrom,int[] childrenTo) {  
  21.   
  22.             super(context, c, groupLayout, groupFrom, groupTo,   
  23.                     childLayout, childrenFrom, childrenTo);  
  24.         }  
  25.   
  26.         @Override  
  27.         protected Cursor getChildrenCursor(Cursor groupCursor) {  
  28.             Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();  
  29.             ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX));  
  30.             builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY);  
  31.             Uri phoneNumbersUri = builder.build();  
  32.               
  33.             Cursor c = getContentResolver().query(phoneNumbersUri, PHONE_PROJECTION, Phone.MIMETYPE + "=?"new String[] { Phone.CONTENT_ITEM_TYPE}, null);  
  34.             startManagingCursor(c);  
  35.             return c;  
  36.         }  
  37.     }  
  38.       
  39.     private SimpleCursorTreeAdapter mAdapter;  
  40.       
  41.     @Override  
  42.     public void onCreate(Bundle savedInstanceState) {  
  43.         super.onCreate(savedInstanceState);  
  44.   
  45.         Cursor c = getContentResolver().query(Contacts.CONTENT_URI, CONTACTS_PROJECTION,   
  46.                 Contacts.HAS_PHONE_NUMBER + "=1"nullnull);  
  47.   
  48.         mAdapter = new ExpandableListAdapter(  
  49.                 this, c,  
  50.                 android.R.layout.simple_expandable_list_item_1,  
  51.                 new String[] { Contacts.DISPLAY_NAME },   
  52.                 new int[] { android.R.id.text1 },  
  53.                 android.R.layout.simple_expandable_list_item_1,  
  54.                 new String[] { Phone.NUMBER },  
  55.                 new int[] { android.R.id.text1 });  
  56.           
  57.         startManagingCursor(c);  
  58.           
  59.         setListAdapter(mAdapter);  
  60.     }  
  61. }  


SimpleCursorTreeAdapter にも SimpleCursorAdapter 同様 ViewBinder を設定して表示方法を変更することができます。

  1. public class SimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity {  
  2.   
  3.     private static final String[] CONTACTS_PROJECTION = new String[] {  
  4.         Contacts._ID,  
  5.         Contacts.DISPLAY_NAME  
  6.     };  
  7.       
  8.     private static final int GROUP_ID_COLUMN_INDEX = 0;  
  9.       
  10.     private static final String[] PHONE_PROJECTION = new String[] {  
  11.         Phone._ID,  
  12.         Phone.TYPE,  
  13.         Phone.NUMBER  
  14.     };  
  15.   
  16.     private static final int[] COLOR_LIST = new int[] {  
  17.         Color.parseColor("#002A42"),  
  18.         Color.parseColor("#3DC3EA"),  
  19.         Color.parseColor("#99417B"),  
  20.         Color.parseColor("#F2AE30"),  
  21.         Color.parseColor("#F2D338"),  
  22.     };  
  23.   
  24.     public class ExpandableListAdapter extends SimpleCursorTreeAdapter {  
  25.   
  26.         public ExpandableListAdapter(Context context, Cursor c,  
  27.                 int groupLayout, String[] groupFrom, int[] groupTo,   
  28.                 int childLayout, String[] childrenFrom,int[] childrenTo) {  
  29.   
  30.             super(context, c, groupLayout, groupFrom, groupTo,   
  31.                     childLayout, childrenFrom, childrenTo);  
  32.         }  
  33.   
  34.         @Override  
  35.         protected Cursor getChildrenCursor(Cursor groupCursor) {  
  36.             Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();  
  37.             ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX));  
  38.             builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY);  
  39.             Uri phoneNumbersUri = builder.build();  
  40.               
  41.             Cursor c = getContentResolver().query(phoneNumbersUri, PHONE_PROJECTION, Phone.MIMETYPE + "=?"new String[] { Phone.CONTENT_ITEM_TYPE}, null);  
  42.             startManagingCursor(c);  
  43.             return c;  
  44.         }  
  45.     }  
  46.       
  47.     private SimpleCursorTreeAdapter mAdapter;  
  48.       
  49.     @Override  
  50.     public void onCreate(Bundle savedInstanceState) {  
  51.         super.onCreate(savedInstanceState);  
  52.   
  53.         Cursor c = getContentResolver().query(Contacts.CONTENT_URI, CONTACTS_PROJECTION,   
  54.                 Contacts.HAS_PHONE_NUMBER + "=1"nullnull);  
  55.   
  56.         mAdapter = new ExpandableListAdapter(  
  57.                 this, c,  
  58.                 android.R.layout.simple_expandable_list_item_1,  
  59.                 new String[] { Contacts.DISPLAY_NAME },  
  60.                 new int[] { android.R.id.text1 },  
  61.                 android.R.layout.simple_expandable_list_item_2,  
  62.                 new String[] { Phone.TYPE, Phone.NUMBER },  
  63.                 new int[] { android.R.id.text1, android.R.id.text2 });  
  64.           
  65.         mAdapter.setViewBinder(new SimpleCursorTreeAdapter.ViewBinder() {  
  66.               
  67.             public boolean setViewValue(View view, Cursor cursor, int columnIndex) {  
  68.                 if (cursor.getColumnName(columnIndex).equals(Phone.TYPE)) {  
  69.                     int type = cursor.getInt(columnIndex);  
  70.                     String text = (String) Phone.getTypeLabel(getResources(), type, null);  
  71.                       
  72.                     ((TextView) view).setText(text);  
  73.                     ((TextView) view).setTextColor(COLOR_LIST[type % 5]);  
  74.                     return true;  
  75.                 }  
  76.                 return false;  
  77.             }  
  78.         });  
  79.           
  80.         startManagingCursor(c);  
  81.           
  82.         setListAdapter(mAdapter);  
  83.     }  
  84. }  


Y.A.M の 雑記帳: Android AsyncQueryHandler を使う - でも触れたように、UI スレッドで Cursor を取得するためのクエリを走らせると UI スレッドをブロックしていまいます。それを避けるために SimpleCursorTreeAdapter に対しても AsyncQueryHandler を使うようにすることができます。

  1. public class AsyncQuerySimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity {  
  2.   
  3.     private static final String[] CONTACTS_PROJECTION = new String[] {  
  4.         Contacts._ID,  
  5.         Contacts.DISPLAY_NAME  
  6.     };  
  7.       
  8.     private static final int GROUP_ID_COLUMN_INDEX = 0;  
  9.       
  10.     private static final String[] PHONE_PROJECTION = new String[] {  
  11.         Phone._ID,  
  12.         Phone.TYPE,  
  13.         Phone.NUMBER  
  14.     };  
  15.   
  16.     private static final int[] COLOR_LIST = new int[] {  
  17.         Color.parseColor("#002A42"),  
  18.         Color.parseColor("#3DC3EA"),  
  19.         Color.parseColor("#99417B"),  
  20.         Color.parseColor("#F2AE30"),  
  21.         Color.parseColor("#F2D338"),  
  22.     };  
  23.       
  24.     private static final int TOKEN_GROUP = 0;  
  25.     private static final int TOKEN_CHILD = 1;  
  26.   
  27.       
  28.     private static final class QueryHandler extends AsyncQueryHandler {  
  29.         private CursorTreeAdapter mAdapter;  
  30.   
  31.         public QueryHandler(Context context, CursorTreeAdapter adapter) {  
  32.             super(context.getContentResolver());  
  33.             this.mAdapter = adapter;  
  34.         }  
  35.   
  36.         @Override  
  37.         protected void onQueryComplete(int token, Object cookie, Cursor cursor) {  
  38.             switch (token) {  
  39.             case TOKEN_GROUP:  
  40.                 mAdapter.setGroupCursor(cursor);  
  41.                 break;  
  42.   
  43.             case TOKEN_CHILD:  
  44.                 int groupPosition = (Integer) cookie;  
  45.                 mAdapter.setChildrenCursor(groupPosition, cursor);  
  46.                 break;  
  47.             }  
  48.         }  
  49.     }  
  50.       
  51.     public class ExpandableListAdapter extends SimpleCursorTreeAdapter {  
  52.   
  53.         public ExpandableListAdapter(Context context,   
  54.                 int groupLayout, int childLayout,   
  55.                 String[] groupFrom, int[] groupTo,   
  56.                 String[] childrenFrom,int[] childrenTo) {  
  57.   
  58.             super(context, null, groupLayout, groupFrom, groupTo,   
  59.                     childLayout, childrenFrom, childrenTo);  
  60.         }  
  61.   
  62.         @Override  
  63.         protected Cursor getChildrenCursor(Cursor groupCursor) {  
  64.             Uri.Builder builder = Contacts.CONTENT_URI.buildUpon();  
  65.             ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX));  
  66.             builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY);  
  67.             Uri phoneNumbersUri = builder.build();  
  68.   
  69.             mQueryHandler.startQuery(TOKEN_CHILD, groupCursor.getPosition(), phoneNumbersUri,   
  70.                     PHONE_PROJECTION, Phone.MIMETYPE + "=?",   
  71.                     new String[] { Phone.CONTENT_ITEM_TYPE }, null);  
  72.   
  73.             return null;  
  74.         }  
  75.     }  
  76.       
  77.     private QueryHandler mQueryHandler;  
  78.     private SimpleCursorTreeAdapter mAdapter;  
  79.       
  80.     @Override  
  81.     public void onCreate(Bundle savedInstanceState) {  
  82.         super.onCreate(savedInstanceState);  
  83.   
  84.         mAdapter = new ExpandableListAdapter(  
  85.                 this,   
  86.                 android.R.layout.simple_expandable_list_item_1,  
  87.                 android.R.layout.simple_expandable_list_item_2,  
  88.                 new String[] { Contacts.DISPLAY_NAME },  
  89.                 new int[] { android.R.id.text1 },  
  90.                 new String[] { Phone.TYPE, Phone.NUMBER },  
  91.                 new int[] { android.R.id.text1, android.R.id.text2 });  
  92.           
  93.         mAdapter.setViewBinder(new SimpleCursorTreeAdapter.ViewBinder() {  
  94.               
  95.             public boolean setViewValue(View view, Cursor cursor, int columnIndex) {  
  96.                 if (cursor.getColumnName(columnIndex).equals(Phone.TYPE)) {  
  97.                     int type = cursor.getInt(columnIndex);  
  98.                     String text = (String) Phone.getTypeLabel(getResources(), type, null);  
  99.                       
  100.                     ((TextView) view).setText(text);  
  101.                     ((TextView) view).setTextColor(COLOR_LIST[type % 5]);  
  102.                     return true;  
  103.                 }  
  104.                 return false;  
  105.             }  
  106.         });  
  107.           
  108.         setListAdapter(mAdapter);  
  109.   
  110.         mQueryHandler = new QueryHandler(this, mAdapter);  
  111.         mQueryHandler.startQuery(TOKEN_GROUP, null, Contacts.CONTENT_URI,   
  112.                 CONTACTS_PROJECTION, Contacts.HAS_PHONE_NUMBER + "=1"nullnull);  
  113.     }  
  114.   
  115.     @Override  
  116.     protected void onDestroy() {  
  117.         super.onDestroy();  
  118.         mAdapter.changeCursor(null);  
  119.         mAdapter = null;  
  120.     }  
  121. }  



1 件のコメント: