Reputation: 11800
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
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.
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
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
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
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
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
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