はじめに
今回はMVP(アーキテクチャ)に沿って、RemoteRepository設定(API通信のやり取り)を実装していきたいと思います。
本記事は「【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)」の内容を引き継いで実装しています。
これまでの内容は
【kotlin】超簡単なMVPを実装してみた、①Presenterの実装
【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)
をご参照ください
実装内容
APIは「楽天ブックス雑誌検索API」を使用する。
EditTextに文字を入力してボタンを押したら、APIから本を検索し、タイトル、画像、値段を表示する。
実装
[準備]
「楽天ブックス雑誌検索API」
から、アプリIDを獲得する。
[レイアウト]
①editText
②button
③textView
④bookTitleTextView
⑤bookImageView
⑥bookPriceTextView
[コード]
・RemoteRepository設定
①androidmanifestにを追加する。
<uses-permission android:name="android.permission.INTERNET"/>
②Retrofit設定
※詳しい説明は割愛させていただきます。
data class BooksEntity(
var Items: ArrayList<ItemsData>?
)
data class ItemsData(
var Item:ItemData?
)
data class ItemData(
//商品タイトル
var title: String ="",
//値段
var itemPrice: String ="",
//画像URL
var largeImageUrl: String ="",
)
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
interface ApiService {
@GET("20170404?applicationId=(獲得した楽天BooksアプリID)")
fun getBooks(
@Query("format") format:String,
@Query("keyword") keyword:String,
@Query("booksGenreId") booksGenreId:String,
@Query("sort") sort:String,
): Call<BooksEntity>
}
class ApiClientManager {
//Retrofitクラスのインスタンス化
val retrofit = Retrofit.Builder()
//ベースURLを入力
.baseUrl("https://app.rakuten.co.jp/services/api/BooksTotal/Search/")
//レスポンスを変換する設定を追加
.addConverterFactory(GsonConverterFactory.create())
//Httpクライアント結合
.client(getOkHttpClient())
//onResponseバックグラウンド処理
.callbackExecutor(Executors.newSingleThreadExecutor())
.build()
// サービスクラスの実装オブジェクト取得(インターフェイスを利用する)
val apiClient:ApiService = retrofit.create(ApiService::class.java)
private fun getOkHttpClient(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
.readTimeout(30,TimeUnit.SECONDS)
.writeTimeout(30,TimeUnit.SECONDS)
.build()
return httpClient
}
}
③RemoteRepositoryを作成
※RemoteRepositoryの役割はPresenterに橋渡し、Presenterで使用するメソッドを定義する。
④RemoteRepositoryで使用するメソッドを追加する。
interface RemoteRepository {
//④本を検索しデータを持ってくる。
fun getBooks(word: String, callback: Callback<BooksEntity>)
}
⑤ApiClientManager戻り値にRemoteRepositoryを追加する。
⑥RemoteRepositoryで設定したメソッドが出るので設定。
//⑤戻り値にRemoteRepositoryを追加する。
class ApiClientManager: RemoteRepository {
val retrofit = Retrofit.Builder()
.baseUrl("https://app.rakuten.co.jp/services/api/BooksTotal/Search/")
.addConverterFactory(GsonConverterFactory.create())
.client(getOkHttpClient())
.callbackExecutor(Executors.newSingleThreadExecutor())
.build()
val apiClient:ApiService = retrofit.create(ApiService::class.java)
private fun getOkHttpClient(): OkHttpClient {
val httpClient = OkHttpClient.Builder()
.readTimeout(30,TimeUnit.SECONDS)
.writeTimeout(30,TimeUnit.SECONDS)
.build()
return httpClient
}
//⑥本を検索するメソッド
override fun getBooks(word: String, callback: Callback<BooksEntity>) {
apiClient.getBooks(format = "json", keyword = word, booksGenreId = "000", sort = "standard").enqueue(callback)
}
}
・Presenter設定
①Presenterの引数にRemoteRepositoryを追加。
//①Presenterの引数にRemoteRepositoryを追加。
class MainPresenter(private val view: MainContract.View, private val localRepository: LocalRepository, private val remoteRepository: RemoteRepository): MainContract.Presenter {
init {
view.presenter = this
}
override fun start() {
val text =localRepository.loadText()
view.showTextView(text)
}
override fun onClickButton(text: String) {
localRepository.saveText(text)
view.showTextView(text)
}
}
・Contract設定
①UIが増えたのでview側で使用するメソッドを作成。
interface MainContract {
interface View: BaseView<Presenter> {
fun showTextView (text: String)
//①bookTitleTextViewに本のタイトルを表示させるメソッド
fun showBookTitleTextView (text: String)
//①bookPriceTextViewに値段を表示させるメソッド
fun showBookPriceTextView (text: String)
//①bookImageViewに画像を表示させるメソッド
fun showBookImageView (bitmap: Bitmap)
}
interface Presenter: BasePresenter {
fun onClickButton(text: String)
}
}
・View設定
①追加したUIを獲得。
②Contractで追加したメソッドが出てくるので、設定。
③Presenterに引数にApiClientManager()を追加。
class MainFragment: Fragment(),MainContract.View {
override lateinit var presenter: MainContract.Presenter
lateinit var editText :EditText
lateinit var button :Button
lateinit var textView :TextView
//①追加したUIを獲得。
lateinit var bookTitleTextView :TextView
lateinit var bookImageView :ImageView
lateinit var bookPriceTextView :TextView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_main, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
editText = view.findViewById<EditText>(R.id.editTextText)
button = view.findViewById<Button>(R.id.button)
textView = view.findViewById<TextView>(R.id.textView)
//①追加したUIを獲得。
bookTitleTextView = view.findViewById<TextView>(R.id.bookTitleTextView)
bookImageView = view.findViewById<ImageView>(R.id.bookImageView)
bookPriceTextView = view.findViewById<TextView>(R.id.bookPriceTextView)
//③Presenterに引数にApiClientManager()を追加。
initPresenter()
presenter.start()
button.setOnClickListener{
presenter.onClickButton(editText.text.toString())
}
}
override fun showTextView(text: String) {
textView.text = text
}
//②bookTitleTextViewに本のタイトルを表示する。
override fun showBookTitleTextView(text: String) {
bookTitleTextView.text = text
}
//②bookPriceTextViewに本の値段を表示する。
override fun showBookPriceTextView(text: String) {
bookPriceTextView.text = text
}
//②bookImageViewに本の画像を表示させる。
override fun showBookImageView(bitmap: Bitmap) {
activity?.runOnUiThread {
bookImageView.setImageBitmap(bitmap)
}
}
fun initPresenter() {
if (::presenter.isInitialized) {
return
}
context?.let {
presenter = MainPresenter(this, PreferenceManager(it),ApiClientManager())
}
}
}
・Presenter設定
①LocalRepositoryのメソッドを呼び出す。
②通信が成功した場合、view側に反映させる。
class MainPresenter(private val view: MainContract.View, private val localRepository: LocalRepository, private val remoteRepository: RemoteRepository): MainContract.Presenter {
init {
view.presenter = this
}
override fun start() {
val text =localRepository.loadText()
view.showTextView(text)
}
override fun onClickButton(text: String) {
localRepository.saveText(text)
view.showTextView(text)
//①LocalRepositoryのgetBooksメソッドを呼び出す。
remoteRepository.getBooks(text, object: Callback<BooksEntity>{
override fun onResponse(call: Call<BooksEntity>, response: Response<BooksEntity>) {
if(response.isSuccessful){
//②通信が成功した場合showBookTitleTextViewを使用し、View側に本のタイトルを反映させる。
response.body()?.Items?.get(0)?.Item?.title?.let {
view.showBookTitleTextView(it)
}
//②通信が成功した場合showBookImageViewを使用し、View側に画像を反映させる。
response.body()?.Items?.get(0)?.Item?.largeImageUrl?.let {
val url = URL(it)
val streem = url.openStream()
val bitmap = BitmapFactory.decodeStream(streem)
view.showBookImageView(bitmap)
}
//②通信が成功した場合showBookPriceTextViewを使用し、View側に値段を反映させる。
response.body()?.Items?.get(0)?.Item?.itemPrice?.let {
view.showBookPriceTextView(it+"円")
}
} else {
}
}
override fun onFailure(call: Call<BooksEntity>, t: Throwable) {
}
})
}
}
以上、RemoteRepositoryの実装でした。
関連記事
【kotlin】超簡単なMVPを実装してみた、①Presenterの実装
【kotlin】超簡単なMVPを実装してみた、②LocalRepository設定(ローカルデータに保存、読み込み)