はじめに
Android の ListView
の使い方を忘れてしまっていたので ListView
の使い方についてまとめておきます(検索してもぱっとコードが出てこなかった。。。)。
ListView
は一覧表示するときお世話になるのでこれさえ使えればわりと色々なアプリをつくれる気がします(iOS でいう UITableView
だと思います)。
Cursor 系はよくわかってないので機会があればまた更新したいと思います。。。
ソース(github)ここをいい感じにしていきたい。
ListView
ListView
の定義は下記です。
1 |
open class ListView : AbsListView |
XML attributes で下記の値が設定できるようです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// 境界線(Drawable or color) android:divider // 境界線の高さ android:dividerHeight // リスト android:entries // false ならフッターの上の線を非表示にする(デフォtrue) android:footerDividersEnabled // false ならヘッダーの下の線を非表示にする(デフォtrue) android:headerDividersEnabled |
ListView
自体はどのような View を表示するという情報は持っておらず ListAdapter
に対して表示する View を要求するみたいです(ListAdapter
が UITableViewDataSourece
+ UITableViewCell
相当だと思います)。
つまり ListView
で一覧表示したい場合はアダプター(ListAdapter
)の設定が必須です。表示の更新とかもアダプターを介しておこないます。
アダプター
ListAdapter
の定義は下記です。
1 |
interface ListAdapter : Adapter |
ListAdapter
を継承したクラスは下記 8 つが用意されているようです。
- ArrayAdapter
- BaseAdapter
- SimpleAdapter
- WrapperListAdapter
- HeaderViewListAdapter
- CursorAdapter
- ResourceCursorAdapter
- SimpleCursorAdapter
ArrayAdapter
定義は下記です。
1 |
open class ArrayAdapter<T : Any!> : BaseAdapter, Filterable, ThemedSpinnerAdapter |
1つのテキストを表示したい場合に使うようです。設定したリストのテキストが TextView
に表示されます。下記のコンストラクタが用意されています。
1 2 3 4 5 6 7 8 9 10 11 |
<init>(context: Context, resource: Int) <init>(context: Context, resource: Int, textViewResourceId: Int) <init>(context: Context, resource: Int, objects: Array<T>) <init>(context: Context, resource: Int, textViewResourceId: Int, objects: Array<T>) <init>(context: Context, resource: Int, objects: MutableList<T>) <init>(context: Context, resource: Int, textViewResourceId: Int, objects: MutableList<T>) |
引数
- Context
何か適当なやつを。。。 - resource
レイアウトファイルの ID です(TextView 必須)。 - textViewResourceId
リストの項目を表示する TextView の id(レイアウトを自作するときに使う)。 - objects
表示するリストです。
resource
レイアウトファイルの ID です。レイアウトファイルには少なくとも1つの TextView
が必須です。
自作しなくても1つの TextView
を表示するだけなら android.R.layout.simple_list_item_1 が用意されています。
こんな感じで使います。
1 2 3 |
val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1) // 別にここでリスト設定してもいい listView.adapter = adapter adapter.add("AAA") |
textViewResourceId
リストの項目を表示する TextView
の ID です。レイアウトを自作する場合に利用するものと思われます。
下記のようなレイアウトファイルを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:orientation="horizontal"> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> </LinearLayout> |
下記のように設定するとリストが表示されました。
1 2 3 4 5 |
val adapter = ArrayAdapter<String>(this, R.layout.text_row, R.id.text) // 別にここでリスト設定してもいい listView.adapter = adapter adapter.add("AAA") adapter.add("BBB") adapter.add("CCC") |
レイアウトファイルを修正すれば複数の View を置くことも可能です。ただし、可変で値を設定できるのは ID を指定している TextView
のみです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:padding="16dp" android:orientation="horizontal"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="タイトル:" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> </LinearLayout> |
こんな感じ。
objects
表示する項目のリストです。Mutable ではない場合は Adapter
の clear()
や add()
といったメソッドを使用したときにクラッシュしてしまいます。
型は String
に変換できるなら何でもいいみたいです。試しに下記のように設定してみるとちゃんと表示されました。
1 2 3 4 5 6 7 8 |
data class Hoge( val text: String, val num: Int, ) listView.adapter = ArrayAdapter<Hoge>(this, android.R.layout.simple_list_item_1, listOf(Hoge("hoge", 1), Hoge("fuga", 2), Hoge("piyo", 3))) |
こんな感じです。
BaseAdapter
定義は下記です。
1 |
abstract class BaseAdapter : ListAdapter, SpinnerAdapter |
カスタムアダプターを作るときに継承させるクラスです。これさえ使えればだいたいいける気がします。
使い方
レイアウトファイルと BaseAdapter
を継承したカスタムクラスを用意します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:padding="16dp" android:orientation="horizontal"> <CheckBox android:id="@+id/checkBox" android:layout_width="wrap_content" android:layout_height="wrap_content" android:clickable="false" android:focusable="false" android:focusableInTouchMode="false" /> <TextView android:id="@+id/text" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> </LinearLayout> |
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 |
class CustomAdapter(context: Context, private val items: List<Hoge>) : BaseAdapter() { private val inflater = LayoutInflater.from(context) override fun getCount() = items.size override fun getItem(position: Int) = items[position] // 一覧内で一意になるやつ override fun getItemId(position: Int) = position.toLong() override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { val view = convertView ?: createView(parent) val item = getItem(position) val viewHolder = view.tag as ViewHolder viewHolder.text.text = item.text viewHolder.checkBox.isChecked = item.num % 2 == 0 return view } private fun createView(parent: ViewGroup?): View { val view = inflater.inflate(R.layout.custom_row, parent, false) view.tag = ViewHolder(view) return view } private class ViewHolder(view: View) { val text = view.findViewById<TextView>(R.id.text) val checkBox = view.findViewById<CheckBox>(R.id.checkBox) } } |
ポイントは getView()
の部分です。View
の生成と findViewById
はコストが高めなのでここを注意しないとスクロールがカクカクしてしまいます。convertView
が null
の場合のみView
を生成し、毎回 findViewById
をしないように生成時に ViewHolder
で各 View
を保持しておきます。
あとは ListView
に設定するだけです。
1 2 3 4 5 6 |
val adapter = CustomAdapter( this, listOf(Hoge("hoge", 1), Hoge("fuga", 2), Hoge("piyo", 3)) ) val listView = findViewById<ListView>(R.id.list) listView.adapter = adapter |
こんな感じ。
SimpleAdapter
定義は下記です。
1 |
open class SimpleAdapter : BaseAdapter, Filterable, ThemedSpinnerAdapter |
Map
の一覧を表示する場合に使うみたいです。表示はテキストのみです。
コンストラクタは下記が用意されています。
1 |
<init>(context: Context!, data: MutableList<out MutableMap<String!, *>!>!, resource: Int, from: Array<String!>!, to: IntArray!) |
引数
- context
何か適当なやつを。。。 - data
表示するデータ。キーはfrom
に対応してる必要があります。 - resource
レイアウトファイルの ID です。 - from
表示したいデータのMap
のキー一覧です。 - to
from
に対応したTextView
の ID 一覧です。
使い方
レイアウトファイルを下記のように作成します。
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 |
<?xml version="1.0" encoding="utf-8"?> <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="wrap_content" android:padding="16dp" android:orientation="vertical"> <TextView android:id="@+id/text1" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> <TextView android:id="@+id/text2" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> <TextView android:id="@+id/text3" android:layout_width="wrap_content" android:layout_height="wrap_content" tools:text="Text" /> </LinearLayout> |
ListView
の設定は下記。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
val listView = findViewById<ListView>(R.id.list) val list = mutableListOf( mutableMapOf( "hoge" to Hoge("hoge1", 1), "fuga" to Hoge("fuga1", 1), "piyo" to Hoge("piyo1", 1) ), mutableMapOf( "hoge" to Hoge("hoge2", 2), "fuga" to Hoge("fuga2", 2), "piyo" to Hoge("piyo2", 2) ), mutableMapOf( "hoge" to Hoge("hoge3", 3), "fuga" to Hoge("fuga3", 3), "piyo" to Hoge("piyo3", 3) ) ) val adapter = SimpleAdapter(this, list, R.layout.simple_text_row, arrayOf("hoge", "fuga", "piyo"), arrayOf(R.id.text1, R.id.text2, R.id.text3).toIntArray() ) listView.adapter = adapter |
こんな感じです。
WrapperListAdapter
定義は下記です。
1 |
interface WrapperListAdapter : ListAdapter |
アダプターをラップするアダプター?? getWrappedAdapter()
で何をラップしてるか取得できるらしいですがイマイチ使い方はわかりません。。。
List adapter that wraps another list adapter. The wrapped adapter can be retrieved by calling getWrappedAdapter().
HeaderViewListAdapter
定義は下記です。
1 |
open class HeaderViewListAdapter : WrapperListAdapter, Filterable |
ヘッダーがある ListView
を扱う際に使われるらしい。。。(ちょっとよくわかってない)
おそらく addHeaderView()
, addFooterView()
とかでヘッダー・フッターを表示するとその Adapter が HeaderViewListAdapter
でラップされるんだと思われます。
getHeadersCount()
などのメソッドが用意されているのでヘッダー・フッターを操作したいときに使うのかも??
ListAdapter used when a ListView has header views. This ListAdapter wraps another one and also keeps track of the header views and their associated data objects.
This is intended as a base class; you will probably not need to use this class directly in your own code.
CursorAdapter
定義は下記です。
1 |
abstract class CursorAdapter : BaseAdapter, Filterable, ThemedSpinnerAdapter |
DB で扱う Cursor を扱う用のアダプター。たぶん大量にデータを扱う際に使う??(ちょっとわからない。。。)
CursorAdapter
を継承したカスタムクラスを作って使う??
下記2つのメソッドで表示処理を行うんだと思われます。
1 2 3 4 5 6 7 8 9 10 11 |
abstract fun bindView( view: View!, context: Context!, cursor: Cursor! ): Unit abstract fun newView( context: Context!, cursor: Cursor!, parent: ViewGroup! ): View! |
コンストラクタは下記2つが用意されている模様。
1 2 3 |
<init>(context: Context!, c: Cursor!, autoRequery: Boolean) <init>(context: Context!, c: Cursor!, flags: Int) |
引数
- context
何か適当なやつを。。。 - c
表示するデータ。 - autoRequery
true
にすると常に最新のデータを DB から取って表示してくれるらしい(けどtrue
にはしない方がいいらしい??)。 - flags
アダプタの動作を決定するために使用されるフラグ??(FLAG_AUTO_REQUERY
,FLAG_REGISTER_CONTENT_OBSERVER
)
ResourceCursorAdapter
定義は下記です。
1 |
abstract class ResourceCursorAdapter : CursorAdapter |
コンストラクタは下記2つが用意されている模様。
1 2 3 |
<init>(context: Context!, layout: Int, c: Cursor!, autoRequery: Boolean) <init>(context: Context!, layout: Int, c: Cursor!, flags: Int) |
引数
下記以外は CursorAdapter
と同様。
- layout
レイアウトファイルの ID です。
CursorAdapter
で必要な View をレイアウトファイルで指定して使うんだと思います(よくわかってない。。。 CusorAdapter
でカスタムしたい場合は基本こいつを継承させるのかも??)。
SimpleCursorAdapter
定義は下記です。
1 |
open class SimpleCursorAdapter : ResourceCursorAdapter |
テキスト表示だけの場合に使うのかも??
用意されているコンストラクタは下記1つの模様。
1 |
<init>(context: Context!, layout: Int, c: Cursor!, from: Array<String!>!, to: IntArray!, flags: Int) |
引数
下記以外は ResourceCursorAdapter
と同様。
- from
表示したいデータの DB のカラム名一覧です。 - to
from
に対応したTextView
の ID 一覧です。
ヘッダー・フッター
ListView
には下記メソッドが用意されており、これでヘッダー・フッターを表示するようです。
1 2 3 4 5 |
addFooterView(v: View!, data: Any!, isSelectable: Boolean) addFooterView(v: View!) addHeaderView(v: View!, data: Any!, isSelectable: Boolean) addHeaderView(v: View!) |
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 |
val listView = findViewById<ListView>(R.id.list) val adapter = ArrayAdapter( this, android.R.layout.simple_list_item_1, mutableListOf(Hoge("hoge", 1), Hoge("fuga", 2), Hoge("piyo", 3)) ) listView.adapter = adapter val header1 = TextView(this).apply { this.layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 100 ) this.setBackgroundColor(getColor(R.color.purple_200)) this.setTextColor(getColor(R.color.white)) this.text = "Header1" } listView.addHeaderView(header1) val header2 = TextView(this).apply { this.layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 100 ) this.setBackgroundColor(getColor(R.color.purple_500)) this.setTextColor(getColor(R.color.white)) this.text = "Header2" } listView.addHeaderView(header2) val footer1 = TextView(this).apply { this.layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 100 ) this.setBackgroundColor(getColor(R.color.purple_200)) this.setTextColor(getColor(R.color.white)) this.text = "Footer1" } listView.addFooterView(footer1) val footer2 = TextView(this).apply { this.layoutParams = LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, 100 ) this.setBackgroundColor(getColor(R.color.purple_500)) this.setTextColor(getColor(R.color.white)) this.text = "Footer2" } listView.addFooterView(footer2) |
上記のように設定するとこんな感じになりました。
ヘッダー・フッターは複数設定できるようです。KITKAT 以前は Adapter
設定前に add()
する必要があったようですが今はどこでもいいみたいです。
おわりに
これで一覧表示がだいたいできるようになりました!
セクション表示とかもっと色々やりたい場合は RecyclerView
を使うみたいです(ちょっとだけ設定がめんどくさい)。
For a more modern, flexible, and performant approach to displaying lists, use android.support.v7.widget.RecyclerView.
参考
- Androidのお勉強 第二回 ListViewと独自Adapterについて
- [Android] ListViewアイテムの移動、削除
- ListView
- ListAdapter
- ArrayAdapter
- BaseAdapter
- WrapperListAdapter
- CursorAdapter
- HeaderViewListAdapter
- ResourceCursorAdapter
- SimpleAdapter
- SimpleCursorAdapter
コメント