Reputation: 1295
I am working on a creating a button with no fill, but a gradient stroke. For reference, here is the end result I am after:
I would like to know how to create such a button with a gradient stroke and no fill programmatically. I looked over GradientDrawable
class and the setStroke()
method in it. None seem to allow this. Is there any way to programmatically perform this or is it not possible at all?
Upvotes: 2
Views: 2706
Reputation: 4964
Here is a further step from what @lamat8 contributed:
import android.graphics.*
import android.graphics.Paint.ANTI_ALIAS_FLAG
import android.graphics.Path.FillType
import android.graphics.drawable.Drawable
import androidx.annotation.NonNull
import java.lang.RuntimeException
class GradientDrawable(
private var strokeStartColor: Int,
private var strokeEndColor: Int,
private var fillStartColor: Int,
private var fillEndColor: Int,
private var strokeWidth: Float,
private var radius: Float,
private var strokeGradientDirection: Direction,
private var fillGradientDirection: Direction
) : Drawable() {
enum class Direction {
LEFT_RIGHT,
TOP_BOTTOM,
RIGHT_LEFT,
BOTTOM_TOP,
TL_BR,
TR_BL,
BR_TL,
BL_TR
}
private val strokePaint: Paint = Paint(ANTI_ALIAS_FLAG)
private val fillPaint: Paint = Paint(ANTI_ALIAS_FLAG)
private val strokeOuterRect = RectF()
private val fillRect = RectF()
private val path = Path()
init {
strokePaint.style = Paint.Style.FILL
path.fillType = FillType.EVEN_ODD
}
override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
path.reset()
strokeOuterRect.set(bounds)
fillRect.set(
bounds.left + strokeWidth,
bounds.top + strokeWidth,
bounds.right - strokeWidth,
bounds.bottom - strokeWidth
)
path.addRoundRect(
strokeOuterRect,
radius,
radius,
Path.Direction.CW
)
path.addRoundRect(
fillRect,
radius,
radius,
Path.Direction.CW
)
}
override fun draw(@NonNull canvas: Canvas) {
var x0: Float
var y0: Float
var x1: Float
var y1: Float
// drawing the stroke
when (strokeGradientDirection) {
Direction.LEFT_RIGHT -> {
x0 = strokeOuterRect.left
y0 = strokeOuterRect.centerY()
x1 = strokeOuterRect.right
y1 = strokeOuterRect.centerY()
}
Direction.TOP_BOTTOM -> {
x0 = strokeOuterRect.centerX()
y0 = strokeOuterRect.top
x1 = strokeOuterRect.centerX()
y1 = strokeOuterRect.bottom
}
Direction.RIGHT_LEFT -> {
x0 = strokeOuterRect.right
y0 = strokeOuterRect.centerY()
x1 = strokeOuterRect.left
y1 = strokeOuterRect.centerY()
}
Direction.BOTTOM_TOP -> {
x0 = strokeOuterRect.centerX()
y0 = strokeOuterRect.bottom
x1 = strokeOuterRect.centerX()
y1 = strokeOuterRect.top
}
Direction.TL_BR -> {
x0 = strokeOuterRect.left
y0 = strokeOuterRect.top
x1 = strokeOuterRect.right
y1 = strokeOuterRect.bottom
}
Direction.TR_BL -> {
x0 = strokeOuterRect.right
y0 = strokeOuterRect.top
x1 = strokeOuterRect.left
y1 = strokeOuterRect.bottom
}
Direction.BR_TL -> {
x0 = strokeOuterRect.right
y0 = strokeOuterRect.bottom
x1 = strokeOuterRect.left
y1 = strokeOuterRect.top
}
Direction.BL_TR -> {
x0 = strokeOuterRect.left
y0 = strokeOuterRect.bottom
x1 = strokeOuterRect.right
y1 = strokeOuterRect.top
}
}
strokePaint.shader = LinearGradient(
x0,
y0,
x1,
y1,
strokeStartColor,
strokeEndColor,
Shader.TileMode.MIRROR
)
canvas.drawPath(path, strokePaint)
// filling the shape
when (fillGradientDirection) {
Direction.LEFT_RIGHT -> {
x0 = fillRect.left
y0 = fillRect.centerY()
x1 = fillRect.right
y1 = fillRect.centerY()
}
Direction.TOP_BOTTOM -> {
x0 = fillRect.centerX()
y0 = fillRect.top
x1 = fillRect.centerX()
y1 = fillRect.bottom
}
Direction.RIGHT_LEFT -> {
x0 = fillRect.right
y0 = fillRect.centerY()
x1 = fillRect.left
y1 = fillRect.centerY()
}
Direction.BOTTOM_TOP -> {
x0 = fillRect.centerX()
y0 = fillRect.bottom
x1 = fillRect.centerX()
y1 = fillRect.top
}
Direction.TL_BR -> {
x0 = fillRect.left
y0 = fillRect.top
x1 = fillRect.right
y1 = fillRect.bottom
}
Direction.TR_BL -> {
x0 = fillRect.right
y0 = fillRect.top
x1 = fillRect.left
y1 = fillRect.bottom
}
Direction.BR_TL -> {
x0 = fillRect.right
y0 = fillRect.bottom
x1 = fillRect.left
y1 = fillRect.top
}
Direction.BL_TR -> {
x0 = fillRect.left
y0 = fillRect.bottom
x1 = fillRect.right
y1 = fillRect.top
}
}
fillPaint.shader = LinearGradient(
x0,
y0,
x1,
y1,
fillStartColor,
fillEndColor,
Shader.TileMode.MIRROR
)
canvas.drawRoundRect(
fillRect,
radius,
radius,
fillPaint
)
}
override fun setAlpha(alpha: Int) {
strokePaint.alpha = alpha
fillPaint.alpha = alpha
invalidateSelf()
}
override fun setColorFilter(colorFilter: ColorFilter?) {
throw RuntimeException("Color filter cannot be set to this Drawable")
}
override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}
fun setStrokeColors(startColor: Int, endColor: Int) {
this.strokeStartColor = startColor
this.strokeEndColor = endColor
invalidateSelf()
}
fun setFillColors(startColor: Int, endColor: Int) {
this.fillStartColor = startColor
this.fillEndColor = endColor
invalidateSelf()
}
fun setRadius(radius: Float) {
this.radius = radius
invalidateSelf()
}
fun setStrokeWidth(width: Float) {
this.strokeWidth = width
invalidateSelf()
}
}
That class adds the following features:
Upvotes: 1
Reputation: 3906
I have tried something for you..
Use mRect.set
to set path and mPath.addRoundRect
add rectangle.Use setShader
for strock purpose link
Drawable class:
public class CustomDrawable extends Drawable {
Paint mPaint;
int startColor, endColor, mBorderWidth, mBorderRadius;
RectF mRect;
Path mPath;
public CustomDrawable(int startColor, int endColor, int borderWidth, int borderRadius) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
mPath = new Path();
mPath.setFillType(Path.FillType.EVEN_ODD);
mRect = new RectF();
this.startColor = startColor;
this.endColor = endColor;
mBorderWidth = borderWidth;
mBorderRadius = borderRadius;
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mPath.reset();
// out rect
mRect.set(bounds.left + mBorderWidth, bounds.top + mBorderWidth, bounds.right - mBorderWidth, bounds.bottom - mBorderWidth);
mPath.addRoundRect(mRect, mBorderRadius, mBorderRadius, Path.Direction.CW);
// inner rect
mRect.set(bounds.left + 20, bounds.top + 20, bounds.right - 20, bounds.bottom - 20);
mPath.addRoundRect(mRect, mBorderRadius, mBorderRadius, Path.Direction.CW);
}
@Override
public void draw(@NonNull Canvas canvas) {
// kind of strock
mPaint.setShader(new LinearGradient(0, 0, 0, 100, startColor, endColor, Shader.TileMode.MIRROR));
canvas.drawPath(mPath, mPaint);
}
@Override
public void setAlpha(int alpha) { mPaint.setAlpha(alpha);}
@Override
public void setColorFilter(@Nullable ColorFilter colorFilter) {mPaint.setColorFilter(colorFilter);}
@Override
public int getOpacity() {return PixelFormat.TRANSLUCENT;}
}
Main :
Button but = ((Button)findViewById(R.id.but));
but.setBackground(new CustomDrawable(Color.parseColor("#FD659B"),
Color.parseColor("#F76E63"),
but.getPaddingLeft(), 100));
layout :
<Button
android:id="@+id/but"
android:layout_width="300dp"
android:layout_height="80dp"
android:background="@android:color/transparent"
android:layout_centerInParent="true"
android:text="Signin"/>
Upvotes: 4
Reputation: 1369
I don't think we can set gradient for Stroke but we can hack for the same using layer list.
you can create below drawable xml with the gradient you wanted
<item>
<shape android:shape="rectangle">
<!-- gradient for the stroke is set below -->
<gradient
android:angle="180"
android:startColor="#555994"
android:endColor="#b5b6d2"
android:type="linear" />
<corners android:radius="4dp"></corners>
</shape>
</item>
<!-- stroke width has to be adjusted by setting left, right , top and bottom -->
<item android:left="4dp" android:right="4dp"
android:top="4dp" android:bottom="4dp">
<shape
android:shape="rectangle">
<solid android:color="@android:color/white"/>
</shape>
</item>
you can set the above drawable in layout/programmatically
button.setBackgroundResource(R.drawable.button_background);
Upvotes: -1