記録

主にAndroidアプリ開発に関する知見やその周辺に関わることについて記事をかいています

おしりセレブWETを2か月間使ってみた感想

おしりセレブWETって何かというと、簡単に言うと おしりを拭くようのWETティッシュ です。ウォシュレットとトイレットペーパーだけじゃ何かと不安で(あとごしごししたくない、痛いし)という理由で購入して2か月間使いました。

正直めちゃめちゃ良いです。ルーアナが優しく綺麗になっていくのが体験として素晴らしい。感覚的にトイレットペーパーの使用量も減りました。ウォシュレットの水拭くぐらいにしか使わない。

Amazonで注文するだけだけだし、去年買った買い物の中でかなり良い買い物でした。

【Android】Layout PreviewのDefault Themeを設定する

AndroidManifest.xmlでapplicationにthemeを設定しているとレイアウトプレビューのThemeにそれが反映される。

f:id:m4kvn:20210127071615p:plain

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.NavigationComponentSample">

そのため、モジュール分割等でAndroidManifest.xmlが分割されている場合などは、そちらにもthemeの設定をしないとそのモジュール内のレイアウトプレビューにthemeが適用されません。

<application android:theme="@style/Theme.NavigationComponentSample">
    <activity android:name=".HogeActivity" />
</application>

スタイルを変更してもプレビューに反映されずビルドしないと確認できなくて困っていたらこれが原因だった。

こちらを参考にしました。

stackoverflow.com

Navigation componentの暗黙的ディープリンクにはpathが必要

Navigation componentで m4kvn://main のようなディープリンクの設定をしたけど全然機能してくれないから何故なのか調べた。

調べた結果、Merged Manifestで <data android:path="/" /> が設定されるので m4kvn://mainURIには反応しないということだった。

次の記事が参考になります。

star-zero.medium.com

ナビゲーションには原則がある

Android Developersにナビゲーションの原則が記載されている。

developer.android.com

まとめると以下のようになる(細かい内容は上記を読む)

  • 最初に表示される固定の画面が必要で必ずバックスタックの最下層にある
  • アップボタンとバックボタンは同じ機能を持つがアップボタンがアプリを終了することはない
  • ディープリンクは手動で移動したのと同じバックスタックになり以前のバックスタックは破棄する

DialogFragmentでViewBindingを利用する

DialogやFragmentの layoutInflater を使ってViewBindingをinflateして、Dialogの setContentView にわたす。クリック時にDialogをdismissしたい場合などは先にDialogを作っておいて dialog.dismiss() をすれば良い。

class HogeDialogFragment : DialogFragment() {

    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        val binding = FragmentHogeBinding.inflate(layoutInflater)
        binding.textView.text = getString(R.string.hello_world)

        return super.onCreateDialog(savedInstanceState).apply {
            // val binding = FragmentHogeBinding.inflate(layoutInflater)
            // binding.textView.text = getString(R.string.hello_world)
            setContentView(binding.root)
        }
    }

普段はAlertDialogにsetViewするのであまり使わないけど、リファクタリングとかに役立つかも。

MenuItemの文字色を動的に変更する

MenuItemのtitleにSpannableStringBuilderで加工したテキストを渡してあげる。なので色以外も変更できる。

val titleText = menuItem.title
val builder = SpannableStringBuilder(titleText)
val colorSpan = ForegroundColorSpan(color)
builder.setSpan(colorSpan, 0, titleText.length, SpannableStringBuilder.SPAN_EXCLUSIVE_EXCLUSIVE)
menuItem.title = builder

MenuItemの取得方法

Toolbarに直接Menuをinflateしている場合はToolbarのMenuから取得できる。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(binding.root)
    binding.toolbar.inflateMenu(R.menu.hoge)
    val menuItem: MenuItem = binding.toolbar.menu.findItem(R.id.piyo)
}

onCreateOptionsMenuでMenuをinflateしてる場合は、onPrepareOptionsMenuを invalidateOptionsMenu() で呼び出しMenuItemを取得し処理する。

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    menuInflater.inflate(R.menu.hoge, menu)
    return true
}

override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
    val menuItem: MenuItem? = menu?.findItem(R.id.piyo)
    return true
}

GroupieでViewBindingを簡単に利用する

GroupieのViewHolderから itemView でViewが使えるので、これを利用して ViewBinding.bind(viewHolder.itemView) をしてあげるだけで良い。かなり簡略的だが、実際は次のように使う。

class HogeItem : Item<ViewHolder>() {

    override fun getLayout(): Int = R.layout.item_hoge

    override fun bind(viewHolder: ViewHolder, position: Int) {
        val binding = ItemHogeBinding.bind(viewHolder.itemView)
        binding.textView.text = "Hello world!"
    }
}

また、createViewHolder であらかじめ設定したい場合は、ここでも ViewBinding.bind(itemView) してあげれば良い。

class HogeItem : Item<ViewHolder>() {

    override fun getLayout(): Int = R.layout.item_hoge

    override fun createViewHolder(itemView: View): ViewHolder {
        val binding = ItemHogeBinding.bind(itemView)
        binding.textView.visibility = View.VISIBLE
        return super.createViewHolder(binding.root)
    }
…