Created
March 20, 2018 23:44
-
-
Save darnmason/e9c33d5c310d4699e742099f5319d27f to your computer and use it in GitHub Desktop.
A custom Android view to render a circular shadow. The radius of the gradient intersects both edges of the view and the startColor stop is the first visible point of the gradient. Customisable depth and gravity via xml.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<resources> | |
<declare-styleable name="CircularShadowView"> | |
<attr name="gravity"> | |
<flag name="bottom" value="80" /> | |
<flag name="left" value="3" /> | |
<flag name="right" value="5" /> | |
<flag name="top" value="48" /> | |
</attr> | |
<attr name="depth" format="dimension" /> | |
<attr name="startColor" format="color" /> | |
<attr name="endColor" format="color" /> | |
</declare-styleable> | |
</resources> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.content.Context | |
import android.graphics.Color | |
import android.graphics.RadialGradient | |
import android.graphics.Shader | |
import android.graphics.drawable.PaintDrawable | |
import android.graphics.drawable.ShapeDrawable | |
import android.graphics.drawable.shapes.RectShape | |
import android.util.AttributeSet | |
import android.view.Gravity.* | |
import android.widget.FrameLayout | |
class CircularShadowView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr) { | |
private var customDepth: Float? = null | |
private var endColor = Color.TRANSPARENT | |
private var startColor = Color.BLACK | |
private var gravity = BOTTOM | |
private val shaderFactory = object : ShapeDrawable.ShaderFactory() { | |
override fun resize(width: Int, height: Int): Shader { | |
val x: Double | |
val y: Double | |
val radius: Double | |
val depth: Float | |
when (gravity) { | |
BOTTOM, TOP -> { | |
depth = customDepth ?: height.toFloat() | |
x = width.toDouble() / 2 | |
radius = ((Math.pow(depth.toDouble(), 2.0) + Math.pow(x, 2.0)) / (2 * depth)) | |
y = if (gravity == BOTTOM) height + radius - depth else -radius + depth | |
} | |
else -> { | |
depth = customDepth ?: width.toFloat() | |
y = height.toDouble() / 2 | |
radius = ((Math.pow(depth.toDouble(), 2.0) + Math.pow(y, 2.0)) / (2 * depth)) | |
x = if (gravity == LEFT) -radius + depth else width + radius - depth | |
} | |
} | |
val start = (radius - depth) / radius | |
return RadialGradient(x.toFloat(), y.toFloat(), radius.toFloat(), | |
intArrayOf(startColor, startColor, endColor), | |
floatArrayOf(0f, start.toFloat(), 1f), | |
Shader.TileMode.CLAMP) | |
} | |
} | |
init { | |
if (attrs != null) { | |
val a = context.obtainStyledAttributes(attrs, R.styleable.CircularShadowView) | |
if (a.hasValue(R.styleable.CircularShadowView_depth)) { | |
customDepth = a.getDimension(R.styleable.CircularShadowView_depth, 0f) | |
} | |
endColor = a.getColor(R.styleable.CircularShadowView_endColor, endColor) | |
startColor = a.getColor(R.styleable.CircularShadowView_startColor, startColor) | |
gravity = a.getInt(R.styleable.CircularShadowView_gravity, gravity) | |
a.recycle() | |
} | |
val drawable = PaintDrawable() | |
drawable.shape = RectShape() | |
drawable.shaderFactory = shaderFactory | |
background = drawable | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment