Skip to content

Instantly share code, notes, and snippets.

@andriipanasiuk
Created December 14, 2020 09:14
Show Gist options
  • Save andriipanasiuk/65bd6831c0f2382218475554e4a7b12b to your computer and use it in GitHub Desktop.
Save andriipanasiuk/65bd6831c0f2382218475554e4a7b12b to your computer and use it in GitHub Desktop.
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import androidx.annotation.ColorInt
import com.github.florent37.shapeofview.ShapeOfView
import com.github.florent37.shapeofview.manager.ClipPathManager.ClipPathCreator
import wise.house.R
import kotlin.math.*
class SquircleView : ShapeOfView {
private val borderPaint = Paint(Paint.ANTI_ALIAS_FLAG)
private val borderPath = Path()
@ColorInt
private var borderColor = Color.WHITE
private var borderWidthPx = 0f
private var corners: Double = 0.6
constructor(context: Context) : super(context) {
init(context, null)
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
if (attrs != null) {
val attributes = context.obtainStyledAttributes(attrs, R.styleable.SquircleView)
borderColor = attributes.getColor(R.styleable.SquircleView_shape_squircle_borderColor, borderColor)
borderWidthPx = attributes.getDimensionPixelSize(R.styleable.SquircleView_shape_squircle_borderWidth, borderWidthPx.toInt()).toFloat()
corners = attributes.getFloat(R.styleable.SquircleView_shape_squircle_corners, corners.toFloat()).toDouble()
attributes.recycle()
}
borderPaint.style = Paint.Style.STROKE
super.setClipPathCreator(object : ClipPathCreator {
override fun createClipPath(width: Int, height: Int): Path {
return createSuperEllipsePath(width / 2, height / 2, corners = corners)
}
override fun requiresBitmap(): Boolean = false
})
}
override fun requiresShapeUpdate() {
borderPath.set(
createSuperEllipsePath(
((width - borderWidthPx) / 2).toInt(), ((height - borderWidthPx) / 2).toInt(),
borderWidthPx / 2, borderWidthPx / 2
)
)
super.requiresShapeUpdate()
}
override fun dispatchDraw(canvas: Canvas) {
super.dispatchDraw(canvas)
if (borderWidthPx > 0) {
borderPaint.strokeWidth = borderWidthPx
borderPaint.color = borderColor
canvas.drawPath(borderPath, borderPaint)
}
}
private fun createSuperEllipsePath(radX: Int, radY: Int, translateX: Float = 0f, translateY: Float = 0f, corners: Double = 0.6): Path {
var l = 0.0
var angle = Math.toRadians(l)
val path = Path()
var x = getX(radX, angle, corners)
var y = getY(radY, angle, corners)
path.moveTo(x, y)
for (i in 1 until 360) {
l++
angle = Math.toRadians(l)
x = getX(radX, angle, corners)
y = getY(radY, angle, corners)
path.lineTo(x, y)
}
path.close()
val matrix = Matrix()
matrix.postTranslate(translateX + radX.toFloat(), translateY + radY.toFloat())
path.transform(matrix)
return path
}
private fun getX(radX: Int, angle: Double, corners: Double) =
(cos(angle).absoluteValue.pow(corners) * radX * cos(angle).sign).toFloat()
private fun getY(radY: Int, angle: Double, corners: Double) =
(sin(angle).absoluteValue.pow(corners) * radY * sin(angle).sign).toFloat()
fun setBorderColor(borderColor: Int) {
this.borderColor = borderColor
requiresShapeUpdate()
}
private var borderWidth: Float
get() = borderWidthPx
set(value) {
borderWidthPx = value
requiresShapeUpdate()
}
var borderWidthDp: Float
get() = pxToDp(borderWidth)
set(value) {
borderWidth = dpToPx(value)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment