それがいいことの序章です

楽しいことならいっぱい夢見ることならめいっぱい

Buttonにスタイルを設定する

そりゃそうだよねって感じではあるんだけど、あれこれどうするっけ…
ってよく調べなおすので自分で書いて覚えよう選手権を開催します。

よくあるシーン

ボタンに背景色つけるぞ!ボタンの文字色も変えるぞ!
ちゃんとenabledを設定して制御するぞ!

困った!enabled=falseなのに背景色も文字色も変わらん!OMG!

解決策

ColorStateListでボタンの状態に応じた色を背景色・文字色に設定しよう!
せっかくだし、スタイルで定義しちゃおう!

ColorStateList

たとえば、 enabled=falseのときの色は別で!なときは res/color/background_primary.xmlのようなファイルを作って

<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@android:color/darker_gray" android:state_enabled="false"/>
    <item android:color="@color/colorPrimary"/>
</selector>

としてあげるだけ。便利!

スタイルに設定してあげる

これを文字色も同じように作ってあげて、

<!-- Buttonのスタイル -->
<style name="Material.RadiusButton" parent="Widget.MaterialComponents.Button.UnelevatedButton">
    <item name="backgroundTint">@color/background_primary</item>
    <item name="rippleColor">@color/colorAccent</item>
    <item name="android:layout_height">@dimen/button_height_base</item>
    <item name="cornerRadius">@dimen/button_radius_base</item>
    <item name="android:textColor">@color/button_text</item>
</style>

ボタンにスタイルを適用する

<android.support.design.button.MaterialButton
    android:id="@+id/button"
    style="@style/Material.RadiusButton"
    android:layout_width="match_parent"
    android:enabled="@{!viewModel.executing}"
    android:onClick="@{() -> viewModel.onClick()}"
    android:text="@string/button_text"/>

とすれば、管理が楽ですね!めっちゃ説明を端折りました!

まとめ

書いたらきっと思い出せる!便利!

ioschedのToolbar周りを見てみた

