【2022年】LegacyだけどListViewにソース側からデータを渡して表示する（半ソース依存）


# 前置き

ソース内で用意したアイテムのListをView側に用意したListViewに渡して表示するだけの
簡単な処理を作ります

# 作る物

下記のListViewを表示するだけのActivityを作ります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740385/dece4cab-f8fb-40db-df6a-0fac2450e2c9.png)

人選は、個人的にホットな奴らを採用してます
別にちびまる子ちゃんの登場人物とかでもいいです

# なんでLegacy

なんでか知らんけど、分類がLegacyになってた

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740385/45bfcbdd-0ca9-a873-3d2e-881a1d0720d0.png)

Legacyって書いてあるから、今後の追加機能とかは望めないけど
消したら影響受けるアプリも多そうだし、残り続けはするんじゃないかなと予想

軽く調べた限りでは、移行先候補として「RecyclerView」があるらしい

https://developer.android.com/guide/topics/ui/layout/recyclerview?hl=ja

使った事ないので、説明だけで判断すると
「表示領域外に移動したアイテムを再利用する」とか
ゲームとかで大量のアイテムをスクロールメニューに表示したいときに使えそうだね
まぁ、ゲーム作るならAndroid直に触るよりもUnityとかUnrealEngine使うわって話になるけど

# プロジェクト作成

例によって「Empty Activity」でプロジェクト作成

パッケージ名とプロジェクト名はなんでもいいが、筆者は
パッケージ名：com.arrayadapter.arrayadaptertest
プロジェクト名：ArrayAdapterTest

にした

これから作る各ファイルのファイル構成は以下となる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740385/4561cdbc-10f2-0ccc-f193-1b4d23fe5f42.png)

test_list_item_header.xmlは最後のほうで語る「おまけ」で使うので
今の所は無いものとして扱ってください

# build.gradle(app)

Android セクションに下記を追加

~~~ build.gradle(app)
    buildFeatures {
        dataBinding = true
    }
~~~

# main_activity.xml

HelloWorld消して、空っぽのListViewを置くだけ
ソース側で引っ張ってこれるようにIDも付与する

~~~ main_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

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

</androidx.constraintlayout.widget.ConstraintLayout>
~~~

# test_list_item.xml

ListViewに表示する１行分のアイテムの
見た目やバインディング情報を定義する

~~~ test_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="li"
            type="com.arrayadapter.arrayadaptertest.TestListItem" />
    </data>

    <LinearLayout
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
        <TextView
            android:id="@+id/name"
            android:layout_width="70dp"
            android:layout_height="wrap_content"
            android:text="@{li.name}"/>

        <TextView
            android:id="@+id/gender"
            android:layout_width="70dp"
            android:layout_height="wrap_content"
            android:text="@{li.gender}"/>

        <TextView
            android:id="@+id/age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{Integer.toString(li.age)}"/>
    </LinearLayout>

</layout>
~~~

後述で作る「TestListItem」の名前を、このxml内部で「li」と命名し
セットした３つのTextViewそれぞれにバインディングするだけのxml

最初のrootを「layout」で宣言したため、ビルドすれば
com.arrayadapter.arayadaptertest.databinding パッケージが自動生成され
そこに「TestListItemBinding」クラスが自動生成される

# TestListItem.kt

ListViewにぶち込むアイテム１個分の情報

~~~ TestListItem.kt
package com.arrayadapter.arrayadaptertest

class TestListItem (val name : String , val gender : String , val age : Int ){

}
~~~

余談だが、これだけで、Javaで言う、下みたいな事を書いた事になるらしい

~~~ example.java

public class TestListArray {

     public string name;
     public string gender;
     public string age;

     public TestListArray ( string _name , string _gender , int _age )
     {
          name = _name;
          gender = _gender;
          age = _age;
     }
}
~~~

いやぁ、便利っすねぇ～　Kotlin

# TestListAdapter.kt

こいつが重要

View側のListViewとソースを結び付ける中継役と思えばいい
たぶんもっといろんなことができそうな気がする

~~~ TestListAdapter.kt
package com.arrayadapter.arrayadaptertest

import android.content.Context
import android.view.LayoutInflater
import android.widget.ArrayAdapter
import android.view.View
import android.view.ViewGroup
import com.arrayadapter.arrayadaptertest.databinding.TestListItemBinding

class TestListAdapter(context: Context, objects: List<TestListItem>) :
    ArrayAdapter<TestListItem>(context, 0, objects)  {

    private lateinit var iLayoutInflater : LayoutInflater

    override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
        iLayoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

        // test_list_item.xmlのバインディングデータ取得
        var binding = TestListItemBinding.inflate(iLayoutInflater)

        // test_list_item.xmlのli変数とbindさせる
        val li : TestListItem = getItem(position)!!
        binding.li = li

        return binding.root
    }

}
~~~

