Kotlin アプリ開発 vol.1-5 InputFilterを使う

RM計算機

前回の課題をおさらい

前回解決しようとした課題

  • ウエイトに0以下の値が入力できてしまう。
  • rep数に0以下の値が入力できてしまう。

計算ボタンを押したときにエラーメッセージを出すようにしたが、-5kgという数字にはできてしまう。使えなくはないがユーザーフレンドリーではない。

既存アプリのベンチマーク

アプリ側で入力できる数値を制限しているのでユーザーに不必要な動作をさせない

(rep数が0になるような動作は受け付けない)

InputFilterを使う

Inputfilterなるものを使えばよいらしい

How to set input type and format in EditText using kotlin?
I want to force users to enter a Number and i'm using that (and it's working fine): android:inputType="number" I want to force the user to make the input just...

(原文)

I want to force the user to make the input just two numbers (Int or Double does not matter) and those numbers must be between 0 and 20 (or 0.0 and 20.0) : e.g : 0 or 0.0 1 or 1.5 (1.0 etc) . . . 20 or 20.0

(和訳)

IntかDoubleしか打てないようにしたくて、かつ 0~20の値しか打てないようにしたいんだけど!ねえ!教えて偉い人!

(原文)

Here’s how I like to do this:

(和訳)

ま、こんな感じかな(ドヤ

class InputFilterMinMax(min:Float, max:Float): InputFilter {
    private var min:Float = 0.0F
    private var max:Float = 0.0F

    init{
        this.min = min
        this.max = max
    }

    override fun filter(source:CharSequence, start:Int, end:Int, dest: Spanned, dstart:Int, dend:Int): CharSequence? {
        try
        {
            val input = (dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length)).toFloat()
            if (isInRange(min, max, input))
                return null
        }
        catch (nfe:NumberFormatException) {}
        return ""
    }

    private fun isInRange(a:Float, b:Float, c:Float):Boolean {
        return if (b > a) c in a..b else c in b..a
    }
}
ざっくり解読する
  • InputFilterMinMaxというclassをつくる
  • min/maxという変数で上下限を指定する
  • inputされた値が min と maxの間にあるときはスルー
  • そうでないときは ” ” (空欄)で返す。

細かな問題点を修正する

指定範囲から外れた場合を ” ” にした場合

まずは初期設定のまま動作を試してみよう。 (範囲は0~20)

[Kotlin] Inputfilter 失敗例①

ユーザーが入力できる範囲は0~20に限定できた。ボタン操作によって0~20の範囲外となった場合、 空欄となり、空欄時のボタン操作によってアプリが強制終了する。

指定範囲から外れた場合を “0.0” にした場合

マイナス側を規制するために0.0にしてみる。(範囲は0~20)

[Kotlin] Inputfilter 失敗例②

マイナス側は0.0となった後の動きを規制できた。文字入力で空欄にしてしまうと自動的に0.0に戻ってしまう。20を超えた場合は0.0に戻ってしまいループする。

inputfilter内に条件を追加してみる

minより小さいときは0.0を返し、maxより大きいときは20.0を返すようにしてみた。

        try
        {
            val input = (dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length)).toFloat()
            if (isInRange(min, max, input))
                return null
            if (min > input)
                return "0.0"
            if (max < input)
                return "20.0"
        }
        catch (nfe:NumberFormatException) {}
        return ""
[Kotlin] Inputfilter 失敗例③

マイナスボタンとプラスボタンの動きは規制できた。文字を消去もできるが、50と打とうとすると input > maxという条件にひっかかり、520となってしまう。

問題点を解決したアプリが完成

やらないといけないことは以下

・inputfilterは初期のままで触らない

・ボタン操作のアクションに条件の判定を入れる

空欄をDoubleに変換しないようにしよう!

正しく動作したコード

       var isValid = true
            val get_weight = findViewById<EditText>(R.id.e_weight)
            val st_weight = get_weight.text.toString()

            if (st_weight.isEmpty()) {
                isValid = false
            }
            else if (st_weight.toDouble() < 2.5) {
                isValid = false
            }

            else if (isValid) {
                val countdown = st_weight.toDouble() - 2.5
                val countdownn = countdown.toString()
                get_weight.setText(countdownn, TextView.BufferType.EDITABLE)
            }

