- 投稿日:2019-06-27T19:33:32+09:00
[Android] NavigationでSafeArgsを使って引数付き画面遷移をする
SafeArgsとは
みなさん,ActivityやFragment間の遷移で引数を受け渡しする時はどうしているでしょうか.
まだBundleを使っている方もいるかもしれませんね.Navigation Componentの1機能であるSafeArgsを使えば,型安全な引数の受け渡しが実現できます.みんな使っているからいっぱい記事があるかと思いきや,alphaやbetaの記事ばかりなのでstable版としてまとめておきたいと思います.
(あれから便利な拡張関数がサイレントで登場したりしている)Navigationの基本的な使い方は理解している前提から始めますので,微妙という方は以下の記事からキャッチアップしてきてください!
[Android] 10分で作る、Navigationによる画面遷移
https://qiita.com/tktktks10/items/7df56b4795d907a4cd31作るもの
最終的な実装は下記レポジトリに置いてあります.
tks10/safeargs-example
https://github.com/tks10/safeargs-example仕様はミニマムな感じでいきましょう.
MainActivityの上にFragmentを乗せて遷移をしていく,1Activtity多Fragmentなかんじです.
FirstFragmentにはEditTextとButtonがあり,ButtonをおしたらEditTextの内容とともにSecondFragmentに遷移してその内容を表示します.ではやっていきましょう.
事前準備
以下をgradleに追記.
project/build.gradlebuildscript { ... dependencies { ... classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0" } }app/build.gradleapply plugin: "androidx.navigation.safeargs" dependencies { ... implementation "android.arch.navigation:navigation-fragment:1.0.0" implementation "android.arch.navigation:navigation-ui:1.0.0" implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0" implementation "android.arch.navigation:navigation-ui-ktx:1.0.0" }初期状態
1から作っていては冗長なので,各Fragmentとnavigationグラフの初期配置が完了した時点から始めます.ちなみに現時点でnavigationグラフは下記のようになっており,FirstFragmentからSecondFragmentにただの遷移をするだけのactionが1つだけ定義されている状況です.
navigation_graph.xml<?xml version="1.0" encoding="utf-8"?> <navigation 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:id="@+id/navigation_graph" app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.tks10.safeargsexample.FirstFragment" android:label="fragment_first" tools:layout="@layout/fragment_first"> <action android:id="@+id/action_first_to_second" app:destination="@id/secondFragment"/> </fragment> <fragment android:id="@+id/secondFragment" android:name="com.tks10.safeargsexample.SecondFragment" android:label="fragment_second" tools:layout="@layout/fragment_second"> </fragment> </navigation>FirstFragmentではSecondFragmentに遷移する処理だけを記述した状態です.
FirstFragment.ktclass FirstFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_first, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.nextButton.setOnClickListener { findNavController().navigate(R.id.action_first_to_second) } } }また,FisrtFragmentとSecondFragmentのxmlは下記のようになっています.
fragment_fisrt.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" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatEditText android:id="@+id/contentEditText" android:layout_width="240dp" android:layout_height="wrap_content" android:gravity="center" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/nextButton" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintVertical_chainStyle="packed"/> <androidx.appcompat.widget.AppCompatButton android:id="@+id/nextButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@id/contentEditText" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>fragment_second.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" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/resultTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>当然この状態では引数の受け渡しは行なっていないので,遷移しても何も表示されません.
実装
それではこれを改造してsafeargsを使っていきましょう!
safeargsを使うにはnavigationグラフにargumentタグを追加することから始めます.この時argumentタグは,引数を受け取る側に追加します.下記のようにSecondFragment内にargumentタグを追加しましょう.この時指定するのは,変数名を示す
android:name
と型を示すandroid:argType
の2つです.navigation_graph.xml<?xml version="1.0" encoding="utf-8"?> <navigation 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:id="@+id/navigation_graph" app:startDestination="@id/firstFragment"> <fragment android:id="@+id/firstFragment" android:name="com.tks10.safeargsexample.FirstFragment" android:label="fragment_first" tools:layout="@layout/fragment_first"> <action android:id="@+id/action_first_to_second" app:destination="@id/secondFragment"/> </fragment> <fragment android:id="@+id/secondFragment" android:name="com.tks10.safeargsexample.SecondFragment" android:label="fragment_second" tools:layout="@layout/fragment_second"> <!-- ココに定義する --> <argument android:name="content" app:argType="string"/> </fragment> </navigation>なんとnavigationグラフの変更はこれで終わりです.
それでは次に行く・・・,前にRebuild Projectをしておきましょう.
現状DataBindingのようにリアルタイムにコードが生成されず,自動生成系のクラスが見つからん!などとハマります.さて,RebuildがおわったらFirstFragmentをいじっていきます.先ほどはただnavigateしていただけの部分を,以下のように書き換えてみましょう.
FirstFragment.ktoverride fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.nextButton.setOnClickListener { // EditTextの中身を取り出す val content = view.contentEditText.text.toString() // 生成されたクラスに引数を渡して遷移 val action = FirstFragmentDirections.actionFirstToSecond(content) findNavController().navigate(action) } }先ほどargumentタグをSecondFragment側に追加したことにより,SecondFragmentをDestinationとするactionのクラスとメソッドが自動生成されているはずです.今回だと,
FirstFragmentDirections#actionFirstToSecond
ですね.actionタグにつけたidから自動的にメソッド名をつけてくれます.このメソッドの引数に先ほど追加したargumentを受け取るようになっていることがわかります.これで実行時に名前が違って落ちた...なんてことがなくなりますね.さてここで満足して終わりそうですが,引数を受け取る側の処理が残っています.
SecondFragmentで引数を受け取ってみましょう.といっても,実は1行で終わります.それではSecondFragmentの頭の方に以下の1行を加えてみましょう.
SecondFragment.ktclass SecondFragment : Fragment() { private val args: SecondFragmentArgs by navArgs() .... }
navArgs()
というFragmentに対する拡張関数が気がついたら提供されており,
args.content
みたいなかんじで受け取った引数にアクセスできるようになっています.あとはこれをTextViewにセットしてあげるだけです.
SecondFragment.ktclass SecondFragment : Fragment() { private val args: SecondFragmentArgs by navArgs() .... override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.resultTextView.text = args.content } }お疲れ様です!
複数の引数を指定する
なるほどわかった,でも複数渡すことだって多いでしょ?
と突っ込んきた人のために書くこととしましょう.先ほど受け取った引数に加えてもう1つ値をEditTextに入力し,計2つの引数を次のFragmentに渡したいと思います.全く同じだとなんなので,整数にしましょう.下記のような感じです.
流れは先ほどと全く同じで,目的地であるThirdFragment側にargumentタグを追加します.何も考えずに並列に追加するだけで問題ありません.そして忘れずにRebuildをします.
あと,SecondFragmentからThirdFragmentへのaction定義も忘れないようにしましょう.
navigation_graph.xml... <fragment android:id="@+id/secondFragment" android:name="com.tks10.safeargsexample.SecondFragment" android:label="fragment_second" tools:layout="@layout/fragment_second"> <argument android:name="content" app:argType="string"/> <action android:id="@+id/action_second_to_third" app:destination="@id/thirdFragment"/> </fragment> <fragment android:id="@+id/thirdFragment" android:name="com.tks10.safeargsexample.ThirdFragment" android:label="fragment_third" tools:layout="@layout/fragment_third"> <argument android:name="content" app:argType="string"/> <argument android:name="value" app:argType="integer"/> </fragment>そうしたら,SecondFragmentを改造していきます.viewのxmlは以下のようになり,しれっとEditTextとButtonを追加しています.
fragment_second.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" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/resultTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:layout_marginBottom="120dp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toTopOf="@id/valueEditText" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintVertical_chainStyle="packed"/> <androidx.appcompat.widget.AppCompatEditText android:id="@+id/valueEditText" android:layout_width="240dp" android:layout_height="wrap_content" android:gravity="center" android:inputType="number" app:layout_constraintTop_toBottomOf="@id/resultTextView" app:layout_constraintBottom_toTopOf="@id/nextButton" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> <androidx.appcompat.widget.AppCompatButton android:id="@+id/nextButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" android:layout_marginTop="16dp" app:layout_constraintTop_toBottomOf="@id/valueEditText" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>さて,プログラムの方は以下のように改造してみましょう.
SecondFragment.ktoverride fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.resultTextView.text = args.content view.nextButton.setOnClickListener { val content = args.content val value = view.valueEditText.text.toString().toInt() // argumentを増やすと,このメソッドの引数も対応して増える val action = SecondFragmentDirections.actionSecondToThird(content, value) findNavController().navigate(action) } }シンプルですね.actionのメソッドが受け取る引数が素直に増えます.
ここにEditTextから取得した値を渡してあげましょう.そして忘れずに受け取る側も書きます.
xmlとプログラムは下記の通りです.ThirdFragment.ktclass ThirdFragment : Fragment() { private val args: ThirdFragmentArgs by navArgs() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_third, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.contentTextView.text = args.content view.valueTextView.text = args.value.toString() } }fragment_third.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" android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/contentTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/valueTextView" app:layout_constraintHorizontal_chainStyle="packed"/> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/valueTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="20sp" android:layout_marginStart="16dp" app:layout_constraintBaseline_toBaselineOf="@id/contentTextView" app:layout_constraintStart_toEndOf="@id/contentTextView" app:layout_constraintEnd_toEndOf="parent"/> </androidx.constraintlayout.widget.ConstraintLayout>お疲れ様です!
自作クラスを指定する
ん,じゃぁ自作で作ったdata classはどうするんだと.では書きましょう.
結論から言うと,Parcelable
を継承していればsafeargsを利用することができます.では今回は,下記のクラスをsafeargsで渡すことを考えてみましょう.
MyData.ktdata class MyData( val content: String, val value: Int, val message: String )完成予想図はこんな感じです.(messageには適当に文字列をいれています)
まずはじめに,このクラスにParcelableを実装しましょう,,,と言ってもめんどくさいので,
@Parcelize
アノテーションを使います.Experimentalをenableにしましょう.app/build.gradleandroid { ... androidExtensions { experimental = true } }MyDataに対して以下のようにアノテーションをつけます.
MyData.kt@Parcelize data class MyData( val content: String, val value: Int, val message: String ): Parcelableあとはこれまでの流れと同じになります.まずはnavigationグラフに追加しましょう.
navigation_graph.xml... <fragment android:id="@+id/thirdFragment" android:name="com.tks10.safeargsexample.ThirdFragment" android:label="fragment_third" tools:layout="@layout/fragment_third"> <argument android:name="content" app:argType="string"/> <argument android:name="value" app:argType="integer"/> <action android:id="@+id/action_third_to_fourth" app:destination="@id/fourthFragment"/> </fragment> <fragment android:id="@+id/fourthFragment" android:name="com.tks10.safeargsexample.FourthFragment" android:label="fragment_fourth" tools:layout="@layout/fragment_fourth"> <argument android:name="myData" app:argType="com.tks10.safeargsexample.MyData"/> </fragment>プリミティブなクラス(stringとかintegerとか)以外はフルパスでargTypeを指定してあげましょう.たとえばParcelableを実装しているBitmapとかも渡せたりします.
もうお察しでしょうが,ThirdFragmentでは以下のように記述します.
ThirdFragment.ktclass ThirdFragment : Fragment() { private val args: ThirdFragmentArgs by navArgs() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_third, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) view.contentTextView.text = args.content view.valueTextView.text = args.value.toString() view.nextButton.setOnClickListener { val myData = MyData( args.content, args.value, "LGTM!!" ) val action = ThirdFragmentDirections.actionThirdToFourth(myData) findNavController().navigate(action) } } }受け取るFourthFragmentは以下のようになります.
(xmlは省略しますが,messageを表示するTextViewが増えただけです)FourthFragment.ktclass FourthFragment : Fragment() { private val args: FourthFragmentArgs by navArgs() override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_fourth, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val myData = args.myData view.contentTextView.text = myData.content view.valueTextView.text = myData.value.toString() view.messageTextView.text = myData.message } }おわりに
以上,SafeArgsによる引数の受け渡しでした.複数の受け渡しや自作クラスの受け渡しもできるようになったかと思います.
これまでalphaやbetaでコロコロと記述が変わりましたが,ようやくstableになって固定された感があります.
型安全になるだけでもかなりメリットですし,navigationグラフをみたときに引数が一覧できるのも良いと思います.
ぜひ既存のプロジェクトでも部分的に取り入れていきたいところですね.
- 投稿日:2019-06-27T18:37:02+09:00
【Android】パパっと使えるAlertDialog一覧
はじめに
普段使いたい場面で使い方を忘れるAlertDialogを、パパっと使うための備忘録です。
@suzukihrさんが書かれている【コピペしてすぐ使えるアラートダイアログ集】を参考に、
さらに初心者向けに追記しKotlinで書き直したものとなります。※ActivityやFragmentなどに直接コピペして使用できますが、
画面回転時にメモリリークが発生する可能性があるため、
DialogFragmentを継承したクラスを作って使用することをおすすめします。通常ダイアログ(Yesのみ)
AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("タイトル") .setMessage("メッセージ") .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .show()通常ダイアログ(Yes・No)
AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("タイトル") .setMessage("メッセージ") .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .setPositiveButton("No", { dialog, which -> // TODO:Noが押された時の挙動 }) .show()通常ダイアログ(Yes・No・その他)
AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("タイトル") .setMessage("メッセージ") .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .setPositiveButton("No", { dialog, which -> // TODO:Noが押された時の挙動 }) .setNeutralButton("その他", { dialog, which -> // TODO:その他が押された時の挙動 }) .setIcon(R.mipmap.ic_launcher) .show()通常ダイアログ(アイコン付き)
AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("タイトル") .setMessage("メッセージ") .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .setPositiveButton("No", { dialog, which -> // TODO:Noが押された時の挙動 }) .setNeutralButton("その他", { dialog, which -> // TODO:その他が押された時の挙動 }) .show()リスト項目選択ダイアログ
リスト表示では setItems を使用します。
第1引数(CharSequence[]):リスト表示する文字配列
第2引数(OnClickListener):項目クリック時のクリックリスナーval strList = arrayOf("りんご","みかん","ぶどう") AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("リスト選択ダイアログ") .setItems(strList, { dialog, which -> // TODO:アイテム選択時の挙動 }) .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .show()ラジオボタン項目選択ダイアログ
ラジオボタン表示では setSingleChoiceItems を使用します。
第1引数(CharSequence[]):リスト表示する文字配列
第2引数(int):デフォルトでチェックを入れる位置
第3引数(OnClickListener):項目クリック時のクリックリスナーval strList = arrayOf("りんご","みかん","ぶどう") AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("ラジオボタン選択ダイアログ") .setSingleChoiceItems(strList, 0, { dialog, which -> // TODO:アイテム選択時の挙動 }) .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .show()チェックボックス選択ダイアログ
チェックボックス表示では setMultiChoiceItems を使用します。
第1引数(CharSequence[]):リスト表示する文字配列
第2引数(boolean[]):デフォルトでチェックを入れておく状態を管理する配列
第3引数(OnClickListener):項目クリック時のクリックリスナーval strList = arrayOf("りんご","みかん","ぶどう") val checkedItems = booleanArrayOf(true, false, false) AlertDialog.Builder(this) // FragmentではActivityを取得して生成 .setTitle("チェックボックス選択ダイアログ") .setMultiChoiceItems(strList, checkedItems, { dialog, which, isChecked -> // TODO:アイテム選択時の挙動 }) .setPositiveButton("OK", { dialog, which -> // TODO:Yesが押された時の挙動 }) .show()その他、設定関連
■setCancelable
ダイアログの表示範囲外をタップした際に、ダイアログが閉じれるかどうかを設定します。setCancelable(cancelable : Boolean)
■setView
自分で作ったレイアウトやViewを直接セットするときに使用します。setView(layoutResId : Int)
or
setView(view : View)
- 投稿日:2019-06-27T17:14:51+09:00
Kotlin + Retrofit でAPIコールしてみた
はじめに
二年目になりました。
kotlinでAndroid開発を勉強中です。
とりあえずAPIでとってきたデータを表示するところまでできたので、メモ書き程度に残します。やりたいこと
楽天ウェブサービスの楽天商品ランキングAPIで総合ランキングをとってきて、リストに表示する!
(本当は順位や画像、価格など表示したいのですが、一旦商品名だけ出します)
環境と使うもの
環境
- Mac OS Mojave v10.14.5
- Android Studio 3.4.1
使う物
- 楽天ウェブサービス(https://webservice.rakuten.co.jp/)
- Kotlin 1.3.31
- Retrofit 2.4.0
やってみよう
1.環境を整える
2.表示する場所をつくる
3.APIからの返り値を置く場所を作る
4.APIを呼ぶ環境を整える
- 楽天ウェブサービスに登録する
楽天ウェブサービスに登録し、APIをコールする際に必要になる
applicationId
を取得します。
取得の仕方はこちらを参考にさせていただきました。
→楽天(Rakuten Developers)のアプリIDとアフィリエイトIDを取得する方法
- retrofitを使えるように
build.gradle
に以下を追加するbuild.gradledef retrofitVersion = '2.4.0' implementation "com.squareup.retrofit2:retrofit:$retrofitVersion" implementation "com.squareup.retrofit2:adapter-rxjava:$retrofitVersion" implementation "com.squareup.retrofit2:converter-gson:$retrofitVersion"
- HTTP通信ができるように、
AndroidManifest.xml
に以下を追加するAndroidManifest.xml<uses-permission android:name="android.permission.INTERNET"/>manifestタグ内に入れましょう。
これがないとHTTP通信ができません!
- Activityを作成する
プロジェクトビューで左クリックし、
新規 > アクティビティー > 空のアクティビティー
と選択します。
アクティビティー名を入力し、
レイアウトファイルを生成する
にチェックをいれます。
レイアウト名が自動で入ります。(好きなように変更できます)
パッケージ名はご自身の環境にあるものをお使いください。
完了を押すと、以下の2つのファイルが生成されます。
- RakutenActivity.kt
- activity_rakuten.xml表示する場所を作る
先程作成した
activity_rakuten.xml
で、レイアウトを作成していきます。
GUIで作成することが可能ですが、今回は完成しているものをxmlで載せておきます。activity_rakuten.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".RakutenActivity"> <Button android:text="ランキングを表示する" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button" app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp" android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" app:layout_constraintHorizontal_bias="0.498" android:onClick="getRanking" android:layout_marginTop="16dp" app:layout_constraintTop_toTopOf="parent"/> <TextView android:layout_width="0dp" android:layout_height="36dp" android:id="@+id/titleRanking" android:textSize="18sp" android:gravity="center_horizontal|center_vertical" android:layout_marginStart="8dp" app:layout_constraintStart_toStartOf="parent" android:layout_marginLeft="8dp" android:layout_marginEnd="8dp" app:layout_constraintEnd_toEndOf="parent" android:layout_marginRight="8dp" app:layout_constraintHorizontal_bias="0.0" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/button"/> <ListView android:layout_width="395dp" android:layout_height="618dp" android:id="@+id/listRanking" app:layout_constraintEnd_toEndOf="parent" android:layout_marginEnd="8dp" android:layout_marginRight="8dp" android:layout_marginTop="8dp" app:layout_constraintTop_toBottomOf="@+id/titleRanking"/> </androidx.constraintlayout.widget.ConstraintLayout>
Button
を押すと、getRanking
イベントが起こり、
TextView
タグ内にランキングのタイトルを、
ListView
タグ内にランキング上位から商品の名前を詰めていきます。APIからの返り値を置く場所を作る
データクラスなるものを作ります。
JsonToKotlinClassというプラグインを使用しました。
以下の記事の通りに行いました。
kotlinでJSON扱うならJsonToKotlinClassが便利記事のとおりに行うと以下のようなクラスが出来上がります。
RakutenRankingResult.ktdata class RakutenRankingResult( val Items: List<Item>, val lastBuildDate: String, val title: String ) data class Item( val affiliateRate: String, val affiliateUrl: String, val asurakuArea: String, val asurakuClosingTime: String, val asurakuFlag: Int, val availability: Int, val carrier: Int, val catchcopy: String, val creditCardFlag: Int, val endTime: String, val genreId: String, val imageFlag: Int, val itemCaption: String, val itemCode: String, val itemName: String, val itemPrice: String, val itemUrl: String, val mediumImageUrls: List<String>, val pointRate: Int, val pointRateEndTime: String, val pointRateStartTime: String, val postageFlag: Int, val rank: Int, val reviewAverage: String, val reviewCount: Int, val shipOverseasArea: String, val shipOverseasFlag: Int, val shopCode: String, val shopName: String, val shopOfTheYearFlag: Int, val shopUrl: String, val smallImageUrls: List<String>, val startTime: String, val taxFlag: Int )APIを呼ぶ
前半で作成した
RakutenActivity.kt
に書き足していきます。interface
APIを表現、定義します。
@
の後に基本のURLに続くURLを書きます。
本来であればクエリパラメータとして渡すのが正しい方法のような気はしますが、本記事ではできるだけシンプルに記述したいので、べた書きしてます。
applicationId
は、最初に取得したご自身のIDを書いてください。[]
は不要ですRakutenActivity.ktprivate val itemInterface by lazy { createService() } interface ItemInterface { @GET("IchibaItem/Ranking/20170628?formatVersion=2&applicationId=[applicationId]") fun items(): retrofit2.Call<RakutenRankingResult> }HttpClient
基底URLの定義などを行います。
baseApiUrl
が基本のURLです。
/
まで入れるのが基本?と見たような気がします。ソースは不明です。申し訳ありません。RakutenActivity.ktfun createService(): ItemInterface { val baseApiUrl = "https://app.rakuten.co.jp/services/api/" val httpLogging = HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY) val httpClientBuilder = OkHttpClient.Builder().addInterceptor(httpLogging) val retrofit = Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(baseApiUrl) .client(httpClientBuilder.build()) .build() return retrofit.create(ItemInterface::class.java) }Retrofit instance
このserviceは、APIを定義したinterfaceをインスタンス化したものであり、実際にAPIコールを行う際に利用されます。
メソッド名
getRanking
は、レイアウトで作成したボタンのonClick
属性に入力します。
onFailure
がAPIコール失敗時に呼ばれる処理、onResponse
が成功時に呼ばれる処理です。
onFailure
には何も書いていません。エラーハンドリングしたい方はここに書きましょう。
title
にランキングのタイトルをつめて、titleRanking
というidのtextViewで表示しています。for文でレスポンスのItemsを回し、
itemName
をitems
につめていきます。
ArrayAdapterを使用し、items
につめたitemName
を、listRanking
に順番に表示します。RakutenActivity.ktfun getRanking(v: View){ itemInterface.items().enqueue(object : retrofit2.Callback<RakutenRankingResult> { override fun onFailure(call: retrofit2.Call<RakutenRankingResult>?, t: Throwable?) { } override fun onResponse(call: retrofit2.Call<RakutenRankingResult>?, response: retrofit2.Response<RakutenRankingResult>) { if (response.isSuccessful) { response.body()?.let { var items = mutableListOf<String>() var res = response.body()?.Items?.iterator() var title = response.body()!!.title titleRanking.text = "$title" if (res != null) { for (item in res) { items.add(item.itemName) } } val adapter = ArrayAdapter(this@RakutenActivity, android.R.layout.simple_list_item_1, items) val list: ListView = findViewById(R.id.listRanking) list.adapter = adapter } } } }) }以上で完成です。ランキングは出てきましたか?
さいごに
はじめてandroid開発を行っています。
java経験もほぼ皆無なので苦戦中です。
なにか間違っていることや、もっとスマートな書き方等あればお教えいただきたいです。参考