qnote Advent Calendar 2016 2日目を担当いたします。
チャン・ジュン(日本人)です。
よろしくどうぞ
今回は、今年Android界隈で話題になったことを...
ということでKotlinでAndroidアプリ書くとこんな感じというのを紹介していこうと思います。
ちなみに、どんなアプリで紹介するかと言いますと、昨年のAdvent Calendarで書いたGoogle画像検索を利用して画像をグリッド表示するだけのアプリです。
→記事はこちら
何はともあれ
さて、Kotlinのプロジェクトを作るのはとても簡単です。
ただしKotlinのプラグインは必須なのでInstallしておいてください。
- まずは普通に新しいプロジェクトを作りましょう。
- 最初に作るアクティビティは何でも良いです。
- プロジェクトができたら
Tools>Kotlin>Configure Kotlin in Project
でプロジェクトでKotlinが使えるようにしましょう。 - Kotlinが使えるようになったら
Code>Convert Java File to Kotlin File
でJavaファイルをKotlinファイルにコンバートしてもらいます。
おめでとうございます!
Kotlinで書かれたAndroidアプリができました!
google画像検索を利用して画像をグリッド表示してみる
それでは本題の実装部分ですが、今回もまた通信関係はVolleyに任せちゃいます。
事前準備として
manifestに<uses-permission android:name="android.permission.INTERNET"/>
build.gradleのdependenciesにcompile 'com.android.volley:volley:1.0.0'
を追記してください。
高階関数+ラムダでかなりスッキリ書ける
まずはリクエストを作ります。
Google画像検索でhtmlファイルを取得して画像URLを抽出するのでStringRequestを使います。
なお、KotlinファイルとJavaファイルが混在する環境を再現したかったのでJavaで書いています。
public class ImageRequest extends StringRequest {
private Map<String, String> mParams;
private static final String URL = "http://www.google.co.jp/search";
public ImageRequest(Response.Listener<String> listener, Response.ErrorListener errorListener, Map<String, String> mParams) {
super(Method.GET, URL, listener, errorListener);
this.mParams = mParams;
}
@Override
public String getUrl() {
return super.getUrl() + makeParameter();
}
private String makeParameter() {
String param = "?tbm=isch";
for (Map.Entry<String, String> entry : mParams.entrySet()) {
param += "&" + entry.getKey() + "=" + entry.getValue();
}
return param;
}
}
ということでMainActivity.ktからの呼び出しがこちら
fun requestImage() {
val params = createParameter()
val request = ImageRequest(
Response.Listener {
//抽出とアダプターのセット
},
Response.ErrorListener {
//リクエストに失敗
}, params)
queue.add(request)
}
paramsにはキーワードとoffsetが必要になると思います。
Javaで書かれたクラスもKotlinで書かれたクラスもKotlinではインスタンスの生成にnew
は不要です。
また引数に関数を取ることができ(高階関数)、ラムダを渡せばかなり簡略化して書くことができます。
今回使用しているListenerは引数が1つのメソッド1つを実装するインターフェースなので、下記のようにメソッドの引数の記述を省略してit
で表すことができます。
Response.ErrorListener {
Toast.makeText(this@MainActivity, it.message, Toast.LENGTH_SHORT).show()
}
lateinit修飾子で初期化を先延ばしできる
MainActivity全体はこんな感じです。
Kotlinでは基本的にnullが許容されていないのでval queue: RequestQueue
と書いただけでは赤線が引かれます。
そこでlateinit
修飾子をつけることで初期化を先延ばしにできるのですが、初期化しなくてもランタイムまでエラーが出ないので取り扱いには要注意です。
class MainActivity : AppCompatActivity() {
lateinit var queue: RequestQueue
val imageUrlList = ArrayList<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar = findViewById(R.id.toolbar) as Toolbar
setSupportActionBar(toolbar)
val fab = findViewById(R.id.fab) as FloatingActionButton
fab.setOnClickListener { view -> Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", null).show() }
queue = Volley.newRequestQueue(applicationContext)
requestImage()
}
fun requestImage() {
val params = createParameter()
val request = ImageRequest(
Response.Listener {
val regex = "<img.+?src=\"(.+?)\".+?>"
val pattern = Pattern.compile(regex)
val matcher = pattern.matcher(it)
while (matcher.find()) {
imageUrlList.add(matcher.group(1))
}
if (imageUrlList.size < 100) {
requestImage()
}else {
val recyclerView = findViewById(R.id.image_grid) as RecyclerView
val layoutManager = GridLayoutManager(this@MainActivity, 4)
recyclerView.layoutManager = layoutManager
recyclerView.adapter = GridAdapter(this@MainActivity)
}
},
Response.ErrorListener {
Toast.makeText(this@MainActivity, it.message, Toast.LENGTH_SHORT).show()
}, params)
queue.add(request)
}
fun createParameter(): HashMap<String, String> {
val params = HashMap<String, String>()
params.put("q", "cat")
if (imageUrlList.size > 0 && imageUrlList.size < 100) {
params.put("start", imageUrlList.size.toString())
}
return params
}
}
Kotlinファイルのクラスが持っているグローバル変数をJavaで参照するには...
それではMainActivityのコードを踏まえてGridAdapterを見てみましょう。
public class GridAdapter extends RecyclerView.Adapter<GridViewHolder> {
private ScrollingActivity mActivity;
public GridAdapter(ScrollingActivity activity) {
mActivity = activity;
}
@Override
public GridViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
return new GridViewHolder(new NetworkImageView(mActivity));
}
@Override
public void onBindViewHolder(GridViewHolder holder, int position) {
holder.getImageView().setImageUrl(mActivity.getImageUrlList().get(position), new ImageLoader(mActivity.getQueue(), new GridImageCache()));
}
@Override
public int getItemCount() {
return mActivity.getImageUrlList().size();
}
}
正直言って良いコードではありませんが...
さておき、ポイントはmActivity.getImageUrlList()
とmActivity.getQueue()
です。
MainActivityにはgetterの実装などありませんが自動的にgetterが生成されています。
逆にJavaでgetterを作っている変数についてはrecyclerView.adapter
のように取得できますし、セットする場合にはrecyclerView.adapter = 〜
で代入をするような記述で済みます。
※GridImageCache、GridViewHolderクラスはテンプレート通りな感じですので割愛いたします。
まとめ
勉強会に参加するとよく耳にしたKotlinという言語ですが、実際の業務で全てをKotlinで書いているという話はまだ聞きません。
一部コードでKotlinを利用することに対してリスクを考えている人に、混在する状態でも意識することなく実装を進めることができるということが伝われば良いなと思います。
最後に
その2を書くかどうかはまだ未定...