LoginSignup
1
0

More than 1 year has passed since last update.

Custom View 探求記(TextView 継承編 その3)

Last updated at Posted at 2020-07-19

前回のあらずじ

前回は、custom view の情報をレイアウトファイルから設定できるようにし、その振る舞いをレイアウトエディタ上から確認しました。

前回の不満点

  • 画面回転などにより値がリセットされてしまう。

今回の課題

  • custom view に状態保存機能を実装する。

関連する関数

現状確認

▼ SHOW CURRENT TIME ボタン押下直後
ボタン押下時の現在時刻が表示されている。

▼ 画面回転直後
セットされた時刻がリセットされてしまっている。

変更実装

◆ build.gradle

API 23 で DateTimeFormatter を利用したかったので、 desugaring を利用するために以下を追加しました。

android {
    compileOptions {
        coreLibraryDesugaringEnabled true
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}
dependencies {
    coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.9'
}

◆ Custom View クラス

UnixEpochTextView.kt
package com.objectfanatics.chrono10.ex_cv

import android.content.Context
import android.os.Parcelable
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.objectfanatics.chrono10.R
import com.objectfanatics.infra.android.view.ThisInstanceStateBase
import com.objectfanatics.infra.android.view.getLongAttr
import com.objectfanatics.infra.android.view.restoreThisInstanceStateAndGetSuperInstanceState
import kotlinx.android.parcel.Parcelize
import java.time.Instant
import java.time.ZoneId
import java.time.format.DateTimeFormatter
import java.util.*

class UnixEpochTextView : AppCompatTextView {
    constructor(context: Context?) : super(context) {
        initAttrs(null)
    }

    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
        initAttrs(attrs)
    }

    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initAttrs(attrs)
    }

    private fun initAttrs(attrs: AttributeSet?) {
        getLongAttr(attrs, R.styleable.UnixEpochTextView, R.styleable.UnixEpochTextView_unixEpoch, 0, this::unixEpoch.setter)
    }

    var unixEpoch: Long
        get() = Instant.from(unixEpochFormatter.parse(text)).toEpochMilli()
        set(unixEpoch) {
            text = unixEpoch.unixEpochString
        }

    companion object {
        private val unixEpochFormatter =
            DateTimeFormatter.ofPattern("yyyy-MM-dd\nHH:mm:ss.SSS", Locale.US).withZone(ZoneId.of("Asia/Tokyo"))

        private val Long.unixEpochString: String
            get() = unixEpochFormatter.format(Instant.ofEpochMilli(this))
    }

    override fun onSaveInstanceState(): Parcelable =
        ThisInstanceInstanceState(super.onSaveInstanceState(), unixEpoch)

    override fun onRestoreInstanceState(thisStateParcel: Parcelable) =
        super.onRestoreInstanceState(restoreThisInstanceStateAndGetSuperInstanceState(thisStateParcel, this::restoreThisState))

    private fun restoreThisState(thisInstanceState: ThisInstanceInstanceState) {
        unixEpoch = thisInstanceState.unixEpoch
    }

    @Parcelize
    private data class ThisInstanceInstanceState(
        override val superInstanceState: Parcelable?,
        val unixEpoch: Long
    ) : ThisInstanceStateBase, Parcelable
}

◆ Viewの状態保存に関する共通コード

interface ThisInstanceStateBase {
    val superInstanceState: Parcelable?
}

fun <T : ThisInstanceStateBase> restoreThisInstanceStateAndGetSuperInstanceState(thisInstanceState: Parcelable, restoreThisInstanceState: (T) -> Unit): Parcelable? =
    @Suppress("UNCHECKED_CAST")
    (thisInstanceState as T).run {
        restoreThisInstanceState(this)
        superInstanceState
    }

◆ SampleActivity

unixEpochTextView.setUnixEpoch(System.currentTimeMillis())unixEpochTextView.unixEpoch = System.currentTimeMillis() に変わっただけです。

SampleActivity.kt
package com.objectfanatics.chrono10.ex_cv

import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.objectfanatics.chrono10.R

class SampleActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.sample_activity)

        val unixEpochTextView = findViewById<UnixEpochTextView>(R.id.unix_epoch_text_view)
        val showCurrentTimeButton = findViewById<Button>(R.id.show_current_time_button)
        showCurrentTimeButton.setOnClickListener { unixEpochTextView.unixEpoch = System.currentTimeMillis() }
    }
}

考察

◆ 今回やったこと

今回は、UnixEpochTextView に状態保存の仕組みを追加してみました。

◆ 問題点

  • SDK 等から提供される多くの View は、レイアウトファイル内部での属性指定を、databinding でもサポートしていることが多いが、現在の UnixEpochTextView ではサポートしていない。

◆ 次回

次回は、UnixEpochTextView を Data Binding に対応させてみようと思います。

Links

第1回: Custom View 探求記(TextView 継承編 その1)
第2回: Custom View 探求記(TextView 継承編 その2)
第3回: Custom View 探求記(TextView 継承編 その3)いまココ!
第4回: Custom View 探求記(TextView 継承編 その4)
第5回: Custom View 探求記(DataBindingを使うべきか使わぬべきかそれが問題だ編 その1)

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0