Genta Hirauchi

公開日:2020/03/02
更新日:2020/03/22

【Kotlin基礎】ListViewで項目を一覧表示させる方法を解説

  • Kotlinで項目を一覧表示させる方法が知りたい。
  • 画像やボタンなども一覧表示させたい。
  • 項目がタップされた時に、何かしらの処理を実行させたい。

アプリを実装していると、配列やリストを一覧表示させたいと思うことはありませんか?

Kotlinでは、ListViewというViewを使用することで、項目の一覧表示を簡単に実装することができます。

本記事では、そんなListViewの基本的な実装方法や、より実用的なカスタマイズしたListViewの実装方法などを、サンプルコードを交え、わかりやすく解説致します。

目次

ListViewで項目を一覧表示させる方法

ArrayAdapterを使用した基本的な実装

ListViewで項目を一覧表示させるには、以下の実装が必要となります。

  • ListViewを実装する
  • ArrayAdapterを生成する
  • ListViewに、Adapterを設定する

ListViewを実装する

まずはListViewを実装する方法です。ListViewは以下のように実装します。

  • activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/container"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

   <ListView
       android:id="@+id/list_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
</LinearLayout>

ListViewを実装するには、<ListView/>タグを使用します。

Point
  • ListViewを実装するには、<ListView/>タグを使用する

ArrayAdapterを生成と設定

続いてArrayAdapterを生成し、ListViewに設定する方法を紹介します。ArrayAdapterは、配列やListを1つずつ表示する際に使用されるAdapterです。

ArrayAdapterの生成とListViewへの設定は、以下のようにコードにて行います。

  • MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)

   // 配列の生成
   val array = arrayOf("リスト1", "リスト2", "リスト3", "リスト4", "リスト5")

   // xmlにて実装したListViewの取得
   val listView = findViewById<ListView>(R.id.list_view)

   // ArrayAdapterの生成
   val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array)

   // ListViewに、生成したAdapterを設定
   listView.adapter = adapter
}

ArrayAdapter生成では、以下のコンストラクタを使用しております。

/**
* Constructor. This constructor will result in the underlying data collection being
* immutable, so methods such as {@link #clear()} will throw an exception.
*
* @param context The current context.
* @param resource The resource ID for a layout file containing a TextView to use when
*                 instantiating views.
* @param objects The objects to represent in the ListView.
*/
public ArrayAdapter(@NonNull Context context, @LayoutRes int resource, @NonNull T[] objects) {
   this(context, resource, 0, Arrays.asList(objects));
}

第1引数には、Contextを指定します。MainActivityにあたるthisを指定しております。

第2引数には、各項目のレイアウトを指定します。サンプルでは、android.R.layout.simple_list_item_1というレイアウトを指定しております。このレイアウトは、予め用意されているレイアウトで、TextViewが実装されたレイアウトになります。

第3引数には、一覧表示させる配列を指定します。

そして、生成したArrayAdapterを、ListViewのAdapterとして設定します。

Point
  • ArrayAdapterの生成時に、項目のレイアウトと、項目一覧を指定する
  • ListView.adapterで、ListViewのAdapterを設定する

項目へのタップイベントの実装

さて、ここまででテキストを一覧表示させることができました。続いては、各項目をタップした際に、何かしらの処理を行う方法を紹介致します。

ListViewにタップイベントを追加するには、OnItemClickListenerを実装します。

以下は、項目タップ時に、項目の内容をトーストで表示するサンプルコードです。

  • MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)

   val array = arrayOf("リスト1", "リスト2", "リスト3", "リスト4", "リスト5")

   val listView = findViewById<ListView>(R.id.list_view)
   val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, array)
   listView.adapter = adapter

   // OnItemClickListenerを実装
   listView.setOnItemClickListener { parent, view, position, id ->
       Toast.makeText(this, array[position], Toast.LENGTH_SHORT).show()
   }
}

OnItemClickListenerは、setOnItemClickListenerで実装することができます。

