Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import android.content.Context
- import android.graphics.Canvas
- import android.graphics.Outline
- import android.graphics.Path
- import android.graphics.Rect
- import android.graphics.Region
- import android.graphics.drawable.Drawable
- import android.graphics.drawable.LevelListDrawable
- import android.os.Build
- import android.util.AttributeSet
- import android.view.View
- import android.view.ViewGroup
- import androidx.annotation.Px
- import androidx.constraintlayout.widget.ConstraintLayout
- import androidx.core.graphics.withSave
- import androidx.core.view.forEach
- class CutoutBackgroundLayout : ConstraintLayout {
- constructor(context: Context) : super(context)
- constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
- constructor(
- context: Context,
- attrs: AttributeSet?,
- defStyleAttr: Int
- ) : super(context, attrs, defStyleAttr)
- init {
- viewTreeObserver.addOnGlobalLayoutListener { refreshOutlines() }
- }
- private val outline: Outline = Outline()
- private val rect = Rect()
- private fun refreshOutlines() {
- val background = this.background as? CutoutDrawable ?: return
- val path = background.cutoutPath
- path.rewind()
- forEach { view ->
- if (view.visibility != View.VISIBLE || (view.layoutParams as? LayoutParams)?.isCutoutBackground != true) {
- return@forEach
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
- view.outlineProvider.getOutline(view, outline)
- if (!outline.isEmpty) {
- outline.offset(view.left, view.top)
- if (outline.getRect(rect) && !rect.isEmpty) {
- path.addRoundRect(
- rect.left.toFloat(),
- rect.top.toFloat(),
- rect.right.toFloat(),
- rect.bottom.toFloat(),
- outline.radius,
- outline.radius,
- Path.Direction.CCW
- )
- return@forEach
- }
- }
- // else... [Outline.mPath] is `@hide` :(
- }
- view.getHitRect(rect)
- if (!rect.isEmpty) {
- path.addRect(
- rect.left.toFloat(),
- rect.top.toFloat(),
- rect.right.toFloat(),
- rect.bottom.toFloat(),
- Path.Direction.CCW
- )
- }
- }
- background.invalidateSelf()
- }
- override fun setBackground(background: Drawable?) {
- super.setBackground(
- if (background is CutoutDrawable?) background else CutoutDrawable(
- background
- )
- )
- refreshOutlines()
- }
- override fun generateDefaultLayoutParams(): ConstraintLayout.LayoutParams =
- LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
- override fun generateLayoutParams(p: ViewGroup.LayoutParams): ViewGroup.LayoutParams =
- LayoutParams(p)
- override fun generateLayoutParams(attrs: AttributeSet): LayoutParams =
- LayoutParams(context, attrs)
- override fun checkLayoutParams(p: ViewGroup.LayoutParams): Boolean = p is LayoutParams
- class LayoutParams : ConstraintLayout.LayoutParams {
- @JvmField
- var isCutoutBackground: Boolean = false
- constructor(source: LayoutParams) : super(source) {
- isCutoutBackground = source.isCutoutBackground
- }
- constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
- val ta =
- context.obtainStyledAttributes(attrs, R.styleable.CutoutBackgroundLayout_Layout)
- isCutoutBackground =
- ta.getBoolean(
- R.styleable.CutoutBackgroundLayout_Layout_layout_cutoutBackground,
- false
- )
- ta.recycle()
- }
- constructor(@Px width: Int, @Px height: Int) : super(width, height)
- constructor(source: ViewGroup.LayoutParams) : super(source)
- }
- }
- class CutoutDrawable() : LevelListDrawable() {
- constructor(drawable: Drawable?) : this() {
- addLevel(0, 0, drawable)
- }
- val cutoutPath = Path()
- override fun draw(canvas: Canvas) {
- canvas.withSave {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- clipOutPath(cutoutPath)
- } else {
- @Suppress("DEPRECATION")
- clipPath(cutoutPath, Region.Op.DIFFERENCE)
- }
- super.draw(this)
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement