背景
数字入力に限定したEditText
(android:inputType="number"
)に対して、3桁ごとのセパレーターとsuffix
として単位をつけたい
理由
・ユーザーに極力数字以外のものを受け付けたくない
・桁数と単位でより入力内容をわかりやすくしたい
環境
MVVM-DataBinding
※導入方法は省略
実装
今回は長さ(m)を例に実装します
やりたい事は、もしユーザーが1222
と入力したとすると、1,222m
とEditText
に表示させたいです
XML
<data>
<import type="your.package.name.Converter" />
<variable
name="viewModel"
type="your.package.name.SnippetViewModel" />
</data>
(略)
<EditText
android:id="@+id/length_m"
android:layout_width="match_parent"
android:gravity="end|center_vertical"
android:hint="0m"
android:inputType="number"
android:maxLines="1"
android:paddingStart="0dp"
android:paddingEnd="8dp"
android:text="@={Converter.addNumberSeparatorAndUnitMeter(viewModel.length)}"
android:textSize="18sp" />
・ViewModel
をbinding
のvariable
として登録
・EditText
の内容を後述するMutableLiveData
と対応させたいので双方向データバインディングしています
・Converter
のimport
と使用、こちらも後述
ViewModel
class SnippetViewModel() : ViewModel() {
val length = MutableLiveData<Int>()
(略)
ここでは対象のMutableLiveData
を定義しています
Converter
ここではEditText
上でユーザーが数字が入力するものの、EditText
には数字+セパレーター+単位
を表示したいわけです
ただし、今回は双方向データバインディングのため、またユーザーが数字を入力する際に、EditText
に表示されている数字+セパレーター+単位
をそのままgetText
等で使い数字のまま使えるわけではないので不整合が生じやすいです
そのため、
・EditTextへの入力内容(Int) -> EditTextの表示内容(String)
・EditTextの表示内容(String) -> EditTextの入力内容(Int)
これらをうまく変換するためのメソッドが必要になります
ここで役立つのが、@InverseMethodです
@InverseMethod
では上記の変換、逆変換をスマートにこなしてくれます
今回の例でいうとこのようになります
object Converter {
@InverseMethod("inverseToInt")
@JvmStatic
fun addNumberSeparatorAndUnitMeter(value: Int): String {
val intValueWithFormat = String.format("%,d", value)
return "${intValueWithFormat}m"
}
@JvmStatic
fun inverseToInt(value: String?): Int {
return kotlin.runCatching { value?.replace("m", "")?.replace(",", "")?.toInt() }
.getOrNull() ?: 0
}
}
ひとつめのaddNumberSeparatorAndUnitMeter
ではユーザーが入力した内容=LiveDataの値
に対してセパレーターとmの単位を付与しています
@InverseMethod("inverseToInt")
のinverseToInt
は逆変換するのに必要なメソッド名になります
これにより再度ユーザーが数字を入力した際、逆変換のメソッドが呼ばれIntとなり、そのIntの値を使ってまたaddNumberSeparatorAndUnitMeter
が呼ばれるということになります
inverseToInt
メソッドでは数字+セパレーター+単位をIntに戻しています
これでユーザーが数字を入力するだけでEditText
にセパレーターと単位を補助入力できるようになりました🎉
InverseMethodを使うと何が嬉しいの?
個人的な意見です
・パターンに応じた複雑なロジックを自前で定義する手間が減る
・変換、逆変換の処理を無限ループしにくい状態で定義できる
・もとのLiveData
の値を変換していないままの型(今回はInt
)として使うことができるので、その値を使ったバリデーションや値変換(Transformations.map
)がしやすい
おわり
以上です 参考になれば幸いです
よりよい方法がありましたら是非ご教授ください🙏