Skip to content

Instantly share code, notes, and snippets.

@premacck
Last active June 6, 2023 06:55
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save premacck/add41c3b43e7053a017b0a74a05014e0 to your computer and use it in GitHub Desktop.
Save premacck/add41c3b43e7053a017b0a74a05014e0 to your computer and use it in GitHub Desktop.
Bottom sheet dialog with dim and blur background, and curved corners. works with BlurKit (https://github.com/CameraKit/blurkit-android) and Anko (https://github.com/Kotlin/anko)
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/color_white_ffffff" />
<corners
android:topLeftRadius="@dimen/dimen16dp"
android:topRightRadius="@dimen/dimen16dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
style="@style/MatchWidthMatchHeight"
android:fitsSystemWindows="true">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
style="@style/MatchWidthMatchHeight"
android:elevation="1dp"
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"
android:importantForAccessibility="no"
android:soundEffectsEnabled="false" />
<FrameLayout
android:id="@+id/bottom_sheet"
style="?attr/bottomSheetStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:elevation="@dimen/dimen50dp"
app:layout_behavior="@string/bottom_sheet_behavior" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</FrameLayout>
//TODO: add package info here
import android.os.Bundle
import android.annotation.SuppressLint
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.FrameLayout
import androidx.annotation.LayoutRes
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 androidx.fragment.app.FragmentManager
import com.gdn.offline.events.R
import com.gdn.offline.events.base.util.animate
import com.gdn.offline.events.base.util.beginBlur
import com.gdn.offline.events.base.util.then
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN
import io.alterac.blurkit.BlurLayout
import org.jetbrains.anko.find
import org.jetbrains.anko.sdk27.coroutines.onClick
/**
* Prem's creation, on 18/03/19
*/
abstract class BlurBottomSheet : DialogFragment() {
@Suppress("PropertyName")
abstract val TAG: String
private lateinit var bottomSheet: FrameLayout
private lateinit var blurLayout: BlurLayout
private lateinit var behavior: BottomSheetBehavior<FrameLayout>
var isHideable: Boolean = true
var backStackId: Int = -1
private val bottomSheetCallback = object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(bottomSheet: View, newState: Int) {
if (newState == STATE_HIDDEN) {
dismiss()
}
}
override fun onSlide(bottomSheet: View, slideOffset: Float) {}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setStyle(DialogFragment.STYLE_NORMAL, R.style.BlurBottomSheetDialog)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? =
wrapInBottomSheet()
override fun onStart() {
super.onStart()
blurLayout.beginBlur()
if (behavior.state == BottomSheetBehavior.STATE_HIDDEN) {
behavior.state = BottomSheetBehavior.STATE_COLLAPSED
}
}
override fun onStop() {
blurLayout.pauseBlur()
super.onStop()
}
override fun dismiss() {
bottomSheet.animate(R.anim.float_down)?.then { super.dismiss() }
}
private fun wrapInBottomSheet(): View? {
val rootLayout = View.inflate(context, R.layout.design_bottom_sheet_dialog, null)
val coordinatorLayout = rootLayout.find<CoordinatorLayout>(R.id.coordinator)
blurLayout = coordinatorLayout.find(R.id.touch_outside_blur_layout)
bottomSheet = coordinatorLayout.find(R.id.bottom_sheet)
bottomSheet.setBackgroundResource(R.drawable.background_bottom_sheet_white)
behavior = BottomSheetBehavior.from(bottomSheet).apply {
setBottomSheetCallback(bottomSheetCallback)
isHideable = isHideable
}
val bottomSheetView = layoutInflater.inflate(bottomSheetLayout(), coordinatorLayout, false)
if (layoutParams() == null)
bottomSheet.addView(bottomSheetView)
else
bottomSheet.addView(bottomSheetView, layoutParams())
setListeners()
startAnimations(bottomSheet)
return rootLayout
}
private fun startAnimations(bottomSheet: FrameLayout) {
bottomSheet.animate(R.anim.float_up)
}
override fun getTheme(): Int = R.style.BlurBottomSheetDialog
@SuppressLint("ClickableViewAccessibility")
private fun setListeners() {
blurLayout.onClick {
if (isAdded && isHideable) {
dismiss()
}
}
ViewCompat.setAccessibilityDelegate(bottomSheet, 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)
}
})
bottomSheet.setOnTouchListener { _, _ -> true }
}
fun setIsHideable(cancelable: Boolean) {
super.setCancelable(cancelable)
if (isHideable != cancelable) {
isHideable = cancelable
behavior.isHideable = cancelable
}
}
fun show(fragmentManager: FragmentManager) {
backStackId = fragmentManager.beginTransaction()
.add(this, TAG)
.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out)
.commit()
}
@LayoutRes
abstract fun bottomSheetLayout(): Int
open fun layoutParams(): ViewGroup.LayoutParams? = null
}
<color name="color_light_black">#99000000</color>
<style name="MatchWidthMatchHeight">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
</style>
<!--Style for the "touch_outside_blur_layout" layout in "design_bottom_sheet_dialog-->
<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 for BlurBottomSheet-->
<style name="BlurBottomSheetDialog" 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>
<style name="AppModalStyle" parent="Widget.Design.BottomSheet.Modal">
<item name="android:background">@drawable/background_bottom_sheet_white</item>
</style>
import android.view.View
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import androidx.annotation.AnimRes
import androidx.appcompat.app.AppCompatActivity
import com.gdn.offline.events.base.compoundViews.BlurBottomSheet
/**
* Prem's creation, on 18/03/19
*/
fun View.animate(@AnimRes animResId: Int): Animation? {
clearAnimation()
val animation = AnimationUtils.loadAnimation(context, animResId)
startAnimation(animation)
return animation
}
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()
}
})
}
// Extension function for delayed blur of the BlurLayout
fun BlurLayout.beginBlur() {
postDelayed({
visibility = View.VISIBLE
startBlur()
}, 150)
}
fun AppCompatActivity.showBottomSheet(bottomSheet: BlurBottomSheet) {
bottomSheet.show(supportFragmentManager)
}
fun BlurBottomSheet.showBottomSheet(bottomSheet: BlurBottomSheet, dismissCurrent: Boolean = true) {
if (dismissCurrent) dismiss()
bottomSheet.show(activity!!.supportFragmentManager)
}
@Arun1811
Copy link

Arun1811 commented Jun 6, 2023

Can you please share the "float_up" and "float_down" anim files? Furthermore, could you provide an explanation of how to integrate the bottom sheet into this code?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment