Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
AlertDialog like DoalogFragment with blur background. Works with BlurKit (https://github.com/CameraKit/blurkit-android) and Anko (https://github.com/Kotlin/anko)
// TODO: add package info here
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.StringRes
import androidx.annotation.StyleRes
import androidx.appcompat.app.AppCompatActivity
import androidx.cardview.widget.CardView
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.view.AccessibilityDelegateCompat
import androidx.core.view.ViewCompat
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
import androidx.fragment.app.DialogFragment
import io.alterac.blurkit.BlurLayout
import kotlinx.android.synthetic.main.design_blur_alert_dialog.*
import org.jetbrains.anko.find
import org.jetbrains.anko.sdk27.coroutines.onClick
/**
* Prem's creation, on 18/03/19
*/
class BlurAlertDialog : DialogFragment() {
private lateinit var alertDialog: CardView
private lateinit var blurLayout: BlurLayout
var isHideable: Boolean = true
@StyleRes
var styleRes: Int = -1
@StringRes
var title: String? = null
@StringRes
var message: String? = null
@StringRes
var positiveText: Int = -1
@StringRes
var negativeText: Int = -1
private var positiveClickListener: OnClickListener? = null
private var negativeClickListener: OnClickListener? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, styleRes)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
wrapInBottomSheet()
override fun onStart() {
super.onStart()
blurLayout.beginBlur()
validateAndAddLayout()
setListeners()
}
override fun onStop() {
blurLayout.pauseBlur()
super.onStop()
}
override fun dismiss() {
alertDialog.zoomOut()?.then { super.dismiss() }
}
private fun initWith(builder: Builder) {
this.styleRes = builder.styleRes
this.title = builder.title
this.message = builder.message
this.positiveText = builder.positiveText
this.negativeText = builder.negativeText
this.positiveClickListener = builder.positiveClickListener
this.negativeClickListener = builder.negativeClickListener
}
private fun wrapInBottomSheet(): View? {
val rootLayout = View.inflate(context, R.layout.design_blur_alert_dialog, null)
val coordinatorLayout = rootLayout.find<CoordinatorLayout>(R.id.coordinator)
blurLayout = coordinatorLayout.find(R.id.touch_outside_blur_layout)
alertDialog = coordinatorLayout.find(R.id.alert_dialog)
alertDialog.zoomIn()
return rootLayout
}
private fun validateAndAddLayout() {
title_text.text = title
message_text.text = message
if (positiveText != -1) {
positive_btn.makeVisible()
positive_btn.setText(positiveText)
positive_btn.onClick { positiveClickListener?.onClick(this@BlurAlertDialog) }
} else {
positive_btn.makeGone()
}
if (negativeText != -1) {
negative_btn.makeVisible()
negative_btn.setText(negativeText)
negative_btn.onClick { negativeClickListener?.onClick(this@BlurAlertDialog) }
} else {
negative_btn.makeGone()
}
}
override fun getTheme(): Int = styleRes
@SuppressLint("ClickableViewAccessibility")
private fun setListeners() {
blurLayout.onClick {
if (isAdded && isHideable) {
dismiss()
}
}
ViewCompat.setAccessibilityDelegate(alertDialog, object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfoCompat?) {
super.onInitializeAccessibilityNodeInfo(host, info)
if (isHideable) {
info?.addAction(AccessibilityNodeInfo.ACTION_DISMISS)
info?.isDismissable = true
} else {
info?.isDismissable = false
}
}
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
return if (action == AccessibilityNodeInfo.ACTION_DISMISS && isHideable) {
dismiss()
true
} else super.performAccessibilityAction(host, action, args)
}
})
alertDialog.setOnTouchListener { _, _ -> true }
}
class Builder(private val activity: AppCompatActivity) {
@StyleRes
var styleRes: Int = R.style.BlurDialog
@StringRes
var title: String? = null
@StringRes
var message: String? = null
@StringRes
var positiveText: Int = -1
@StringRes
var negativeText: Int = -1
var positiveClickListener: OnClickListener? = null
var negativeClickListener: OnClickListener? = null
fun setStyle(@StyleRes styleRes: Int) = apply { this.styleRes = styleRes }
fun setTitle(@StringRes title: Int) = apply { this.title = activity.getString(title) }
fun setMessage(@StringRes message: Int) = apply { this.message = activity.getString(message) }
fun setTitle(title: String?) = apply { this.title = title }
fun setMessage(message: String?) = apply { this.message = message }
fun setPositiveButton(@StringRes positiveText: Int, onClickListener: (BlurAlertDialog) -> Unit) = apply {
positiveText(positiveText)
positiveClickListener(object : BlurAlertDialog.OnClickListener {
override fun onClick(dialog: BlurAlertDialog) {
onClickListener(dialog)
}
})
}
fun setNegativeButton(@StringRes negativeText: Int, onClickListener: (BlurAlertDialog) -> Unit) = apply {
negativeText(negativeText)
negativeClickListener(object : BlurAlertDialog.OnClickListener {
override fun onClick(dialog: BlurAlertDialog) {
onClickListener(dialog)
}
})
}
private fun positiveText(@StringRes positiveText: Int) {
this.positiveText = positiveText
}
private fun negativeText(@StringRes negativeText: Int) {
this.negativeText = negativeText
}
private fun positiveClickListener(positiveClickListener: OnClickListener) {
this.positiveClickListener = positiveClickListener
}
private fun negativeClickListener(negativeClickListener: OnClickListener?) {
this.negativeClickListener = negativeClickListener
}
fun show() {
val blurAlertDialog = BlurAlertDialog()
blurAlertDialog.initWith(this)
blurAlertDialog.show(activity.supportFragmentManager, "$title - $message")
}
}
interface OnClickListener {
fun onClick(dialog: BlurAlertDialog)
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator"
style="@style/MatchWidthMatchHeight"
android:fitsSystemWindows="true">
<io.alterac.blurkit.BlurLayout
android:id="@+id/touch_outside_blur_layout"
style="@style/BlurLayoutFullScreen"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false" />
<View
style="@style/MatchWidthMatchHeight"
android:background="@color/color_light_black_blur_background"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false" />
<androidx.cardview.widget.CardView
android:id="@+id/alert_dialog"
style="@style/DefaultCard.WrapHeight"
android:layout_gravity="center"
app:cardElevation="@dimen/dimen20dp">
<RelativeLayout
style="?attr/bottomSheetStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<TextView
android:id="@+id/title_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dimen16dp"
android:textColor="@color/black_text"
android:textSize="@dimen/text18sp"
android:textStyle="bold" />
<TextView
android:id="@+id/message_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title_text"
android:layout_marginStart="@dimen/dimen16dp"
android:layout_marginEnd="@dimen/dimen16dp" />
<Button
android:id="@+id/positive_btn"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/message_text"
android:layout_alignParentEnd="true"
android:layout_marginTop="@dimen/dimen8dp"
android:layout_marginEnd="@dimen/dimen16dp"
android:text="@string/confirm"
android:textColor="@color/pumpkin_orange" />
<Button
android:id="@+id/negative_btn"
style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/message_text"
android:layout_marginTop="@dimen/dimen8dp"
android:layout_marginEnd="@dimen/dimen8dp"
android:layout_toStartOf="@+id/positive_btn"
android:text="@string/cancel"
android:textColor="@color/torch_red" />
</RelativeLayout>
</androidx.cardview.widget.CardView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
<style name="MatchWidthMatchHeight">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
</style>
<style name="BlurLayoutFullScreen">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
<item name="android:visibility">invisible</item>
<item name="blk_blurRadius">2</item>
<item name="blk_downscaleFactor">0.12</item>
<item name="blk_fps">0</item>
</style>
<style name="DefaultCard" parent="CardView.Light">
<item name="cardCornerRadius">@dimen/dimen8dp</item>
<item name="cardElevation">4dp</item>
<item name="cardUseCompatPadding">true</item>
</style>
<style name="DefaultCard.WrapHeight">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_marginStart">@dimen/dimen8dp</item>
<item name="android:layout_marginEnd">@dimen/dimen8dp</item>
</style>
<style name="BlurDialog" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">false</item>
<item name="bottomSheetStyle">@style/AppModalStyle</item>
<item name="android:windowBackground">@android:color/transparent</item>
<item name="android:statusBarColor">@color/colorPrimaryDark</item>
</style>
package com.gdn.offline.events.base.util
import android.animation.Animator
import android.view.View
import android.view.ViewPropertyAnimator
import android.view.animation.AccelerateInterpolator
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.annotation.AnimRes
import com.gdn.offline.events.R
/**
* Prem's creation, on 18/03/19
*/
fun BlurLayout.beginBlur() {
postDelayed({
visibility = View.VISIBLE
startBlur()
}, 150)
}
fun Animation.then(action: () -> Unit) {
setAnimationListener(object : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation?) {}
override fun onAnimationStart(animation: Animation?) {}
override fun onAnimationEnd(animation: Animation?) {
action()
}
})
}
fun ViewPropertyAnimator.then(action: () -> Unit) {
setListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {}
override fun onAnimationCancel(animation: Animator?) {}
override fun onAnimationStart(animation: Animator?) {}
override fun onAnimationEnd(animation: Animator?) {
action()
}
})
}
fun View.zoomIn(): Animation? = animate(R.anim.zoom_in)
fun View.zoomOut(): ViewPropertyAnimator? {
val anim = animate()
.scaleX(0f)
.scaleY(0f)
.alpha(0f)
.setInterpolator(AccelerateInterpolator())
.setDuration(150)
anim.start()
return anim
}
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<scale
android:duration="200"
android:fromXScale="0%"
android:fromYScale="0%"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="102%"
android:toYScale="102%" />
<scale
android:duration="100"
android:fromXScale="102%"
android:fromYScale="102%"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="200"
android:toXScale="98%"
android:toYScale="98%" />
<scale
android:duration="150"
android:fromXScale="98%"
android:fromYScale="98%"
android:interpolator="@android:anim/decelerate_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:startOffset="300"
android:toXScale="100%"
android:toYScale="100%" />
<alpha
android:duration="50"
android:fromAlpha="0"
android:startOffset="100"
android:toAlpha="1" />
</set>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment