月: 2023年11月

Composeのプレビューをちょっと便利にする

Composeのプレビューを表示しておくとアプリをビルドしなくても大体の見た目を確認できるので便利です。しかし普通に@Previewアノテーションだけつけてもプレビューは一つしか表示されません。複数のプレビューを表示したい時はPreviewParameterProviderを使うと2個以上のプレビューを表示できます。

import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter

internal class AlertDialogPreviewParameterProvider : PreviewParameterProvider<AlertDialogPreviewParameterProvider.AlertDialogPreviewParameter> {
    data class AlertDialogPreviewParameter(
        val iconRes: Int?,
        val iconDescription: String?
    )

    override val values: Sequence<AlertDialogPreviewParameter>
        get() = sequenceOf(
            AlertDialogPreviewParameter(null, null),
            AlertDialogPreviewParameter(android.R.drawable.ic_delete, "iconDescription"),
        )
}

@Preview
@Composable
internal fun AlertDialogPreview(
    @PreviewParameter(AlertDialogPreviewParameterProvider::class) parameter: AlertDialogPreviewParameterProvider.AlertDialogPreviewParameter,
) =
    AlertDialog(
        iconRes = parameter.iconRes,
        iconDescription = parameter.iconDescription,
        title = "title",
        text = "text",
        dismissText = "dismissText",
        confirmText = "confirmText",
        onDismissRequest = {},
        onConfirmation = {},
    )

@Composable
fun AlertDialog(
    @DrawableRes iconRes: Int? = null,
    iconDescription: String? = null,
    title: String,
    text: String,
    confirmText: String,
    dismissText: String,
    onDismissRequest: () -> Unit,
    onConfirmation: () -> Unit,
) {
    // iconResとiconDescriptionはnullの場合に表示が異なるAlertDialog
}

これで2個以上のプレビューがAndroidStudio上に表示されます。ちなみにkoverでカバレッジを計測している場合は@Previewの関数やPreviewParameterProviderを継承しているクラスがカバレッジ対象のコードとして判定されてしまいますが、下記のような設定をbuild.gradle.ktsに記載すればカバレッジ対象から外すことができます。

koverReport {
    filters {
        excludes {
            classes(
                // PreviewParamterProviderというクラス名を除外する
                "*PreviewParameterProvider*",
            )
            // @Previewアノテーションがついている関数を除外する
            annotatedBy("androidx.compose.ui.tooling.preview.Preview")
        }
    }
}

setHasOptionsMenu()、onCreateOptionsMenu()、onOptionsItemSelected()あたりのdeprecated対応でMenuProviderに置き換える

メニュー関連の関数がdeprecatedになったので対応しないといけなくなりました。

https://developer.android.com/reference/androidx/core/view/MenuProvider

変更前

class MyFragment : Fragment() {
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.fragment_my, menu)
        // menuの初期化処理
    }

    override fun onOptionsItemSelected(menuItem: MenuItem): Boolean {
        when (menuItem) {
            R.id.menu1 -> {
                // menu1が押されたときの処理
            }
        }
        return super.onOptionsItemSelected(menuItem)
    }
}

変更後

class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        setupMenus()
    }

    private fun setupMenus() {
        (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider {
            override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                menuInflater.inflate(R.menu.fragment_my, menu)
                // menuの初期化処理
            }

            override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
                when (menuItem.itemId) {
                    R.id.menu1 -> {
                        // menu1が押されたときの処理
                    }
                }
                return true // 押下したイベントを消費する場合はtrue、それ以外の場合はfalse
            }
        }, viewLifecycleOwner, Lifecycle.State.RESUMED)
    }
}

MenuProviderにはonPrepareMenu()とonMenuClosed()があるのでもう少し細かい制御が必要な場合はこのあたりを使うと良いかも。


[Android] いい感じに文節を区切って表示する

TextViewで文字列を表示するときに日本語の文節毎に改行などがされていないと読みにくい見た目になってしまいます。そういう場合はTextViewの属性にandroid:lineBreakWordStyle=”phrase”を指定するといい感じに文節を区切って表示してくれます。lineBreakWordStyleはAPI LEVEL 33以降ではないと利用できません。

試しに実装してみましたが「タッチスクリーンモバイルデバイス」あたりがおかしいですね。全部カタカナだから文節の判定がしにくかったということだろうか。

表示を分節に区切るのではなくて文字列を分節に区切ってほしいという需要もあるかと思いますがそういうときはBudouXというライブラリを使います。

https://developers-jp.googleblog.com/2023/09/budoux-adobe.html

implementation("com.google.budoux:budoux:0.5.2") // バージョンは適宜調べてください
import com.google.budoux:budoux:

val phraseList = Parser.loadDefaultJapaneseParser().parse("長い文章を文節に区切ってほしい")

phraseListにList<String>型で文節に区切られた文字列が入っています。あとはお好きに加工したりすれば良いかと思います。