Android のダイアログで吹き出しを表現する

公開:2021/01/24
更新:2021/01/24
5 min読了の目安(約4500字TECH技術記事

はじめに

ダイアログで吹き出しを表示するサンプルがなかったので作成してみました!
少し考えれば実装できるような内容ですが、ちょっとした追加機能をお知らせする際の強調表示などに使えると思います。

実行結果

最終的にはこのような吹き出しが表示されます。

サンプル

layout

Dialog の setView に設定する layout です。

<?xml version="1.0" encoding="utf-8"?>
<layout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingStart="16dp"
    android:paddingEnd="16dp">

    <TextView
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:textSize="40dp"
      android:text=""
      android:textColor="#de3949ab"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      android:layout_marginEnd="12dp"
      android:rotation="290"
      tools:ignore="HardcodedText,SpUsage" />

    <com.google.android.material.card.MaterialCardView
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginBottom="28dp"
      app:cardCornerRadius="4dp"
      app:cardElevation="0dp"
      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintRight_toRightOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintBottom_toBottomOf="parent">

      <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="16dp"
        android:background="#de3949ab"
        android:orientation="vertical">

        <TextView
          android:id="@+id/text_normal"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:textColor="@android:color/white"
          tools:text="細字はこのように表示されます" />

        <TextView
          android:id="@+id/text_bold"
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:textColor="@android:color/white"
          android:textStyle="bold"
          tools:text="太字はこのように表示されます" />

      </LinearLayout>
    </com.google.android.material.card.MaterialCardView>
  </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

吹き出し部分は TextView を用いて を回転させることで表現しています。
先にこの TextView を持ってくることで CardView の下へ設定できました。
textSize を sp で設定してしまうとフォント設定に依存してしまうので、dp 指定しています。
実際には三角形の画像がよいと思います。

DialogFragment

引数を増やせば矢印の位置だったり向きだったりを変えられると思いますが、サンプルなので最低限の引数にしています。

class BubbleDialogFragment : DialogFragment() {

  private lateinit var argNormalText: String
  private lateinit var argBoldText: String

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    arguments?.run {
      argNormalText = getString(ARGS_TEXT_NORMAL) ?: throw IllegalArgumentException("arg is null")
      argBoldText = getString(ARGS_TEXT_BOLD) ?: throw IllegalArgumentException("arg is null")
    }
  }

  override fun onCreateDialog(savedInstanceState: Bundle?) = context?.let { context ->
    AlertDialog.Builder(context).apply {
      setView(FragmentBubbleDialogBinding.inflate(layoutInflater).apply {
        textNormal.text = argNormalText
        textBold.text = argBoldText
      }.root.apply {
        setOnClickListener { dismiss() }
      })
    }.create().apply {
      window?.run {
        setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))

        attributes = attributes.apply {
          width = ViewGroup.LayoutParams.MATCH_PARENT
          height = ViewGroup.LayoutParams.MATCH_PARENT
          gravity = Gravity.BOTTOM
          y = 30
        }
      }
    }
  }!!

  companion object {
    const val ARGS_TEXT_NORMAL = "bubble_dialog_text_normal"
    const val ARGS_TEXT_BOLD = "bubble_dialog_text_bold"

    fun newInstance(normalText: String, boldText: String, isPremium: Boolean) =
      BubbleDialogFragment().apply {
        arguments = Bundle().apply {
          putString(ARGS_TEXT_NORMAL, normalText)
          putString(ARGS_TEXT_BOLD, boldText)
        }
      }
  }
}

layout.root の setOnClickListener に dismiss を入れることで画面のどこをタップしても消えるようにしました。
また、window.setBackgroundDrawable に透明を指定することで CardView の角や吹き出し部分に白い枠が出ないようにしています。
attributes.y などで画面に合わせて表示する位置を変えていただければと思います。

最後に…

工夫の余地はありますが、流れは掴めたでしょうか??
どなたかの参考になれば幸いです<(_ _)>