3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

AlertDialogのレイアウトをHackする

Posted at

はじめに

AlertDialogの見た目はAlertDialog.Builderの下記のAPIで調整できます。しかし、どうしても謎のpaddingやSpaceに邪魔をされることがあったので、その解決方法を記載します。

  • Builder() : ダイアログ全体のstyleが指定できる。
  • setCustomTitle() : タイトルのlayoutを指定できる。backgroundを変更したい時などに使える。
  • setView() : コンテンツ領域のlayoutを指定できる。

実例

Android 6 で下記のようにAlertDialogを作成すると、下線付きタイトルと縦長のメッセージが表示されますが、画像の赤丸のように謎のpaddingが入ってしまっています。

CustomDialogFragment.kt
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return AlertDialog.Builder(context)
            .setCustomTitle(activity!!.layoutInflater.inflate(R.layout.layout_dialog_title, null))
            .setMessage((0..100).joinToString("\n") { i -> "hello ${i}" })            
            .setPositiveButton("OK", null)
            .create()
    }

screen_3.png

原因の調査

まず、AlertDialogのレイアウトの構造を調べるために先ほどのコードを以下のように書き換え実行し、Logcatを確認します。

CustomDialogFragment.kt
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return AlertDialog.Builder(context)
            .setCustomTitle(activity!!.layoutInflater.inflate(R.layout.layout_dialog_title, null))
            .setMessage((0..100).joinToString("\n") { i -> "hello ${i}" })
            .setPositiveButton("OK", null)
            .create()
            .also { dialog ->
                dialog.show() // show() しておかないとViewが取得できない
                showViewInfoRecursively(dialog.window.decorView)
            }
    }

    private fun showViewInfoRecursively(
        view: View?,
        hierarchy: Int = 0
    ) {
        if (view == null) return
        val indent = (0..hierarchy).joinToString("  ") { "" }
        val resourceName =
            if (view.id != View.NO_ID) view.resources.getResourceName(view.id)
            else "none"
        Log.d("CustomDialog", "${indent}class=${view.javaClass.simpleName}, resource=${resourceName}, id=${view.id}")

        if (view is ViewGroup)
            for (i in 0..view.childCount)
                showViewInfoRecursively(view.getChildAt(i), hierarchy + 1)
    }
Logcat
D/CustomDialog: class=DecorView, resource=none, id=-1
D/CustomDialog:   class=FrameLayout, resource=none, id=-1
D/CustomDialog:     class=FrameLayout, resource=android:id/content, id=16908290
D/CustomDialog:       class=LinearLayout, resource=android:id/parentPanel, id=16909076
D/CustomDialog:         class=LinearLayout, resource=android:id/topPanel, id=16909077
D/CustomDialog:           class=ConstraintLayout, resource=none, id=-1
D/CustomDialog:             class=AppCompatTextView, resource=none, id=-1
D/CustomDialog:           class=LinearLayout, resource=android:id/title_template, id=16909078
D/CustomDialog:             class=AppCompatImageView, resource=android:id/icon, id=16908294
D/CustomDialog:             class=DialogTitle, resource=android:id/alertTitle, id=16909079
D/CustomDialog:         class=FrameLayout, resource=android:id/contentPanel, id=16909084
D/CustomDialog:           class=ScrollView, resource=android:id/scrollView, id=16909085
D/CustomDialog:             class=LinearLayout, resource=none, id=-1
D/CustomDialog:               class=AppCompatTextView, resource=android:id/message, id=16908299
D/CustomDialog:               class=Space, resource=android:id/textSpacerNoButtons, id=16909090
D/CustomDialog:         class=FrameLayout, resource=android:id/customPanel, id=16909086
D/CustomDialog:           class=FrameLayout, resource=android:id/custom, id=16908331
D/CustomDialog:         class=ButtonBarLayout, resource=android:id/buttonPanel, id=16909081
D/CustomDialog:           class=AppCompatButton, resource=android:id/button3, id=16908315
D/CustomDialog:           class=Space, resource=android:id/spacer, id=16909087
D/CustomDialog:           class=AppCompatButton, resource=android:id/button2, id=16908314
D/CustomDialog:           class=AppCompatButton, resource=android:id/button1, id=16908313
D/CustomDialog:     class=ViewStub, resource=android:id/action_mode_bar_stub, id=16909303

Logcatに表示されたレイアウト構造から怪しげなViewを探します。ここでは、Spaceクラスが見当たらないので、いづれかのViewのmarginかpaddingが原因だと思われます。ここではリソース名がscrollViewのScroolViewとその子のLinearLayoutに当たりをつけます。

原因の確認

さらに以下のようにコードを変更し実行後、ScrollViewの境界(赤)とLinearLayoutの境界(青)を表示してみます。画像を見ると、問題の空白が赤線と青線で挟まれているので、原因はScrollViewのpaddingTopかLinearLayoutのlayoutMarginTopです。

CustomDialogFragment.kt
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return AlertDialog.Builder(context)
            .setCustomTitle(activity!!.layoutInflater.inflate(R.layout.layout_dialog_title, null))
            .setMessage((0..100).joinToString("\n") { i -> "hello ${i}" })
            .setPositiveButton("OK", null)
            .create()
            .also { dialog ->
                dialog.show() // show() しておかないとViewが取得できない
//                showViewInfoRecursively(dialog.window.decorView)
                // 公開されていないリソース名でViewを探す
                dialog.findViewById<ScrollView?>(getResources().getIdentifier(
                    "scrollView", "id", "android"
                ))?.also { scrollView ->
                    // ScrollView に赤いフレームを付与
                    scrollView.setBackgroundResource(R.drawable.shape_frame_red)
                    // ScrollView の子のLinearLayoutに青いフレームを付与
                    scrollView.getChildAt(0).setBackgroundResource(R.drawable.shape_frame_blue)
                }
            }
    }

aaa_3.png

修正

最終的な修正コードは以下のようになります。実行すると画像のように空白がなくなっています。

CustomDialogFragment.kt
    override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
        return AlertDialog.Builder(context)
            .setCustomTitle(activity!!.layoutInflater.inflate(R.layout.layout_dialog_title, null))
            .setMessage((0..100).joinToString("\n") { i -> "hello ${i}" })
            .setPositiveButton("OK", null)
            .create()
            .also { dialog ->
                dialog.show() // show() しておかないとViewが取得できない
//                showViewInfoRecursively(dialog.window.decorView)
                // 公開されていないリソース名でViewを探す
                dialog.findViewById<ScrollView?>(getResources().getIdentifier(
                    "scrollView", "id", "android"
                ))?.also { scrollView ->
                    // 今回の原因. paddingTopに24が設定されていた.
                    Log.d("CustomDialog", "paddingTop=${scrollView.paddingTop}")
                    scrollView.setPadding(0, 0, 0, 0)
                    // 今回の原因ではないが修正方法を記載
                    // val child = scrollView.getChildAt(0)
                    // child.layoutParams = FrameLayout.LayoutParams(child.layoutParams)
                    //     .also { it.setMargins(0, 0, 0, 0) }
                }
            }
    }

bbb_3.png

おわりに

SDKが意図していない方法でのレイアウト編集なので、SDKのバージョンが変われば動かなくなるかもしれません。あくまでも最終手段となります。

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?