AndroidでのListView:リストのカスタマイズ

AndroidのListView に関する記事の続き。リスト項目のアイコンやこれらの項目にチェックボックスを追加するなど、より複雑な使用例を検討します。 また、コードを最適化する可能性も検討します。



記事は著者の許可を得たこの記事の翻訳です。



例:独自のテンプレートを使用したListActivity。



リストアイテム用の独自のテンプレートを作成し、それをアダプタに適用できます。 テンプレートはリストの各要素で同じになりますが、より柔軟にする方法について説明します。 この例では、リスト内の各アイテムにアイコンを追加します。



プロジェクト「de.vogella.android.listactivity」のres / layoutフォルダーに「rowlayout.xml」テンプレートファイルを作成します。

<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" > <ImageView android:id="@+id/icon" android:layout_width="22px" android:layout_height="22px" android:layout_marginLeft="4px" android:layout_marginRight="10px" android:layout_marginTop="4px" android:src="@drawable/ic_launcher" > </ImageView> <TextView android:id="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@+id/label" android:textSize="20px" > </TextView> </LinearLayout>
      
      





アクティビティを次のように変更します。 コードは前の例とほとんど同じですが、唯一の違いは、ArrayAdapterで独自のテンプレートを使用し、テキストに含まれるユーザーインターフェイス要素をアダプターに伝えることです。 標準テンプレートを使用したため、前の記事ではこれを行いませんでした。

 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; //    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.rowlayout, R.id.label, values); setListAdapter(adapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { String item = (String) getListAdapter().getItem(position); Toast.makeText(this, item + " selected", Toast.LENGTH_LONG).show(); } }
      
      









例:柔軟なテンプレートを使用したListActivity



前の例は両方とも一度にすべての行に同じパターンを使用します。 特定の行の外観を変更する場合は、アダプターを定義し、getView()メソッドを置き換える必要があります。



このメソッドは、ListViewの個々の要素を作成します。 getView()はビューを返します。 このビューは実際にはテンプレート(ViewGroup)であり、ImageViewやTextViewなどの他のビューが含まれています。 getView()を使用すると、個々のビューのパラメーターを変更することもできます。



getView()でXMLからテンプレートを読み取るには、LayoutInflatorシステムサービスを使用できます。



この例では、ArrayAdapterを拡張していますが、BaseAdapterを直接実装することもできます。



単純なアダプター定義


最適化に注意を払わずに、独自のアダプターを作成するのは非常に簡単です。 アクティビティに表示するデータを取得し、リストアイテムに保存するだけです。 getView()で、要素の定義済みテンプレートを設定し、findViewById()で必要な要素を取得します。 その後、プロパティを定義できます。



この例では、「no.png」と「ok.png」の2つの画像を使用します。 それらを「res / drawable-mdpi」フォルダーに入れます。 写真を使用します。 それらが見つからない場合は、「icon.png」をコピーして、グラフィカルエディターを使用して少し変更します。



アダプタとして機能するクラス「MySimpleArrayAdapter」を作成します。

 package de.vogella.android.listactivity; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MySimpleArrayAdapter extends ArrayAdapter<String> { private final Context context; private final String[] values; public MySimpleArrayAdapter(Context context, String[] values) { super(context, R.layout.rowlayout, values); this.context = context; this.values = values; } @Override public View getView(int position, View convertView, ViewGroup parent) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); View rowView = inflater.inflate(R.layout.rowlayout, parent, false); TextView textView = (TextView) rowView.findViewById(R.id.label); ImageView imageView = (ImageView) rowView.findViewById(R.id.icon); textView.setText(values[position]); //    Windows  iPhone String s = values[position]; if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { imageView.setImageResource(R.drawable.no); } else { imageView.setImageResource(R.drawable.ok); } return rowView; } }
      
      





このアダプターを使用するには、MyListクラスを次のように変更します



 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; MySimpleArrayAdapter adapter = new MySimpleArrayAdapter(this, values); setListAdapter(adapter); } }
      
      





このアプリケーションを起動すると、いくつかのアイコンが異なるアイコンのリストが表示されます。



独自のアダプターのパフォーマンスを最適化する


要素ごとにJavaオブジェクトを作成すると、メモリ消費と時間が消費されます。 既に述べたように、Androidは表示されなくなったリストの要素(ビュー)を消去し、convertViewパラメーターを介してgetView()メソッドに管理を委任します。



アダプタはこのビューを使用して、この要素のテンプレートを「肥大化」させることを回避できます。 これにより、メモリが節約され、プロセッサの負荷が軽減されます。



実装では、convertViewが空でない場合、コンテンツのconvertViewを確認し、既存のテンプレートに新しいデータを送信して、コンテンツを再割り当てする必要があります。



また、実装ではViewHolderモデルを使用します。 findViewById()メソッドは非常にリソースを消費するため、直接必要でない場合は避けてください。



ViewHolderは、必要なテンプレートへのリンクをリストアイテムに保存します。 このViewHolderは、setTag()メソッドを使用して要素にアタッチされます。 各ビューには、適用されたリンクが含まれる場合があります。 アイテムがクリアされると、getTag()メソッドを介してViewHolderを取得できます。 ロードされているように見えますが、実際にはfindViewById()を繰り返し呼び出すよりも速く動作します。



