Skip to content

Instantly share code, notes, and snippets.

@tunjid
Last active January 3, 2020 10:33
Show Gist options
  • Select an option

  • Save tunjid/b30b62f722a80af627cf5f032b705f02 to your computer and use it in GitHub Desktop.

Select an option

Save tunjid/b30b62f722a80af627cf5f032b705f02 to your computer and use it in GitHub Desktop.
private const val MIRROR = 180F
private const val INITIAL_DELAY = 0.15F
private val translucentBlack = Color.argb(50, 0, 0, 0)
fun speedDial(
anchor: View,
@ColorInt tint: Int = anchor.context.themeColorAt(R.attr.colorPrimary),
@StyleRes animationStyle: Int = android.R.style.Animation_Dialog,
layoutAnimationController: LayoutAnimationController = LayoutAnimationController(speedDialAnimation, INITIAL_DELAY).apply { order = ORDER_NORMAL },
items: List<Pair<CharSequence?, Drawable>>,
dismissListener: (Int?) -> Unit
) = LinearLayout(anchor.context).run root@{
rotationY = MIRROR
rotationX = MIRROR
clipChildren = false
clipToPadding = false
orientation = VERTICAL
layoutAnimation = layoutAnimationController
popOver(anchor = anchor, adjuster = getOffset(anchor)) popUp@{
this.animationStyle = animationStyle
var dismissReason: Int? = null
setOnDismissListener { dismissListener(dismissReason) }
items.mapIndexed { index, pair -> speedDialLayout(pair, tint, View.OnClickListener { dismissReason = index; dismiss() }) }
.forEach(this@root::addView)
}
}
private fun LinearLayout.speedDialLayout(pair: Pair<CharSequence?, Drawable>, tint: Int, clickListener: View.OnClickListener) = LinearLayout(context).apply {
rotationY = MIRROR
rotationX = MIRROR
clipChildren = false
clipToPadding = false
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)
updatePadding(bottom = context.resources.getDimensionPixelSize(R.dimen.single_margin))
setOnClickListener(clickListener)
addView(speedDialLabel(tint, pair.first, clickListener))
addView(speedDialFab(tint, pair, clickListener))
}
private fun LinearLayout.speedDialLabel(tint: Int, label: CharSequence?, clicker: View.OnClickListener) = AppCompatTextView(context).apply {
val dp4 = context.resources.getDimensionPixelSize(R.dimen.quarter_margin)
val dp8 = context.resources.getDimensionPixelSize(R.dimen.half_margin)
isClickable = true
background = context.ripple(tint) { setAllCornerSizes(dp8.toFloat()) }
isVisible = label != null
text = label
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
marginEnd = context.resources.getDimensionPixelSize(R.dimen.single_margin)
gravity = Gravity.CENTER_VERTICAL
}
updatePadding(left = dp8, top = dp4, right = dp8, bottom = dp4)
setOnClickListener(clicker)
}
private fun LinearLayout.speedDialFab(tint: Int, pair: Pair<CharSequence?, Drawable>, clicker: View.OnClickListener) = AppCompatImageButton(context).apply {
val dp40 = context.resources.getDimensionPixelSize(R.dimen.double_and_half_margin)
imageTintList = null
background = context.ripple(tint) { setAllCornerSizes(dp40.toFloat()) }
layoutParams = LinearLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT).apply {
gravity = Gravity.CENTER_VERTICAL
height = dp40
width = dp40
}
setOnClickListener(clicker)
setImageDrawable(if (pair.second !is BitmapDrawable) BitmapDrawable(context.resources, pair.second.toBitmap()) else pair.second)
}
private fun View.getOffset(anchor: View): () -> Point = {
val dp40 = context.resources.getDimensionPixelSize(R.dimen.double_and_half_margin)
val halfAnchorWidth = (anchor.width / 2)
val halfMiniFabWidth = (dp40 / 2)
val xOffset = if (width > anchor.width) halfAnchorWidth + halfMiniFabWidth - width else halfAnchorWidth - halfMiniFabWidth
Point(xOffset, -(anchor.height / 2) - height)
}
private fun Context.ripple(tint: Int, shapeModifier: ShapeAppearanceModel.Builder.() -> Unit): RippleDrawable = RippleDrawable(
ColorStateList.valueOf(translucentBlack),
MaterialShapeDrawable(ShapeAppearanceModel.builder().run {
shapeModifier(this)
build()
}).apply {
tintList = ColorStateList.valueOf(tint)
setShadowColor(Color.DKGRAY)
initializeElevationOverlay(this@ripple)
},
null
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment