SHARE
TWEET

Untitled

a guest Apr 20th, 2019 64 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import android.content.Context
  2. import android.graphics.Canvas
  3. import android.graphics.Outline
  4. import android.graphics.Path
  5. import android.graphics.Rect
  6. import android.graphics.Region
  7. import android.graphics.drawable.Drawable
  8. import android.graphics.drawable.LevelListDrawable
  9. import android.os.Build
  10. import android.util.AttributeSet
  11. import android.view.View
  12. import android.view.ViewGroup
  13. import androidx.annotation.Px
  14. import androidx.constraintlayout.widget.ConstraintLayout
  15. import androidx.core.graphics.withSave
  16. import androidx.core.view.forEach
  17.  
  18. class CutoutBackgroundLayout : ConstraintLayout {
  19.     constructor(context: Context) : super(context)
  20.     constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
  21.     constructor(
  22.         context: Context,
  23.         attrs: AttributeSet?,
  24.         defStyleAttr: Int
  25.     ) : super(context, attrs, defStyleAttr)
  26.  
  27.     init {
  28.         viewTreeObserver.addOnGlobalLayoutListener { refreshOutlines() }
  29.     }
  30.  
  31.     private val outline: Outline = Outline()
  32.     private val rect = Rect()
  33.  
  34.     private fun refreshOutlines() {
  35.         val background = this.background as? CutoutDrawable ?: return
  36.         val path = background.cutoutPath
  37.         path.rewind()
  38.         forEach { view ->
  39.             if (view.visibility != View.VISIBLE || (view.layoutParams as? LayoutParams)?.isCutoutBackground != true) {
  40.                 return@forEach
  41.             }
  42.             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  43.                 view.outlineProvider.getOutline(view, outline)
  44.                 if (!outline.isEmpty) {
  45.                     outline.offset(view.left, view.top)
  46.                     if (outline.getRect(rect) && !rect.isEmpty) {
  47.                         path.addRoundRect(
  48.                             rect.left.toFloat(),
  49.                             rect.top.toFloat(),
  50.                             rect.right.toFloat(),
  51.                             rect.bottom.toFloat(),
  52.                             outline.radius,
  53.                             outline.radius,
  54.                             Path.Direction.CCW
  55.                         )
  56.                         return@forEach
  57.                     }
  58.                 }
  59.                 // else... [Outline.mPath] is `@hide` :(
  60.             }
  61.             view.getHitRect(rect)
  62.             if (!rect.isEmpty) {
  63.                 path.addRect(
  64.                     rect.left.toFloat(),
  65.                     rect.top.toFloat(),
  66.                     rect.right.toFloat(),
  67.                     rect.bottom.toFloat(),
  68.                     Path.Direction.CCW
  69.                 )
  70.             }
  71.         }
  72.         background.invalidateSelf()
  73.     }
  74.  
  75.     override fun setBackground(background: Drawable?) {
  76.         super.setBackground(
  77.             if (background is CutoutDrawable?) background else CutoutDrawable(
  78.                 background
  79.             )
  80.         )
  81.         refreshOutlines()
  82.     }
  83.  
  84.     override fun generateDefaultLayoutParams(): ConstraintLayout.LayoutParams =
  85.         LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
  86.  
  87.     override fun generateLayoutParams(p: ViewGroup.LayoutParams): ViewGroup.LayoutParams =
  88.         LayoutParams(p)
  89.  
  90.     override fun generateLayoutParams(attrs: AttributeSet): LayoutParams =
  91.         LayoutParams(context, attrs)
  92.  
  93.     override fun checkLayoutParams(p: ViewGroup.LayoutParams): Boolean = p is LayoutParams
  94.  
  95.     class LayoutParams : ConstraintLayout.LayoutParams {
  96.         @JvmField
  97.         var isCutoutBackground: Boolean = false
  98.  
  99.         constructor(source: LayoutParams) : super(source) {
  100.             isCutoutBackground = source.isCutoutBackground
  101.         }
  102.  
  103.         constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
  104.             val ta =
  105.                 context.obtainStyledAttributes(attrs, R.styleable.CutoutBackgroundLayout_Layout)
  106.             isCutoutBackground =
  107.                 ta.getBoolean(
  108.                     R.styleable.CutoutBackgroundLayout_Layout_layout_cutoutBackground,
  109.                     false
  110.                 )
  111.             ta.recycle()
  112.         }
  113.  
  114.         constructor(@Px width: Int, @Px height: Int) : super(width, height)
  115.  
  116.         constructor(source: ViewGroup.LayoutParams) : super(source)
  117.     }
  118. }
  119.  
  120. class CutoutDrawable() : LevelListDrawable() {
  121.     constructor(drawable: Drawable?) : this() {
  122.         addLevel(0, 0, drawable)
  123.     }
  124.  
  125.     val cutoutPath = Path()
  126.  
  127.     override fun draw(canvas: Canvas) {
  128.         canvas.withSave {
  129.             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  130.                 clipOutPath(cutoutPath)
  131.             } else {
  132.                 @Suppress("DEPRECATION")
  133.                 clipPath(cutoutPath, Region.Op.DIFFERENCE)
  134.             }
  135.             super.draw(this)
  136.         }
  137.     }
  138. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top