setOnItemClickListenerのブロック{}内には、項目がタップされた際に行いたい処理を実装します。

またブロック内では、
タップされたListView(サンプルコードのparent)、
タップされた項目のView(サンプルコードのview)、
タップされた項目の位置(サンプルコードのposition)、
タップされた項目のID(サンプルコードのid)
が取得できます。

Point
  • setOnItemClickListenerで、タップイベントを実装する

画像やボタン等を一覧表示させる方法

カスタムAdapterを使用した実装

先ほど紹介したサンプルコードは、テキストを一覧表示させるというものでした。しかし、実際にアプリを開発していると、画像やボタン、あるいはそれらを一まとめにした情報などを一覧表示させたいことがあるかもしれません。

ここからは、そんなより実用的なListViewの実装方法を紹介致します。

dataクラスの作成

紹介するにあたり、動物の画像、名前、年齢を一まとめにした情報を一覧表示させたいと思います。まずは、それらのdataクラスを作成します。

  • Animal.kt
data class Animal(
   val name: String,
   val age: Int,
   val imageId: Int
)

Animalというクラスを作成し、名前(name)、年齢(age)、画像(imageId)をパラメータを持つように実装しました。

カスタムAdapterの作成

続いて、カスタムのAdapterを作成します。

  • CustomAdapter.kt
  • item_list.xml
class CustomAdapter(context: Context, var mAnimalList: List<Animal>) : ArrayAdapter<Animal>(context, 0, mAnimalList) {

   private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

   override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
       // Animalの取得
       val animal = mAnimalList[position]

       // レイアウトの設定
       var view = convertView
       if (convertView == null) {
           view = layoutInflater.inflate(R.layout.list_item, parent, false)
       }

       // 各Viewの設定
       val imageView = view?.findViewById<ImageView>(R.id.image)
       imageView?.setImageResource(animal.imageId)

       val name = view?.findViewById<TextView>(R.id.name)
       name?.text = animal.name

       val age = view?.findViewById<TextView>(R.id.age)
       age?.text = "${animal.age} 才"

       return view!!
   }
}
<?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="match_parent"
   android:padding="12dp">

   <ImageView
       android:id="@+id/image"
       android:layout_width="60dp"
       android:layout_height="match_parent" />

   <LinearLayout
       android:layout_width="0dp"
       android:layout_height="wrap_content"
       android:layout_weight="1"
       android:orientation="vertical"
       android:layout_marginStart="16dp">

       <TextView
           android:id="@+id/name"
           android:textColor="#000000"
           android:textSize="18sp"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:layout_marginBottom="4dp" />

       <TextView
           android:id="@+id/age"
           android:textColor="#000000"
           android:textSize="18sp"
           android:layout_width="match_parent"
           android:layout_height="wrap_content" />

   </LinearLayout>

   <Button
       android:id="@+id/button"
       android:text="追加"
       android:layout_width="60dp"
       android:layout_height="wrap_content" />
</LinearLayout>

CustomAdapterは、ArrayAdapterを継承しております。コンストラクタでContentとListを受け取るようにし、ArrayAdapterのコンストラクタに渡すようにしております。ArrayAdapterの第二引数にのresourceに関しては0を渡しておきます。resourceはgetViewメソッド内で実装します。

CustomAdapterは、getViewメソッドをOverrideしております。getViewメソッドでは、各項目のレイアウトを設定します。

引数として渡ってくるpositionには、項目の位置が格納されております。このpositionを使用し、コンストラクタで受け取ったmAnimalListから、対象のデータを取得します。

続いて、引数として渡ってくるcontentViewがnullの場合、新たにレイアウトを設定しております。初回画面表示時は、contentViewがnullで渡ってきますが、再描画する際は、最初に設定したレイアウトが設定されたままなので、そのレイアウトを再利用するように実装しております。

その後は、list_item.xmlに実装した各Viewの設定を行っております。

ListViewにCustomAdapterを設定

最後に、MainActivityにてCustomAdapterを生成し、ListViewのAdapterとして設定します。

  • MainActivity.kt
