Androidメモ すぐ忘れる自分のための備忘録

# Androidメモ置き場  
物忘れが激しい自分のためのAndroidテクニックメモ。
調べた当時の情報で記載しております。
たまにサンプルコード書いてますがkotlinで書いています。
ググってもJavaのコードが多くてぐぬぬってなりがちなので・・・。
動作保証はしません。自分用メモですのでご了承願います。

## Spinner で同じItemを再選択しても反応しない場合の対処方法
CustomSpinnerを作成する。デフォルトのSpinnerでは同じItemを選択してもonItemSelectedが呼び出されないので

```kotlin:Customspinner.kt
import android.content.Context
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatSpinner

// デフォルトのSpinnerだと同じItemを再選択した場合に、onItemSelectedListenerが発動しないため、自前でSpinnerを作成している
class CustomSpinner : AppCompatSpinner {
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
        context,
        attrs,
        defStyle
    )
    override fun setSelection(position: Int, animate: Boolean) {
        val isSameSelected = position == selectedItemPosition
        super.setSelection(position, animate)
        if (isSameSelected) {
            // 同じ項目が選択された場合もonItemSelectedを呼ぶようにしている
            onItemSelectedListener!!.onItemSelected(this, selectedView, position, selectedItemId)
        }
    }
    override fun setSelection(position: Int) {
        val isSameSelected = position == selectedItemPosition
        super.setSelection(position)
        if (isSameSelected) {
            // 同じ項目が選択された場合もonItemSelectedを呼ぶようにしている
            onItemSelectedListener!!.onItemSelected(this, selectedView, position, selectedItemId)
        }
    }
}
```

レイアウトファイルに作成したCustomSpinnerを設定する。
後はコードで作成したCustomSpinnerを取得して設定すればOK。クラス差し替えるだけでいけるはず。

## 画面初期表示時にSpinnerのonItemSelectedが２回呼ばれるのを防ぐ方法
Spinner作成時に、spinner.isFocusable = falseにし、onItemSelectedが初めて呼ばれたタイミングでspinner.isFocusable = trueで戻してあげると良い

```kotlin

// Spinnerの取得
val spinner = findViewById<CustomSpinner>(R.id.spinner)

// 初回起動時の対応
spinner.isFocusable = false

// OnItemSelectedListenerの実装
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
            // 項目が選択された時に呼ばれる
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                // 初回のみ動作
                if (!spinner.isFocusable) {
                    // ここでtrueに戻す
                    spinner.isFocusable = true
                    return
                }
                // TODO onItemSelectedでやりたい処理を書いてね

            }
            override fun onNothingSelected(parent: AdapterView<*>?) {
                // NOP
            }
        }
```

## OTAからダウンロードしたapkファイルをインストールしてアップデートできない件
【結論】署名ファイル作る。buildTypes release でビルドする。debugでビルドすると新規にインストールできるけど、アップデートできないよ。アプリアンインストールからの再インストールはできる。
https://developer.android.com/studio/publish/app-signing?hl=ja#debug-mode

## Google Maps Platformが提供している Directions APIのレスポンスで返却されるpoints使ってPolyLineで経路の線引こうとして絶望したら・・・読むと救われるかも

google map で2点間の経路を返却してくれるDirections APIだけど経路をPolyLineで引こうとしたら下記のような（一例）値が返却され絶望した。。。

```json:Response抜粋（例です）

    "overview_polyline": {
      "points": "a~l~Fjk~uOnzh@vlbBtc~@tsE`vnApw{A`dw@~w\\|tNtqf@l{Yd_Fblh@rxo@b}@xxSfytA
      blk@xxaBeJxlcBb~t@zbh@jc|Bx}C`rv@rw|@rlhA~dVzeo@vrSnc}Axf]fjz@xfFbw~@dz{A~d{A|zOxbrBbdUvpo@`
      cFp~xBc`Hk@nurDznmFfwMbwz@bbl@lq~@loPpxq@bw_@v|{CbtY~jGqeMb{iF|n\\~mbDzeVh_Wr|Efc\\x`Ij{kE}mAb
      ~uF{cNd}xBjp]fulBiwJpgg@|kHntyArpb@bijCk_Kv~eGyqTj_|@`uV`k|DcsNdwxAott@r}q@_gc@nu`CnvHx`k@dse
      @j|p@zpiAp|gEicy@`omFvaErfo@igQxnlApqGze~AsyRzrjAb__@ftyB}pIlo_BflmA~yQftNboWzoAlzp@mz`@|}_
      @fda@jakEitAn{fB_a]lexClshBtmqAdmY_hLxiZd~XtaBndgC"
    },
```

頑張ってデコードできるようだけどMaps SDK for Android Utility Libraryが提供している「Poly decoding and encoding」を使えば頑張らずに済むよって話。

```kotlin

// poliyUtilを使ってデコードできるよ！やったね。
PolyUtil.decode(引数にDirections APIで返却されたpointsを設定する)
```

https://github.com/googlemaps/android-maps-utils

## Intentで自分で作成したclassを呼び出し先のActivityに送りたい時
送りたい対象のclassにSerializable設定すればOK。設定しないとエラーになる

```kotlin:

----- 色々と中略　------

// 呼び出し先のActivityに送りたいclass
val sample = Sample(1)

val intent =
    Intent(this@SampleActivity, NextActivity::class.java)
// putExtraでセットする
intent.putExtra("sampleclass", sample)
startActivity(intent)

----- 色々と中略　------

// 受け取り側
val sample = intent.getSerializableExtra("sampleclass") as Sample

// Serializableつける必要がある
data class Sample(val id:Int): Serializable
```

【備忘録】フィールドに@Transientをつけているとシリアライズの対象外になる。そのため設定した値を呼び出し先のActivityに渡せないので注意（公式のドキュメントにシリアライズの対象外になる旨記載されています）
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.jvm/-transient/


## "2020-09-15T03:00:00.000Z"みたいな形式で現在時刻を表示したい場合（simpleDateFormat使う場合）
TとZの部分を''で括る必要あり

```kotlin

 val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
```

## Strings.xml とConstantsの使い分けについて

結論から言うと画面に表示するメッセージは国際化を考えてstrings.xmlに定義し、
それ以外のプログラム内部で使う文字列についてはConstantsに定義で良いと言う意見に賛同。

定数ってどこに置くのがAndroid的に良いのって思ったのと使い分けがいまいちピンとこなかったので調べたらstackoverflow上で同じ議論がされていました。
https://stackoverflow.com/questions/16160047/android-strings-xml-vs-static-constants

## android studioからエミュレータ削除したのにディスク容量が増えない場合の対処法（Windows）
結論から言うとavdファイルが残っている場合があるのでその場合は直接avdファイルを削除すればOK.（macは調べていないので確認したらメモっときます）
エミュレータ色々使うと数十GBレベルで容量食いますが、android studioからエミュレータを削除しても容量が減らない事象がたまにあるのでメモ。
具体的には、C:¥users¥username(対象のuser名）¥.android¥avd配下に消したはずのエミュレータ名のavdファイルが残っていたら削除すればOK

## エミュレータから自PC（localhost)につなげたい場合  
× http://localhost/ 
◯ http://10.0.2.2/
see more https://developer.android.com/studio/run/emulator-networking.html


以下随時更新