シンプルなActivity + ViewModel + CustomViewのTemplate例

アプリ作成が中盤に差し掛かると、新規画面を作る際に以前作ったファイルと似たようなファイルをいっぱい作らないといけないとかなりがちですよね。そんなときはTemplateを作って右クリック一発で必要なファイルを作れるようにすると便利です。

# ファイル構成

これはActivity + ViewModel + CustomViewの構成のTemplateの例です。追加するpackageを右クリックしてテンプレートを追加する動作で動きます。

```
.
├── globals.xml.ftl
├── recipe.xml.ftl
├── root
│   ├── res
│   │   └── layout
│   │       ├── activity.xml.ftl
│   │       └── view.xml.ftl
│   └── src
│       └── app_package
│           ├── Activity.kt.ftl
│           ├── ActivityModule.kt.ftl
│           ├── View.kt.ftl
│           └── ViewModel.kt.ftl
└── template.xml
```

# ファイル中身

## template.xml

作成時に入力される変数を記載します。この例ではprefixが入力される想定になります。

```xml:template.xml
<?xml version="1.0"?>
<template
    revision="1"
    format="5"
    name="Activity with ViewModel"
    description="Templates for building a new Activity with">

    <parameter
        id="className"
        name="Class Name"
        constraints="unique|nonempty"
        default="Main"
        type="string" />

    <parameter
        id="resName"
        name="Resource Name"
        constraints="unique|nonempty"
        default="main"
        type="string" />


    <globals file="globals.xml.ftl" />
    <execute file="recipe.xml.ftl" />

</template>
```

## globals.xml.ftl

全体で使用される変数を記載します。今回は出力先のフォルダのみを持っています。

```xml:globals.xml.ftl
<?xml version="1.0"?>
<globals>
    <global id="resOut" value="${resDir}" />
    <global id="srcOut" value="${srcDir}/${slashedPackageName(packageName)}" />
</globals>
```

## recipe.xml.ftl

`globals.xml.ftl` で定義した変数と、 `template.xml` で入力された値を使って、具体的に吐き出すファイルを記載します。 

```xml
<?xml version="1.0"?>
<recipe>

    <instantiate from="root/src/app_package/Activity.kt.ftl"
        to="${escapeXmlAttribute(srcOut)}/${className}Activity.kt" />
    <instantiate from="root/src/app_package/ActivityModule.kt.ftl"
        to="${escapeXmlAttribute(srcOut)}/${className}ActivityModule.kt" />
    <instantiate from="root/src/app_package/ViewModel.kt.ftl"
        to="${escapeXmlAttribute(srcOut)}/${className}ViewModel.kt" />
    <instantiate from="root/src/app_package/View.kt.ftl"
        to="${escapeXmlAttribute(srcOut)}/${className}View.kt" />

    <instantiate from="root/res/layout/activity.xml.ftl"
        to="${escapeXmlAttribute(resOut)}/layout/${resourceName}_activity.xml" />
    <instantiate from="root/res/layout/view.xml.ftl"
        to="${escapeXmlAttribute(resOut)}/layout/${resourceName}_view.xml" />

</recipe>
```

## 実装ファイル

アプリによって変わってきますが共通化できる部分を全て記載します。一般的には決まりきった初期化処理を記載することになると思います。 `ftl` では `globals` や `template` で定義された変数を使うことができます。

```kotlin:activity.kt.ftl
package ${escapeKotlinIdentifiers(packageName)}

import android.os.Bundle
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModel
import com.choilabo.todo.R
import com.choilabo.todo.databinding.${className}ActivityBinding
import com.choilabo.todo.di.ViewModelFactory
import com.choilabo.todo.di.ViewModelKey
import com.choilabo.todo.di.get
import dagger.Binds
import dagger.android.support.DaggerAppCompatActivity
import dagger.multibindings.IntoMap
import javax.inject.Inject

class ${className}Activity : DaggerAppCompatActivity() {

    @Inject
    lateinit var viewModelFactory: ViewModelFactory

    private val viewModel by lazy {
        viewModelFactory.get<${className}ViewModel>(this)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = DataBindingUtil.setContentView<XxxActivityBinding>(
            this,
            R.layout.xxx_activity
        ).also {
            it.lifecycleOwner = this
            it.viewModel = viewModel
        }

        setSupportActionBar(binding.toolbar)
    }

    @dagger.Module
    interface Module {

        @Binds
        @IntoMap
        @ViewModelKey(${className}ViewModel::class)
        fun binds${className}ViewModel(viewModel: ${className}ViewModel): ViewModel
    }
}
```


# まとめ

画面を作るごとにいちいちファイルを作るのは時間の無駄ですし、単純なTypo等のミスが発生しやすくなるので、適宜テンプレートを作って更新し続けると効率が良いと思います。
Github上にプロジェクトごと乗せているので興味がある人はご確認ください。

https://github.com/satohu20xx/android-application-template/tree/master/template/ActivityWithViewModel


# 参考

https://qiita.com/mkosuke/items/c29069003f6d6c0c5b3c