class MainActivity : AppCompatActivity() {

   lateinit var mCustomAdapter: CustomAdapter
   lateinit var mAnimalList: ArrayList<Animal>

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)

       // データ一覧の実装
       val dog = Animal("イヌ", 3, R.drawable.dog)
       val cat = Animal("ネコ", 2, R.drawable.cat)
       val elephant = Animal("ゾウ", 10, R.drawable.elephant)
       val horse = Animal("ウマ", 4, R.drawable.horse)
       val lion = Animal("ライオン", 6, R.drawable.lion)
       mAnimalList = arrayListOf(dog, cat, elephant, horse, lion)

       val listView = findViewById<ListView>(R.id.list_view)

       // CustomAdapterの生成と設定
       mCustomAdapter = CustomAdapter(this, mAnimalList)
       listView.adapter = mCustomAdapter
   }
}

項目内の要素へのタップイベントの実装

続いて、項目内の要素に対し、タップイベントを実装する方法を紹介致します。
「1-2.項目へのタップイベントの実装」で、既にタップイベントを実装する方法を紹介しておりますが、その時に紹介したのは、項目全体へのタップイベントでした。ここで紹介するのは、項目内に実装した画像やボタンなどに、それぞれイベントを実装する方法です。

では、先ほど紹介した動物の情報を一覧表示するサンプルコードを改良していきます。以下は、追加ボタンをタップすると、タップされた項目が新たに追加されるサンプルコードです。

  • CustomAdapter.kt
  • MainActivity.kt
// interfaceの実装
interface CustomAdapterListener {
   fun clicked(animal: Animal)
}

class CustomAdapter(context: Context, var mAnimalList: List<Animal>, val listener: CustomAdapterListener) : ArrayAdapter<Animal>(context, 0, mAnimalList) {

   (省略)

   override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
       (省略)

       val button = view?.findViewById<Button>(R.id.button)
       button?.setOnClickListener {
           listener.clicked(animal)
       }

       return view!!
   }

   // リスト更新用の関数を実装
   fun updateAnimalList(animalList: List<Animal>) {
       mAnimalList = animalList
       // 再描画
       notifyDataSetChanged()
   }
}
// CustomAdapterListenerをimplementする
class MainActivity : AppCompatActivity(), CustomAdapterListener {

   (省略)

   override fun onCreate(savedInstanceState: Bundle?) {
       (省略)

       val listView = findViewById<ListView>(R.id.list_view)
       // MainActivity自身をListenerとして渡す
       mCustomAdapter = CustomAdapter(this, mAnimalList, this)
       listView.adapter = mCustomAdapter
   }

   // 追加ボタンがタップされたら呼ばれる
   override fun clicked(animal: Animal) {
       mAnimalList.add(animal)

       // CustomAdapterに実装したリスト更新用の関数を呼ぶ
       mCustomAdapter.updateAnimalList(mAnimalList)
   }
}

CustomAdapter.ktにて、CustomAdapterListenerを実装しております。MainActivityは、このCustomAdapterListenerをimplementし、コールバックメソッドであるclickedをOverrideしております。

CustomAdapterのgetVeiwでViewを実装する際に、Buttonがタップされると、listenerのclickedメソッドが呼ばれるように実装します。このlistenerは、コンストラクタで渡ってきたMainActivityにあたります。

こうすることで、Buttonタップされると、MainActivityにてOverrideしたclickedメソッドが呼ばれるようになります。

MainActivityのclickedメソッドでは、CustomAdapterのupdateAnimalListメソッドを呼ぶように実装しております。updateAnimalListメソッドでは、notifyDataSetChangedメソッドを呼ぶことで、ListViewの再描画を行っております。

まとめ

  • ListViewを実装するには、<ListView/>タグを使用する
  • ArrayAdapterの生成時に、項目のレイアウトと、項目一覧を指定する
  • ListView.adapterで、ListViewのAdapterを設定する
  • setOnItemClickListenerで、タップイベントを実装する