両方の手法(既存のビューとViewHolderモデルの再割り当て)により、特に大容量のデータでパフォーマンスが約15%向上します。



de.vogella.android.listactivityプロジェクトを引き続き使用して、MyArrayAdapter.javaクラスを作成します。

 package de.vogella.android.listactivity; import android.app.Activity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; import android.widget.ImageView; import android.widget.TextView; public class MyArrayAdapter extends ArrayAdapter<String> { private final Activity context; private final String[] names; public MyArrayAdapter(Activity context, String[] names) { super(context, R.layout.rowlayout, names); this.context = context; this.names = names; } //           //    static class ViewHolder { public ImageView imageView; public TextView textView; } @Override public View getView(int position, View convertView, ViewGroup parent) { // ViewHolder       ViewHolder holder; //   ,    //             View rowView = convertView; if (rowView == null) { LayoutInflater inflater = context.getLayoutInflater(); rowView = inflater.inflate(R.layout.rowlayout, null, true); holder = new ViewHolder(); holder.textView = (TextView) rowView.findViewById(R.id.label); holder.imageView = (ImageView) rowView.findViewById(R.id.icon); rowView.setTag(holder); } else { holder = (ViewHolder) rowView.getTag(); } holder.textView.setText(names[position]); //    Windows  iPhone String s = names[position]; if (s.startsWith("Windows7") || s.startsWith("iPhone") || s.startsWith("Solaris")) { holder.imageView.setImageResource(R.drawable.no); } else { holder.imageView.setImageResource(R.drawable.ok); } return rowView; } }
      
      





 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; public class MyListActivity extends ListActivity { public void onCreate(Bundle icicle) { super.onCreate(icicle); String[] values = new String[] { "Android", "iPhone", "WindowsMobile", "Blackberry", "WebOS", "Ubuntu", "Windows7", "Max OS X", "Linux", "OS/2" }; setListAdapter(new MyArrayAdapter(this, values)); } }
      
      









高度なListActivity



アイテムのロングクリック処理


ビューにLongItemClickListenerを追加することもできます。 これを行うには、getListView()メソッドでListViewを取得し、setOnItemLongClickListener()メソッドでロングクリック処理を定義します。

 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; public class MyList extends ListActivity { /** ,     . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //        ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; ArrayAdapter<String> adapter = new TwoLayoutsArrayAdapter(this, names); setListAdapter(adapter); ListView list = getListView(); list.setOnItemLongClickListener(new OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { Toast.makeText(MyList.this, "Item in position " + position + " clicked", Toast.LENGTH_LONG).show(); //  "",    ,  // onListItemClick    return true; } }); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { super.onListItemClick(l, v, position, id); //  ,    Object o = this.getListAdapter().getItem(position); String keyword = o.toString(); Toast.makeText(this, "You selected: " + keyword, Toast.LENGTH_SHORT) .show(); } }
      
      





データモデルと相互作用する要素


リストアイテムテンプレートには、データモデルとやり取りするビューも含まれる場合があります。 たとえば、リストアイテムでチェックボックスを使用できます。チェックボックスが有効になっている場合、アイテムに表示されるデータを変更できます。



まだ同じプロジェクトを使用しています。 「rowbuttonlayout.xml」リストアイテムテンプレートを作成します。

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="@+id/label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/label" android:textSize="30px"></TextView> <CheckBox android:id="@+id/check" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="4px" android:layout_marginRight="10px" android:layout_alignParentRight="true" ></CheckBox> </RelativeLayout>
      
      





この例では、チェックされている場合、要素の名前とそのコンテンツを含むModelクラスを作成します。

 package de.vogella.android.listactivity; public class Model { private String name; private boolean selected; public Model(String name) { this.name = name; selected = false; } public String getName() { return name; } public void setName(String name) { this.name = name; } public boolean isSelected() { return selected; } public void setSelected(boolean selected) { this.selected = selected; } }
      
      





次のアダプターを作成します。 このアダプターは、チェックボックス変更処理を追加します。 チェックボックスが有効になっている場合、モデルのデータも変更されます。 必須のチェックボックスは、setTag()メソッドを通じてモデルを取得します。

 package de.vogella.android.listactivity; import java.util.List; import android.app.Activity; 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 InteractiveArrayAdapter extends ArrayAdapter<Model> { private final List<Model> list; private final Activity context; public InteractiveArrayAdapter(Activity context, List<Model> list) { super(context, R.layout.rowbuttonlayout, list); this.context = context; this.list = list; } static class ViewHolder { protected TextView text; protected CheckBox checkbox; } @Override public View getView(int position, View convertView, ViewGroup parent) { View view = null; if (convertView == null) { LayoutInflater inflator = context.getLayoutInflater(); view = inflator.inflate(R.layout.rowbuttonlayout, null); final ViewHolder viewHolder = new ViewHolder(); viewHolder.text = (TextView) view.findViewById(R.id.label); viewHolder.checkbox = (CheckBox) view.findViewById(R.id.check); viewHolder.checkbox .setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { Model element = (Model) viewHolder.checkbox .getTag(); element.setSelected(buttonView.isChecked()); } }); view.setTag(viewHolder); viewHolder.checkbox.setTag(list.get(position)); } else { view = convertView; ((ViewHolder) view.getTag()).checkbox.setTag(list.get(position)); } ViewHolder holder = (ViewHolder) view.getTag(); holder.text.setText(list.get(position).getName()); holder.checkbox.setChecked(list.get(position).isSelected()); return view; } }
      
      





最後に、ListViewを次のように変更します。

 package de.vogella.android.listactivity; import java.util.ArrayList; import java.util.List; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; public class MyList extends ListActivity { /** ,     . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //   ,     ListActivity ArrayAdapter<Model> adapter = new InteractiveArrayAdapter(this, getModel()); setListAdapter(adapter); } private List<Model> getModel() { List<Model> list = new ArrayList<Model>(); list.add(get("Linux")); list.add(get("Windows7")); list.add(get("Suse")); list.add(get("Eclipse")); list.add(get("Ubuntu")); list.add(get("Solaris")); list.add(get("Android")); list.add(get("iPhone")); //      list.get(1).setSelected(true); return list; } private Model get(String s) { return new Model(s); } }
      
      





アプリケーションを起動すると、モデルに反映される要素マークにアクセスできます。







複数選択



単一選択と複数選択を行うこともできます。 例については、次のスニペットを参照してください。 選択したアイテムを取得するには、listView.getCheckedItemPositions()またはlistView.getCheckedItemPositions()を使用します。 listView.getCheckedItemIds()を使用して、選択したアイテムのIDを取得することもできます。

 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity { /** ,     . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //   ,    ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_multiple_choice, android.R.id.text1, names)); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); } }
      
      





 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity { /**     . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //   ,    ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); } } package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity { /**     . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //   ,    ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); ListView listView = getListView(); listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); } }
      
      







ヘッダーとフッター



リストの周りに任意の要素を配置できます。 たとえば、2つのTextViewの間にリストテンプレートを作成できます。 これを行う場合、リストビューにID「@android:id / list」を指定する必要があります。 ListActivityは、この識別子を持つビューを検索します。 この場合、1つのTextViewは常にListView(ヘッダー)の上に表示され、もう1つは下に表示されます。 リストの末尾/先頭でのみフッターとヘッダーを使用して固定しないようにするには、view.setHeaderView()またはview.setFooterView()を使用する必要があります。次に例を示します。

 package de.vogella.android.listactivity; import android.app.ListActivity; import android.os.Bundle; import android.view.View; import android.widget.ArrayAdapter; import android.widget.ListView; public class MyList extends ListActivity { /**    . */ public void onCreate(Bundle icicle) { super.onCreate(icicle); //   ,    ListActivity String[] names = new String[] { "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone", "Linux", "Windows7", "Eclipse", "Suse", "Ubuntu", "Solaris", "Android", "iPhone" }; View header = getLayoutInflater().inflate(R.layout.header, null); View footer = getLayoutInflater().inflate(R.layout.footer, null); ListView listView = getListView(); listView.addHeaderView(header); listView.addFooterView(footer); setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_single_choice, android.R.id.text1, names)); } }
      
      





SimpleCursorAdapter



データベースまたはコンテンツを直接操作している場合は、SimpleCursorAdapterを使用してデータをListViewに転送できます。



アクティビティ「MyListActivity」で新しいプロジェクト「de.vogella.android.listactivity.cursor」を作成します。 そのようなアクティビティを作成します。

 package de.vogella.android.listactivity.cursor; import android.app.ListActivity; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.provider.ContactsContract; import android.widget.ListAdapter; import android.widget.SimpleCursorAdapter; public class MyListActivity extends ListActivity { /**      */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Cursor mCursor = getContacts(); startManagingCursor(mCursor); //     ,    // SimpleListAdapter    . ListAdapter adapter = new SimpleCursorAdapter(this, // . android.R.layout.two_line_list_item, //    mCursor, //   ,   . //  ,   . new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }, // ,         new int[] { android.R.id.text1, android.R.id.text2 }); //  . setListAdapter(adapter); } private Cursor getContacts() { //   Uri uri = ContactsContract.Contacts.CONTENT_URI; String[] projection = new String[] { ContactsContract.Contacts._ID, ContactsContract.Contacts.DISPLAY_NAME }; String selection = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '" + ("1") + "'"; String[] selectionArgs = null; String sortOrder = ContactsContract.Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"; return managedQuery(uri, projection, selection, selectionArgs, sortOrder); } }
      
      





アプリに連絡先へのアクセス権を付与してください。 (AndroidManifest.xmlで「android.permission.READ_CONTACTS」を使用します)。



ご清聴ありがとうございました。 翻訳に対するコメントと修正は大歓迎です。 ソースにもエラーとタイプミスがあります。



再投稿をおpoびします。最初は翻訳としてマークしませんでしたが、最近ここにいます。 jestonのヒントに感謝し 、注意を払い 、間違いから学びました。



All Articles