悪いコード例①

            var isValid = true
            val get_weight = findViewById<EditText>(R.id.e_weight)
            val st_weight = get_weight.text.toString()
            val tt_weight = st_weight.toDouble()

            if (st_weight.isEmpty()) {
                isValid = false
            }
            if (tt_weight < 2.5) {
                isValid = false
            }

            else if (isValid) {
                val countdown = st_weight.toDouble() - 2.5
                val countdownn = countdown.toString()
                get_weight.setText(countdownn, TextView.BufferType.EDITABLE)
            }

テキストが空欄だった場合、 4行目のtoDouble()の段階でアプリが落ちる

悪いコード例②

            var isValid = true
            val get_weight = findViewById<EditText>(R.id.e_weight)
            val st_weight = get_weight.text.toString()

            if (st_weight.isEmpty()) {
                isValid = false
            }
            if (st_weight.toDouble() < 2.5) {
                isValid = false
            }

            if (isValid) {
                val countdown = st_weight.toDouble() - 2.5
                val countdownn = countdown.toString()
                get_weight.setText(countdownn, TextView.BufferType.EDITABLE)
            }

else if にしなかった場合は、最初の条件が空欄でも toDoubleを実行してしまいアプリが落ちる。

コード全文を紹介

コードの全文を見る
package com.example.plus_minus_test

import android.os.Bundle
import android.text.InputFilter
import android.text.Spanned
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class InputFilterMinMax(min:Float, max:Float): InputFilter {
    private var min:Float = 0.0F
    private var max:Float = 0.0F

    init{
        this.min = min
        this.max = max
    }

    override fun filter(source:CharSequence, start:Int, end:Int, dest: Spanned, dstart:Int, dend:Int): CharSequence? {

        try
        {
            val input = (dest.subSequence(0, dstart).toString() + source + dest.subSequence(dend, dest.length)).toFloat()
            if (isInRange(min, max, input))
                return null
        }
        catch (nfe:NumberFormatException) {}
        return ""
    }

    private fun isInRange(a:Float, b:Float, c:Float):Boolean {
        return if (b > a) c in a..b else c in b..a
    }

}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val get_weight = findViewById<EditText>(R.id.e_weight)
        val syoki = 10.0
        val syokichi = syoki.toString()
        get_weight.setFilters(arrayOf<InputFilter>(InputFilterMinMax(0.0F, 20.0F)))
        get_weight.setText(syokichi, TextView.BufferType.NORMAL)

        val button1 = findViewById<Button>(R.id.b_minus)
        button1.setOnClickListener {

            var isValid = true
            val get_weight = findViewById<EditText>(R.id.e_weight)
            val st_weight = get_weight.text.toString()

            if (st_weight.isEmpty()) {
                isValid = false
            }
            else if (st_weight.toDouble() < 2.5) {
                isValid = false
            }

            else if (isValid) {
                val countdown = st_weight.toDouble() - 2.5
                val countdownn = countdown.toString()
                get_weight.setText(countdownn, TextView.BufferType.EDITABLE)
            }

            val button2 = findViewById<Button>(R.id.b_plus)
            button2.setOnClickListener {

                var isValid = true
                val get_weight = findViewById<EditText>(R.id.e_weight)
                val st_weight = get_weight.text.toString()
                if (st_weight.isEmpty()) {
                    isValid = false
                }
                else if (st_weight.toDouble() > 17.5) {
                    isValid = false
                }

                else if (isValid) {
                    val countup = st_weight.toDouble() + 2.5
                    val countupp = countup.toString()
                    get_weight.setText(countupp, TextView.BufferType.NORMAL)
                }
            }
        }
    }
}

まとめ

InputFilterの使い方を学んだ
else if の使い方を学んだ
ユーザーフレンドリーなボタン操作を実現した

コメント

タイトルとURLをコピーしました