Kotlinでアンドロイドアプリを作成しています。
その中でこの記事でまとめた、APIを使いたいと思い実装してました。
僕はアプリ開発の素人ですので、本来の実装方法は分かりませんが
とりあえず、ネットワークに繋いで処理する系は非同期処理でしょ!ということで
AsyncTaskで実装してました。
ですが、文字列が渡せない。
流れとしてはsetOnclicklistnerでボタンを押したときのEditTextの文字列をAsyncTaskに渡してたんですが、上手く挙動しません。
KotlinでAsyncTaskは非推奨ということもあり、諦めて別の方法を利用します。
それがタイトルの通りCouroutine(コルーチン)です。
こちらの記事を参考にさせていただき、実装しました。
APIの使い方は今回はパスします!
retrofit2でそこらへんは実装しているので、感じ取って読んでください。
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.*
import android.view.inputmethod.InputMethodManager
import android.widget.*
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.list_item.view.*
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.Retrofit
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import kotlinx.coroutines.experimental.Deferred
import kotlinx.coroutines.experimental.android.UI
import kotlinx.coroutines.experimental.async
class MainActivity : Activity() {
var job: Deferred<Unit>? = null
var foodText: EditText? = null//入力テキスト
var input_food:String? = null//foodTextの文字列
var result_count:Int? = 0//APIの検索結果のリスト長
var eat_foodLists :MutableList<CharSequence> = mutableListOf()//食事リスト
var foodLists :MutableList<Items> = mutableListOf()//APIの検索結果を表示するリスト
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
foodText = findViewById(R.id.edit_text) as EditText
inputButton.setOnClickListener {View ->
//まずソフトキーボードを非表示にする
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(View.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS)
if (foodText!!.text != null) {
// 取得したテキストを TextView に張り付ける
input_food = foodText!!.text.toString(
// async関数の戻り(Deferred型)を受け取る
job = async {
// myInputメソッドの呼び出し
MyInput(input_food!!)
}
}
}
foodListView.setOnItemClickListener { adapterView, view, position, id ->//検索結果リストのタップイベント
val food = view.findViewById<TextView>(R.id.foodTextView).text
eat_foodLists.add(food)
Log.d("TAG",eat_foodLists.toString())
}
}
data class FoodData(val food_name: String, val calory: String)//listviewのカスタムのためのデータクラス
data class ViewHolder(val foodTextView: TextView, val caloryTextView: TextView)
var foods :MutableList<FoodData> = mutableListOf()//
class FoodListAdapter(context: Context, foods: List<FoodData>) : ArrayAdapter<FoodData>(context, 0, foods) {//独自アダプタのクラス
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?.foodTextView!!,
view.caloryTextView
)
view.tag = holder
} else {
holder = view.tag as ViewHolder
}
val foods = getItem(position) as FoodData
holder.foodTextView.text = foods.food_name
holder.caloryTextView.text = foods.calory
return view
}
}
private fun MyInput(input:String) {
var retro = Retrofit.Builder()
.baseUrl("https://apex.oracle.com/")
.addConverterFactory(GsonConverterFactory.create())
.build()
var service = retro.create(API_interface::class.java)
var nutrition = service.API(input)
nutrition.enqueue(object : Callback<OracleAPIData> {
@SuppressLint("SetTextI18n")
override fun onResponse(call: Call<OracleAPIData?>, response: Response<OracleAPIData?>) {
val api_body = response?.body()
if(api_body != null) {
result_count = api_body!!.items.count()
if(result_count == 0){Toast.makeText(applicationContext, "検索した食事名は見つかりませんでした", Toast.LENGTH_LONG).show()}
for (i in 0..(result_count!!-1)){ foodLists.add(api_body!!.items[i]) }
for (i in 0..(result_count!!-1)){
val tmp = FoodData(api_body!!.items[i].food_name, api_body!!.items[i].calories.toString())
foods.add(tmp)
}
//onProgressと同じ処理(UIの変更が可能)
async(UI) {
val foodListView = findViewById(R.id.foodListView) as ListView
//var adapter = ArrayAdapter<Items>(this@MainActivity, android.R.layout.simple_list_item_1, foodLists)
val adapter = FoodListAdapter(this@MainActivity, foods)
foodListView.adapter = adapter
}
Thread.sleep(500)
}else{Toast.makeText(applicationContext, "食事名を入力してください", Toast.LENGTH_LONG).show()}
}
override fun onFailure(call: Call<OracleAPIData>, t: Throwable) {
Log.d("Tag","failed!!")
}
})
// onPostExecuteメソッドと同等の処理
async() {
foodLists.clear()
foods.clear()
}
}
}
今回の注目点は
var job: Deferred<Unit>? = null
// async関数の戻り(Deferred型)を受け取る
job = async {
// myInputメソッドの呼び出し
MyInput(input_food!!)
}
private fun MyInput(input:String) {
//onProgressと同じ処理(UIの変更が可能)
async(UI) {
・・・
}
// onPostExecuteメソッドと同等の処理
async() {
・・・
}
}
まずjob変数を作って、非同期処理を呼び出します。
その中のメソッドでは非同期処理が行われます。
非同期処理中にUIを変更したい場合はasync(UI){}
したくないときはasync(){}を使いましょう。
おおまかな流れはこれだけです。
非常に分かりやすくて、可読性も高いですね。
みなさんもぜひ使ってみてください。
Sponsored Link