重要なのはgetView関数の実装

TestListAdapterの生成時にListが渡されて、すでにListを持っているため、
getView関数が実行されたときに、引数で渡されたpositionが示すlistのTestListItemを取得し
それを「test_list_item.xml」内部で定義した「li」とバインドさせてバインディングを行う

# MainActivity.kt

まずはソース全文

~~~ MainActivity.kt
package com.arrayadapter.arrayadaptertest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ListView;


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

        // listviewの参照を取得
        var listView : ListView = findViewById(R.id.list_view) as ListView

        // 仮データ作成
        var list : MutableList<TestListItem> = mutableListOf(
            TestListItem("田所浩二" , "ホモ" , 24 ),
            TestListItem("長谷川亮太" , "男" , 810 ),
            TestListItem("唐澤貴洋" , "男" , 1000000 ),
            TestListItem("岩間好一" , "男" , 931 ),
        )

        // リストを自動的に構築するための必要な処理や情報をセット
        var adapter : TestListAdapter = TestListAdapter(this,list)
        listView.adapter = adapter

    }
}
~~~

//仮データ作成　の所は、お好みの男児や美少女とかを好きなだけ追加していい

重要なのは

~~~ MainActivity.kt
        // リストを自動的に構築するための必要な処理や情報をセット
        var adapter : TestListAdapter = TestListAdapter(this,list)
        listView.adapter = adapter
~~~

この部分で、用意したlistを元にAdapterを生成し
listViewが持つAdapterと関連付ける
これによって、listViewの内容がソース内で設定したList通りに表示される

# ビルドしてみよう

成功すれば、愉快な仲間たちがList形式で表示されたAcitivityが起動するはず

# 課題

ソース部分にいろいろごちゃごちゃ書きすぎてるので
ＸＭＬの記述でうまく吸収できないか

# 参考

https://qiita.com/ksugawara61/items/2d63f0be279a94b74550

https://akira-watson.com/android/kotlin/listview-simple.html

https://stackoverflow.com/questions/53582679/get-all-items-of-a-custom-arrayadapter-in-kotlin


# おまけ

### ヘッダーをつけてみよう

リスト自体は表示できたけど、項目名が表示されてなくて、なんかイケてないので
ヘッダーをつけてみる事にする

ヘッダー表示用の「test_list_item_header.xml」を用意する

~~~ test_list_item_header.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <TextView
            android:id="@+id/name"
            android:layout_width="70dp"
            android:layout_height="wrap_content"
            android:text="Name"
            android:background="#000000"
            android:textColor="#FFFFFF"/>

        <TextView
            android:id="@+id/gender"
            android:layout_width="70dp"
            android:layout_height="wrap_content"
            android:text="Gender"
            android:background="#000000"
            android:textColor="#FFFFFF"/>

        <TextView
            android:id="@+id/age"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Age"
            android:background="#000000"
            android:textColor="#FFFFFF"/>
    </LinearLayout>

</layout>

~~~

ヘッダーと項目アイテムが別個になってるのが確認できるように
ヘッダーの背景色を黒、文字色を白に適当に設定


MainActivity.ktに３行追加する

~~~ MainActivity.kt
package com.arrayadapter.arrayadaptertest

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ListView;
import com.arrayadapter.arrayadaptertest.databinding.TestListItemHeaderBinding //追加


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

        // listviewの参照を取得
        var listView : ListView = findViewById(R.id.list_view) as ListView

        // ヘッダーをセットする
        val testListItemHeaderBinding = TestListItemHeaderBinding.inflate(layoutInflater) //追加
        listView.addHeaderView(testListItemHeaderBinding.root) // 追加

        // 仮データ作成
        var list : MutableList<TestListItem> = mutableListOf(
            TestListItem("田所浩二" , "ホモ" , 24 ),
            TestListItem("長谷川亮太" , "男" , 810 ),
            TestListItem("唐澤貴洋" , "男" , 1000000 ),
            TestListItem("岩間好一" , "男" , 931 ),
        )

        // リストを自動的に構築するための必要な処理や情報をセット
        var adapter : TestListAdapter = TestListAdapter(this,list)
        listView.adapter = adapter

    }
}
~~~

もっと奇麗なやり方がありそうだが、手っ取り早く思いついた方法はこれ

### ビルドしてみよう

成功すれば、以下のように表示されるはず

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2740385/037f1e88-2042-4848-4bab-2bd06bfade8d.png)

### 参考

https://qiita.com/ehuthon-kd/items/bd3ba7364c9b9dbc3f7c