iosched 2018が公開されましたね!知見たっぷり🎉
Tooblar`周りをどうしようか悩んでいたので抜粋。

BottomNavigationViewを使ったときのあれ。

ioschedではActivityToolbarを持つ形ではないようでした。個人的に先入観もありビックリ。

結論から言うと、FragmentAppBarLayoutを持たせる感じでした。 スケジュールタブの場合は

<AppBarLayout>
    <FrameLayout  app:layout_scrollFlags="scroll|snap|exitUntilCollapsed">
        <ImageView  android:layout_gravity="center_vertical|start"/>
        <TextView  android:textAppearance="@style/TextAppearance.IOSched.ToolbarTitle"/>
    </FrameLayout>
    <TabLayout/>
</AppBarLayout>

といった具合です。ガッツリImageViewTextViewも位置指定してました。

ちょっと気になってToolbarのUseageを探してみたところ、ファイルとしてはtoolbar.xmlはあるものの
これどこも使ってないように見受けられます(違ったら教えてほしい!)

UpNavigationが気になる。

ここで気になるのがスケジュール等の詳細のUpNavigation周りになってきます。
Toolbarではなく普通にImageViewでした。わお。

<ImageButton
    android:id="@+id/up"
    android:layout_width="?actionBarSize"
    android:layout_height="?actionBarSize"
    android:layout_gravity="start|top"
    android:background="?selectableItemBackgroundBorderless"
    android:contentDescription="@string/navigate_up"
    app:srcCompat="@drawable/ic_arrow_back" />

こんな具合です。知らなかった。。
スクロールに応じて見えなくなったりもしていたのでてっきりToolbarかと思ってました。
この挙動についてはPushUpScrollListenerで定義されているようです。

感想

Material studiesを見ていると、Toolbar周りの自由度がグッと上がった気がしていて
勝手にToolbarを拡張と考えていましたが、結構筋肉で実装していく感じになるのかな?

KotlinのSealedクラス使いたくて無理をした

Kotlinのsealed classを使いたいと虎視眈々だったんだけど
ついに突っ込んでみた。今回の用途ではそんなにメリットはないけど。
よくある成功or失敗を柔軟にという感じはなくemunの拡張くらいの気持ち。

使ったとこ

Firebase AnalyticsでUser Propertyを設定したかったんだけど、enum

enum class UserProperty(
    val propertyName: String,
    val value: String
) {

  AREA_KYUSYU("area", "九州),
  AREA_SHIKOKU("area", "四国),
    ...
}

とするのは毎回areaって書かなきゃなのがかっこ悪いなと。

おっ、と思ってsealed class使って見ると

sealed class UserProperty(
    val name: String,
    open val value: String
) {

  abstract class Area(override val value: String) : UserProperty("area", value)

  object Kyushu : Area("male")
  object Shikoku : Area("female")
    ...
}

って書けた。コレジャナイ感は否めないけど、こっちのほうが好みなので良しとしよう。

まとめ

もっと活用していこう。あとブログちゃんと書くぞ!(n回目)

KotlinでGsonでStringをBooleanにする

やったこと

AndroidでRetrofit + Gsonを使っているときのおはなし。
APIレスポンスにStringで"0"、"1"が返ってくる場面に出くわして
これをBooleanとして扱いたくってTypeAdapterを使ったよ。

なにが起きたか

{
    "is_success": "1"
}

みたいなやつをBooleanで扱うぞと思って

data class Response(
    @SerializedName("is_success")
    val isSuccess: Boolean
)

というふうにしてたら全部trueになった。全てが正になった困った。

どうやったか

パッと思いついたのはコンバータクラス作って

// レスポンスはStringで受ける
// data class Response(
//     @SerializedName("is_success")
//     val isSuccess: String
// )

object ResponseConverter {
  fun convert(res: Response) = Item(isSuccess = res.isSuccess == "1")
}

fun Response.convert() = ResponseConverter.convert(this)

みたいにするのだけど、毎回するのアレだよな〜と思って調べたら
TypeAdapterなるものを知った。詳しくは説明しないけど便利。

ということで

class IntToBooleanTypeAdapter : TypeAdapter<Boolean>() {
    override fun write(out: JsonWriter, value: Boolean?) {
        if (value == null) {
            out.nullValue()
            return
        }

        out.value(if (value) 1 else 0)
    }

    override fun read(`in`: JsonReader): Boolean {
        if (`in`.peek() == null) return false
        return `in`.nextInt() == 1
    }
}

というふうにしてあげて、レスポンスを

data class Response(
    @SerializedName("is_success") @JsonAdapter(IntToBooleanTypeAdapter::class)
    val isSuccess: Boolean
)

のようにしてあげることで、無事変換された!!

おわりに

Gsonがどのようにうまいこと変換してくれるかまで
調べるのに手が回ってないので時間ができたら調べる。

Gsonなのか、Kotlinの型の問題なのかわすれちゃった。。
Javaのときはできてたような…できてなかったような…

調べたら追記するかも。明日はDroidKaigi。

使わないパラメータでWarningを出さない for Kotlin

Androidの話。
MVVMアーキテクチャで開発をしていると、ViewModelにクリックメソッドを実装する事が多い。
その際、

fun onClickHoge(view: View) {
  someThing()
}

のように定義するんだけど、このviewというパラメータはそこまで使わない。

使わないパラメータで警告を出さないようにしようと

fun onClickHoge(@SuppressWarnings("unused") view: View) {
  someThing()
}

としてたんだけど、これでは警告が抑えられていなかった。

調べてみると、これはJavaの書き方のようで、Kotlinだと@Suppressを使うらしい。
そこで、

fun onClickHoge(@Suppress("unused") view: View) {
  someThing()
}

としたんだけど、これでも警告が抑えられていなかった。

調べてみると、unusedじゃなくてUNUSED_PARAMETERを使うらしい。 そこで、

fun onClickHoge(@Suppress("UNUSED_PARAMETER") view: View) {
  someThing()
}

とすると、警告が抑えられた!🙌

conclusion

困ったらalt + Enterだ 🙏 f:id:rmakiyama:20180126140801p:plain