learner
learner

Reputation: 11800

Android shape border with gradient

I want to create a border for a linearLayout. So I decide to create a shape. I want the border to have a gradient. The following is not doing it. It fills the rectangle and then creates a stroke. I don't want a filled rectangle, just the stroke. And I want to apply the gradient to the stroke.

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <gradient
        android:angle="360"
        android:centerColor="#e95a22"
        android:endColor="#ff00b5"
        android:gradientRadius="360"
        android:startColor="#006386"
        android:type="sweep" />

    <stroke
        android:width="2dp"
        android:color="#ff207d94" />

</shape>

Upvotes: 51

Views: 82086

Answers (6)

Chinmay
Chinmay

Reputation: 494

I think the gradient borders with rounded corners are not possible with just XML. You have to build a custom view to achieve this.

Here is an example that I have build with which you can set gradient borders and custom fill colors.

enter image description here

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.PorterDuff
import android.graphics.PorterDuffXfermode
import android.graphics.RectF
import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
import com.example.helper.ui.utils.dpToPx

class GradientViewWithBorders : View {

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var porterDuffXfermode_SRC_OVER = PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
    private var viewWidth = 0
    private var viewHeight = 0

    var startColor = Color.parseColor("#FFFFFF")
    var endColor = Color.parseColor("#50FFFFFF")

    var startFillColor = Color.parseColor("#50FFFFFF")

    var endFillColor = Color.parseColor("#00FFFFFF")

    var lineStrokeWidth = 1f

    val strokeGradient: LinearGradient by lazy {
        LinearGradient(
            0f,
            0f,
            this.viewWidth.toFloat(),
            this.viewHeight.toFloat(),
            startColor,
            endColor,
            Shader.TileMode.CLAMP
        )
    }


    val fillGradient: LinearGradient by lazy {
        LinearGradient(
            0f,
            0f,
            this.viewWidth.toFloat(),
            this.viewHeight.toFloat(),
            startFillColor,
            endFillColor,
            Shader.TileMode.CLAMP
        )
    }

    var rect = RectF()
    val corners = floatArrayOf(
        0f, 0f,  // Top, left in px
        0f, 0f,  // Top, right in px
        context.dpToPx(12f), context.dpToPx(12f),  // Bottom, right in px
        context.dpToPx(12f), context.dpToPx(12f) // Bottom,left in px
    )

    val outlinePath = Path()
    val innerPath = Path()


    constructor(context: Context) : super(context)

    constructor(context: Context, attributeSet: AttributeSet?) : super(context, attributeSet)

    constructor(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attributeSet,
        defStyleAttr
    )


    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)

        this.viewWidth = w
        this.viewHeight = h

    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        paint.apply {
            style = Paint.Style.STROKE
            maskFilter = null
            shader = strokeGradient
            xfermode = porterDuffXfermode_SRC_OVER
            strokeWidth = context.dpToPx(lineStrokeWidth)
        }

        rect.set(
            context.dpToPx(lineStrokeWidth) + paddingLeft,
            context.dpToPx(lineStrokeWidth) + paddingTop,
            viewWidth.toFloat() - context.dpToPx(lineStrokeWidth) - paddingRight,
            viewHeight.toFloat() - context.dpToPx(lineStrokeWidth) - paddingBottom
        )


        outlinePath.addRoundRect(rect, corners, Path.Direction.CW);
        canvas.drawPath(outlinePath, paint)





        paint.apply {
            style = Paint.Style.FILL
            maskFilter = null
            shader = fillGradient
            xfermode = porterDuffXfermode_SRC_OVER
            strokeWidth = 0f
        }


        rect.set(
            context.dpToPx(lineStrokeWidth) + paddingLeft + context.dpToPx(0.5f),
            context.dpToPx(lineStrokeWidth) + paddingTop + context.dpToPx(0.5f),
            viewWidth.toFloat() - context.dpToPx(lineStrokeWidth) - paddingRight - context.dpToPx(
                0.5f
            ),
            viewHeight.toFloat() - context.dpToPx(lineStrokeWidth) - paddingBottom - context.dpToPx(
                0.5f
            )
        )


        innerPath.addRoundRect(rect, corners, Path.Direction.CW);

        canvas.drawPath(innerPath, paint)

    }
}

