Skip to content

Instantly share code, notes, and snippets.

@rosenpin
Last active February 12, 2021 16:27
Show Gist options
  • Save rosenpin/bd2a2f076f1a0630fd6c0b95b3c8f45b to your computer and use it in GitHub Desktop.
Save rosenpin/bd2a2f076f1a0630fd6c0b95b3c8f45b to your computer and use it in GitHub Desktop.
bottom sheet alert dialog
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="@dimen/dialog_padding"
android:paddingVertical="@dimen/activity_default_padding">
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.05" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.95" />
<LinearLayout
android:id="@+id/title_wrapper"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_small_padding"
android:gravity="center"
android:orientation="horizontal"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@id/guideline2"
app:layout_constraintStart_toStartOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/icon"
android:layout_width="@dimen/dialog_icon_size"
android:layout_height="@dimen/dialog_icon_size"
android:layout_marginEnd="@dimen/activity_small_padding_half" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:text="Title"
android:textAppearance="@style/TextAppearance.AppCompat.Headline" />
</LinearLayout>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/message"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginVertical="@dimen/activity_default_padding"
android:gravity="center_horizontal"
android:text="Message"
android:textAppearance="@style/TextAppearance.AppCompat.Body1"
app:fontFamily="sans-serif-light"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_wrapper" />
<com.google.android.material.button.MaterialButton
android:id="@+id/positive"
style="@style/DialogButton"
android:layout_width="@dimen/dialog_button_width"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_default_padding"
android:text="Positive"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/message" />
<com.google.android.material.button.MaterialButton
android:id="@+id/negative"
style="@style/Widget.MaterialComponents.Button.TextButton"
android:layout_width="@dimen/dialog_button_width"
android:layout_height="wrap_content"
android:minHeight="@dimen/dialog_button_negative_height"
android:text="Negative"
android:textAllCaps="false"
android:textColor="?colorOnBackground"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/positive"
app:rippleColor="?colorOnBackground" />
</androidx.constraintlayout.widget.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="DialogButton" parent="Widget.MaterialComponents.Button">
<item name="backgroundTint">@color/dialog_background</item>
<item name="android:textColor">?colorOnBackground</item>
<item name="rippleColor">?rippleColor</item>
<item name="strokeColor">?colorOnBackground</item>
<item name="minHeight">@dimen/dialog_button_height</item>
<item name="android:minHeight">@dimen/dialog_button_height</item>
<item name="cornerRadius">@dimen/dialog_positive_button_radius</item>
<item name="strokeWidth">2dp</item>
<item name="textAllCaps">false</item>
<item name="android:stateListAnimator">@animator/button_press_animation</item>
<item name="android:elevation">4dp</item>
</style>
</resources>
class DialogView : BottomSheetDialogFragment() {
private lateinit var binder: DialogViewBinding
private var icon: Drawable? = null
private var negativeText: String? = null
private var negativeListener: Listener? = null
private lateinit var title: String
private var content: String? = null
private var positiveText: String? = null
private var positiveListener: Listener? = null
fun content(content: String) {
this.content = content
}
fun withTitle(title: String) {
this.title = title
}
fun withPositive(positiveText: String, positiveListener: Listener? = null) {
this.positiveText = positiveText
this.positiveListener = positiveListener
}
fun withNegative(negativeText: String, negativeListener: Listener? = null) {
this.negativeText = negativeText
this.negativeListener = negativeListener
}
fun withIcon(icon: Drawable?) {
this.icon = icon
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binder = DialogViewBinding.inflate(LayoutInflater.from(context), container, false)
return binder.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (savedInstanceState != null) {
this.dismissAllowingStateLoss()
return
}
binder.title.text = this.title
binder.message.text = this.content
if (positiveText != null)
binder.positive.text = positiveText
else
binder.positive.visibility = View.GONE
binder.positive.setOnClickListener {
this.dismiss()
positiveListener?.let {
it(this)
}
}
if (negativeText != null)
binder.negative.text = negativeText
else
binder.negative.visibility = View.GONE
binder.negative.setOnClickListener {
this.dismiss()
negativeListener?.let {
it(this)
}
}
if (icon == null)
binder.icon.visibility = View.GONE
else
binder.icon.setImageDrawable(icon)
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
Timber.d("on configuration change")
dismissAllowingStateLoss()
}
companion object {
@JvmStatic
fun newInstance(bundle: Bundle): DialogView {
val fragment = DialogView()
fragment.arguments = bundle
return fragment
}
}
}
<resources>
<dimen name="dialog_button_width">280dp</dimen>
<dimen name="dialog_button_height">64dp</dimen>
<dimen name="dialog_positive_button_radius">24dp</dimen>
<dimen name="dialog_icon_size">24dp</dimen>
<dimen name="dialog_padding">32dp</dimen>
<dimen name="dialog_button_negative_height">42dp</dimen>
<dimen name="dialog_card_radius">16dp</dimen>
<dimen name="activity_default_padding">16dp</dimen>
<dimen name="activity_default_padding_big">24dp</dimen>
<dimen name="activity_default_padding_minus_spacing">12dp</dimen>
<dimen name="activity_small_padding">8dp</dimen>
<dimen name="activity_small_padding_half">4dp</dimen>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/dialog_background" />
<corners
android:topLeftRadius="@dimen/dialog_card_radius"
android:topRightRadius="@dimen/dialog_card_radius" />
</shape>
<resources>
<!-- Base application theme. -->
<style name="YourTheme" parent="Theme.MaterialComponents....">
...
<item name="bottomSheetDialogTheme">@style/AppBottomSheetDialogTheme</item>
</style>
<style name="AppBottomSheetDialogTheme" parent="Theme.Design.Light.BottomSheetDialog">
<item name="bottomSheetStyle">@style/AppModalStyle</item>
</style>
<style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/rounded_dialog</item>
</style>
</resources>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment