前回までは Android が標準で用意しているレイアウトを使いましたが、
今回はオジリナルなレイアウトを使った ListView の作り方について見ていきたいと思います。
完成版はこのような形になります。
[peg-image src=”https://lh3.googleusercontent.com/-7KcKY8VfVdQ/W2unDOSoDaI/AAAAAAAAbFs/v_-n_0YkdZoPlW1BZrYE7nikUI2roP-3gCCoYBhgL/s144-o/kotlin_customlist_10.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6587542553777147298″ caption=”” type=”image” alt=”kotlin_customlist_10.png” image_size=”687×1181″ peg_single_image_size=”w400″ ]
Twitter のようなレイアウトを作っていきます。
クリックすると名前が表示されるようになっています。
目次
開発環境
プロジェクトを作ろう
画面を作ろう
画像ファイルを用意しよう
リストビューに表示するレイアウトを作ろう
リストビューに表示するデータを作ろう
リストビューにデータを表示しよう
リストをクリックしてみよう
いつも通りプロジェクト一式はgithub にありますので、そちらも合わせて参考にしてみてください。
開発環境
Android Studio 3.1.1
JDK 1.8
Kotlin 1.2
macOS(Windows でも基本的には同じように開発できると思います。)
プロジェクトを作ろう
では早速プロジェクトを作っていきましょう。
プロジェクトの作成方法は[Kotlin de Android] 最初はやっぱりHelloWorld を参考にしてみてください。
各画面での設定は以下の通りです。
プロジェクトの名前は「CustomListView2」
「Include Kotlin support」にチェックを入れるのをお忘れなく。
Activityの種類は「Empty Activity」
Activityの名前は「MainActibity」
画面を作ろう
プロジェクトが作れたらまずは画面を作っていきましょう。
作り方は以前と同じなので、こちら の手順と同様に作成してください。
ListView の ID も myListView
としておいてください。
画像ファイルを用意しよう
今回は画像とテキストを2つ使ったオリジナルのレイアウトを作っていくので、表示する画像を用意しておきましょう。
適当なサイズ(1000*1000pxくらいにしてあります)の正方形の画像を5枚用意しました。
ご自身で用意してもらっていいですし、GitHubからダウンロードして使っていただいてもOKです。
用意した画像を Finder 等ですべて選択してコピーします。
Android Studio に戻って、ファイル一覧から app > res > drawable を開いてください。
[peg-image src=”https://lh3.googleusercontent.com/-H5mLhUSFtQk/W2k_IvMek8I/AAAAAAAAbE4/tkbaMieKomgm2om2w-Y_Qgj6XAJ0hEJCACCoYBhgL/s144-o/kotlin_customlist_03.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6586865349346366402″ caption=”” type=”image” alt=”kotlin_customlist_03.png” image_size=”572×482″ peg_single_image_size=”w300″ ]
drawable フォルダを選択した状態で貼り付けます。
そうすると、下のようなダイアログが出るので、drawable のほうを選択してOKボタンをクリックします。
[peg-image src=”https://lh3.googleusercontent.com/-R1auZ3Gj2mE/W2k_IrwxuvI/AAAAAAAAbE4/bKQrFKMfxLsU6DE2VtQYk1CYcKSdfXFewCCoYBhgL/s144-o/kotlin_customlist_04.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6586865348424874738″ caption=”” type=”image” alt=”kotlin_customlist_04.png” image_size=”830×856″ peg_single_image_size=”w400″ ]
すると、drawable フォルダに画像がコピーされたかと思いますので、これでOKです。
[peg-image src=”https://lh3.googleusercontent.com/-CB-JY3qN3_0/W2k_IpHSR_I/AAAAAAAAbE4/P24fe0QZe_4elHlfKtpc2Pm1tuxeH3nkQCCoYBhgL/s144-o/kotlin_customlist_05.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6586865347713976306″ caption=”” type=”image” alt=”kotlin_customlist_05.png” image_size=”576×676″ peg_single_image_size=”w300″ ]
これで画像の準備は完了です。
リストビューに表示するレイアウトを作ろう
オリジナルなレイアウトを作成するために Layout ファイルをまずは作ってあげます。
app > res > layout フォルダを選択して、右クリックをします。
New > Layout resource file を選択します。
[peg-image src=”https://lh3.googleusercontent.com/-lqpT9-2BIEY/W2k_IvduSSI/AAAAAAAAbE4/WSR_731Gnr4IQ_y2WxEWDj3o9WKn8Dl-ACCoYBhgL/s144-o/kotlin_customlist_01.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6586865349418699042″ caption=”” type=”image” alt=”kotlin_customlist_01.png” image_size=”1518×1658″ ]
レイアウト作成ダイアログが表示されるので、下記のように設定してOKボタンをクリックします。
File name: list_item
Root element: android.support.constraint.ConstraintLayout
Source set: main
Directory name: layout
[peg-image src=”https://lh3.googleusercontent.com/-lOnkw0BAsIU/W2k_Igjp-ZI/AAAAAAAAbE4/N6t1zGk0vQwh5MiNXJHuojRtRf2z4mKFwCCoYBhgL/s144-o/kotlin_customlist_02.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6586865345417050514″ caption=”” type=”image” alt=”kotlin_customlist_02.png” image_size=”1648×946″ ]
では作った list_item.xml を開いてレイアウトを作っていきましょう。
まず、ImageView を左側に配置します。このときなにかしらの画像を選択するように言われるので、先程用意した画像を選択してください。
この状態のままだと ImageView が大きすぎるので、layout_width と layout_height を 72dp にしておきます。
ID は flowerImgView にします
TextView を2つ、ImageViewの右側に配置します。Twitter のようなレイアウトになります。
1つ目の TextView は以下のように設定します。
ID: nameTextView
textStyle: Bold (B を選択します)
textSize: 18dp
2つ目の TextView は以下のように設定します。
ID: descTextView
ざっと設定してから位置を調整していきます。
このあたりは文字で説明すると大変なので、動画にしてあるのでそちらを見てみてください。
リストビューに表示するデータを作ろう
次にリストに表示するデータを作っていきましょう。
今回はお花の写真をその名前、説明を表示していくので、それぞれ List を作っていきます。
MainActivity.kt を開いてください。
onCreate() の中に以下のように書いていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
val names = listOf( "あじさい", "蓮", "ネモフィラ", "バラ", "ふじ") val descriptions = listOf( "アジサイ(紫陽花、学名 Hydrangea macrophylla)は、アジサイ科アジサイ属の落葉低木の一種である。広義には「アジサイ」の名はアジサイ属植物の一部の総称でもある。", "ハス(蓮、学名:Nelumbo nucifera)は、インド原産のハス科多年性水生植物。", "ネモフィラはムラサキ科ネモフィラ属(Nemophila)に分類される植物の総称。または、ルリカラクサ(瑠璃唐草、学名:Nemophila menziesii)のこと。", "バラ(薔薇)は、バラ科バラ属の総称である。あるいは、そのうち特に園芸種(園芸バラ・栽培バラ)を総称する。ここでは、後者の園芸バラ・栽培バラを扱うこととする。 バラ属の成形は、灌木、低木、または木本性のつる植物で、葉や茎に棘を持つものが多い。", "フジ(藤、学名: Wisteria floribunda)は、マメ科フジ属のつる性落葉木本。一般名称としての藤には、つるが右巻き(上から見て時計回り)と左巻きの二種類がある。") val images = listOf( R.drawable.hydrangea, R.drawable.lotus, R.drawable.nemophila, R.drawable.rose, R.drawable.wisteria) |
names は String の List でお花の名前の一覧ですね。
descriptions も String の List でお花の説明の一覧です。
images は Int の List で画像の ID の一覧です。
それぞれ、順番通りになるようにしておいてください。
次にこれらをまとめて扱うために Data Class を作っていきます。
今回は MainActivity クラスのなかに定義していきますが、別のファイルにしてもOKです。
以下のように書いてあげます。
|
data class FlowerData(val name: String, val desc: String, val imageId: Int) |
では、この FlowerData クラスを使って List を作ります。
|
val flowers = List(names.size) { i -> FlowerData(names[i], descriptions[i], images[i])} |
List の生成と初期化が1行でできちゃうので、Kotlin って便利。
(Kotlinのバージョンアップに伴うものなのか、前回から少しだけ書き方が変わっています。)
これでデータは完成です。
リストビューにデータを表示しよう
次は実際にリストビューに表示するところを作っていくのですが、今までは ArrayAdapter
や SimpleAdapter
といった Android 標準のアダプタを使ってきましたが、今回のような複雑なレイアウトになってくると独自にアダプタを作成する必要があります。
すべて MainActivity クラス内に書いていきます。
ViewHolder を作ろう
まずは ViewHolder
というクラスを作っていきます。
あとで詳しく見ていきますが、リストはすべての行数分のレイアウトを生成するわけではなく、画面に表示されている分だけ生成して、リソースを使い回すという処理をしています。
そのために必要となってくるのが ViewHolder
というものになります。
このクラスにレイアウトの部品を保持しておいて、その中身だけ入れ替える(TextView ならテキストを替える)というような動作になります。
ViewHolder
はデータを保持しておくだけなので、データクラスで以下のように作成してあげます。
|
data class ViewHolder(val nameTextView: TextView, val descTextView: TextView, val flowerImgView: ImageView) |
今回は独自レイアウト内には3つの部品が配置されているので、このように3つプロパティを用意しました。
配置した部品の分だけ用意してくださいね。
独自アダプタを作ろう
ではいよいよ独自アダプタを作成しましょう。
いろいろな作り方がありますが、今回は ArrayAdapter
を継承して独自アダプタを作っていきます。
クラスの定義は以下のようになります。
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { } |
FlowerListAdapter
のコンストラクタ引数は コンテキストと先ほど作ったお花のデータのリストになります。
親クラスのコンストラクタ引数にもそのまま渡してあげます。親クラスの第二引数はレイアウトのIDを渡すのですが、あとで設定していくので、ここでは 0 としています。
では次に、あとで必要となってくるプロパティを先に用意しておきましょう。
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater } |
レイアウトファイルを取得するために必要となる LayoutInflater を取得しておきます。
レイアウトを生成するたびに取得してもいいのですが、何度もやる処理なのでクラスプロパティとして持っておきます。
さて、次は実際にレイアウトにデータを反映していく部分を作っていきます。
getView
メソッドをオーバーライドしてあげればOKです。
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { } } |
引数の position
は行数、convertView
はレイアウトのView、parent
は親ビューといった感じです。
まずは必要な変数を用意しておきます。
このメソッドは View
を返すのですが、convertView
が null でなければそのまま使い、null だったらレイアウトを生成して返します。
Kotlin では引数は不変な変数なので書き換えができないため、別の変数に代入して使っていきます。(5行目)
それから先程作った ViewHolder
も必要となっておくので変数を定義しておきます。(6行目)
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { var view = convertView var holder: ViewHolder } } |
次に、view が null だった場合に、レイアウトと ViewHolder を生成していきます。
ViewHolder は view の tag に保持しておくことで、レイアウトの使い回しをしています。
view が null 以外だった場合には、view の tag に ViewHolder が格納されているはずなのでそれを使ってあげます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { var view = convertView var holder: ViewHolder if (view == null) { view = layoutInflater.inflate(R.layout.list_item, parent, false) holder = ViewHolder( view?.nameTextView!!, view.descTextView, view.flowerImgView ) view.tag = holder } else { holder = view.tag as ViewHolder } } } |
view
と ViewHolder
の生成ができたら、 ViewHolder
のプロパティにデータを反映させて、最後に 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
|
class FlowerListAdapter(context: Context, flowers: List<FlowerData>) : ArrayAdapter<FlowerData>(context, 0, flowers) { private val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { var view = convertView var holder: ViewHolder if (view == null) { view = layoutInflater.inflate(R.layout.list_item, parent, false) holder = ViewHolder( view?.nameTextView!!, view.descTextView, view.flowerImgView ) view.tag = holder } else { holder = view.tag as ViewHolder } val flower = getItem(position) as FlowerData holder.nameTextView.text = flower.name holder.descTextView.text = flower.desc holder.flowerImgView.setImageBitmap(BitmapFactory.decodeResource(context.resources, flower.imageId)) return view } } |
ImageView のところですが、FlowerData
では画像のIDを保持しているだけなので、 BitmapFactory.decodeResource
を使って BitMap に変換して上げる必要があります。
最初からBitMapにして持っていくというやり方でもOKだと思いますが、このあたりは好みの問題ですかね。
個人的にはBitMapのような大きなデータをずっと保持しておくのも気持ち悪いのでIDで保持するようにしています。
そんなこんなで、これでアダプターは完成です。
データを表示しよう
ではいよいよ、データの表示ですね。
といってもここは今までと同じようにすればOKです。
onCreate
メソッドに以下のように追記します。
独自アダプタを生成して、myListView にセットしてあげるだけですね。
|
val adapter = FlowerListAdapter(this, flowers) myListView.adapter = adapter |
これで表示されるはずなので、エミュレーターで見てみましょう。
[peg-image src=”https://lh3.googleusercontent.com/-Ohlsipze6jY/W2unDBBHY5I/AAAAAAAAbFk/WyfteDz7HHIym0uT48fq9Yad1va39rziQCCoYBhgL/s144-o/kotlin_customlist_07.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6587542550214042514″ caption=”” type=”image” alt=”kotlin_customlist_07.png” image_size=”688×1224″ peg_single_image_size=”w300″ ]
あ、、、なんか各行の下のマージンがないのが微妙ですね(汗)
直しましょう。
list_item.xml を開きます。
ConstraintLayout を選択して、すべての Attributes を表示して、Padding の Botton を16dp にします。
[peg-image src=”https://lh3.googleusercontent.com/–mtBF4ngjxE/W2unDI8DrAI/AAAAAAAAbFk/XsuajUGFfiItpF7GmDRR85X6HprykvrIACCoYBhgL/s144-o/kotlin_customlist_08.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6587542552340311042″ caption=”” type=”image” alt=”kotlin_customlist_08.png” image_size=”1746×834″ peg_single_image_size=”w600″ ]
これでいいと思うので、もう一度エミュレーターで確認してみましょう。
[peg-image src=”https://lh3.googleusercontent.com/–X3HiBzSljw/W2unDHY9lHI/AAAAAAAAbFs/khEDL2aN_g8HrDncFWtO0n82AfMEQRwDgCCoYBhgL/s144-o/kotlin_customlist_09.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6587542551924675698″ caption=”” type=”image” alt=”kotlin_customlist_09.png” image_size=”684×1226″ peg_single_image_size=”w300″ ]
今度は良さそうですね!
無事、独自レイアウトが表示されるようになりました。
リストをクリックしてみよう
最後のクリック処理をさっと見ていきます。前回までとやり方は同じです。
onCreate
メソッドの最後に次のように追記するだけです。
|
myListView.setOnItemClickListener { adapterView, view, position, id -> val name = view.findViewById<TextView>(R.id.nameTextView).text Toast.makeText(this, "clicked: $name", Toast.LENGTH_LONG).show() } |
全て書いてしまいましたが、今まで通り、setOnItemClickListener
に処理を書いてあげればOKです。
今回はお花の名前だけ表示するようしています。
では、エミュレータで確認していきましょう。
[peg-image src=”https://lh3.googleusercontent.com/-7KcKY8VfVdQ/W2unDOSoDaI/AAAAAAAAbFs/v_-n_0YkdZoPlW1BZrYE7nikUI2roP-3gCCoYBhgL/s144-o/kotlin_customlist_10.png” href=”https://picasaweb.google.com/104967861399298501788/6530100651433058145#6587542553777147298″ caption=”” type=”image” alt=”kotlin_customlist_10.png” image_size=”687×1181″ peg_single_image_size=”w300″ ]
クリックして Toast が表示されればOKです!
前回よりは少し難しくなりましたが、独自のレイアウトを使った ListView の作り方について見ていきました。
最初は独自アダプタを作ったりするのが難しく感じるかもしれませんが、慣れるとそこまで難しくも感じなくなるかな、と。
いろいろなレイアウトの ListView をつくってみてください!