Upvotes: 1

Shivam Narula
Shivam Narula

Reputation: 47

This would be the appropriate solution to what you wanna do. It includes gradient in stroke as well as a gradient in fill colour.

<?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item>
            <shape >
                <gradient
                    android:startColor="#2196F3"
                    android:endColor="#673AB7"
                    android:angle="270" />
                <stroke
                    android:width="0dp"
                    android:color="@color/transparentColor" />
                <corners
                    android:radius="8dp" />
                <padding
                    android:left="2dp"
                    android:right="2dp"
                    android:top="2dp"
                    android:bottom="2dp" />
            </shape>
        </item>
        <item>
            <shape android:shape="rectangle">

            </shape>
        </item>
        <item android:top="0dp">
            <shape>
                <gradient
                    android:startColor="#FBB100"
                    android:endColor="#FF9900"
                    android:angle="270"/>

                <corners
                    android:radius="8dp" />
            </shape>
        </item>
    </layer-list>

Upvotes: 3

Johnyoat
Johnyoat

Reputation: 456

This extra source should fix your problem

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <gradient
                android:angle="360"
                android:centerColor="#e95a22"
                android:endColor="#ff00b5"
                android:gradientRadius="360"
                android:startColor="#006386"
                android:type="sweep" />
            <size android:height="170dp"
                android:width="170dp"/>
        </shape>
    </item>
    <item android:top="2dp" android:bottom="2dp" android:right="2dp" android:left="2dp">
        <shape android:shape="rectangle">
            <size android:width="140dp"
                android:height="140dp"/>
            <solid android:color="@color/colorAccent"/>
            <solid android:color="@color/white"/>
        </shape>
    </item>
</layer-list>

Upvotes: 3

Distra
Distra

Reputation: 2470

since the accepted answer didn't work exactly as i wanted it to work for me, i'll post my solution too, maybe it helps someone else :)

<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

<item>
<!-- create gradient you want to use with the angle you want to use -->
    <shape android:shape="rectangle" >
        <gradient
            android:angle="0"
            android:centerColor="@android:color/holo_blue_bright"
            android:endColor="@android:color/holo_red_light"
            android:startColor="@android:color/holo_green_light" />

    </shape>
</item>
<!-- create the stroke for top, left, bottom and right with the dp you want -->
<item
    android:bottom="2dp"
    android:left="2dp"
    android:right="2dp"
    android:top="2dp">
    <shape android:shape="rectangle" >
        <!-- fill the inside in the color you want (could also be a gradient again if you want to, just change solid to gradient and enter angle, start, maybe center, and end color) -->
        <solid android:color="#fff" />
    </shape>
</item>

</layer-list>

Upvotes: 48

Alon Kogan
Alon Kogan

Reputation: 3418

This will create a layout with top border of 2dp. just set it as a background to your layout

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <gradient
                android:startColor="#4fc949"
                android:centerColor="#0c87c5"
                android:endColor="#b4ec51"
                android:angle="180" />
        </shape>
    </item>
    <item android:top="2dp">
        <shape android:shape="rectangle">
            <solid android:color="@color/background_color"/>
        </shape>
    </item>
</layer-list>

Upvotes: 6

vipul mittal
vipul mittal

Reputation: 17401

try something like this:

<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item>
        <shape android:shape="rectangle" >
            <gradient
                android:angle="360"
                android:centerColor="#e95a22"
                android:endColor="#ff00b5"
                android:gradientRadius="360"
                android:startColor="#006386"
                android:type="sweep" />

            <stroke
                android:width="2dp"
                android:color="#ff207d94" />
        </shape>
    </item>
    <item
        android:bottom="2dp"
        android:left="2dp"
        android:right="2dp"
        android:top="2dp">
        <shape android:shape="rectangle" >
            <solid android:color="#fff" />
        </shape>
    </item>

</layer-list>

Upvotes: 60

Related Questions