天下苦shape.xml久已,特别是遇上不靠谱的UI,圆角+背景色+边框,三个属性就能给你折腾出来几百种组合,每个组合都要写对应的shape.xml,太折腾人了!
展示
效果图
代码
/**
* 设置shape
*/
@BindingAdapter(
// 圆角(dp)
"shape_radius",
// 背景色 (支持ID/颜色值字符串)
// 设置渐变色后,背景色无效
"shape_bg_color",
// 边框宽度
"shape_border_width",
// 边框颜色 (支持ID/颜色值字符串)
"shape_border_color",
// 虚线宽度
"shape_dash_width",
// 虚线空格宽度
"shape_dash_gap",
// 渐变开始颜色 (支持ID/颜色值字符串)
"shape_gradient_start_color",
// 渐变结束颜色 (支持ID/颜色值字符串)
"shape_gradient_end_color",
/**
* 渐变方向
* 0: 左->右
* 90: 下->上
* 只能为45的倍数
* 默认 270
*/
"shape_gradient_angle",
requireAll = false
)
fun setShape(
view: View,
radius: Any? = null,
bgColor: Any? = null,
borderWidth: Any? = null,
borderColor: Any? = null,
dashWidth: Any? = null,
dashGap: Any? = null,
gradientStartColor: Any? = null,
gradientEndColor: Any? = null,
gradientAngle: Int? = null
) {
val shapeUtils = createShapeUtils(view)
//圆角
radius?.let {
shapeUtils.setCornerRadius(getSize(it).toFloat())
}
//背景色
bgColor?.let {
shapeUtils.setColor(getColor(it))
}
//边框
borderWidth?.let {
borderColor?.let { color ->
//虚线信息为空,则为实线
if (dashWidth == null || dashGap == null) {
shapeUtils.setStroke(getSize(it), getColor(color))
} else {
shapeUtils.setStroke(
getSize(it),
getColor(color),
getSize(dashWidth).toFloat(),
getSize(dashGap).toFloat()
)
}
}
}
//渐变
gradientStartColor?.let { startColor ->
gradientEndColor?.let { endColor ->
shapeUtils.setColors(intArrayOf(getColor(startColor), getColor(endColor)))
}
}
//渐变角度
gradientAngle?.let { angle ->
shapeUtils.setOrientation(angle)
}
shapeUtils.setDrawable(view)
}
/**
* 设置圆角
* 这里只设置圆角,不设置背景色,边框等
* 并且,不可以与上面方法中的"shape_radius"同时使用
*
* 根据设置逻辑,left,right,top,bottom四个会相互冲突,只能设置其中一个,并且只生效最后一个(是代码处理逻辑的最后一个,不是xml中的最后一个)
*
* 如果设置了top_left,top_right,bottom_left,bottom_right,则会忽略left,right,top,bottom
*
* top_left,top_right,bottom_left,bottom_right 设置某一个,都判定为设置了四个,其余未设置的,默认为0
*/
@BindingAdapter(
// 左侧圆角
"shape_radius_left",
// 上侧圆角
"shape_radius_top",
// 右侧圆角
"shape_radius_right",
// 下侧圆角
"shape_radius_bottom",
// 左上圆角
"shape_radius_top_left",
// 右上圆角
"shape_radius_top_right",
// 左下圆角
"shape_radius_bottom_left",
// 右下圆角
"shape_radius_bottom_right",
requireAll = false
)
fun setShapeRadius(
view: View,
radiusLeft: Any? = null,
radiusTop: Any? = null,
radiusRight: Any? = null,
radiusBottom: Any? = null,
radiusTopLeft: Any? = null,
radiusTopRight: Any? = null,
radiusBottomLeft: Any? = null,
radiusBottomRight: Any? = null
) {
val shapeUtils = createShapeUtils(view)
// 判定是否设置了四个圆角
if (radiusTopLeft != null || radiusTopRight != null || radiusBottomLeft != null || radiusBottomRight != null) {
shapeUtils.setCornerRadius(
getSize(radiusTopLeft).toFloat(),
getSize(radiusTopRight).toFloat(),
getSize(radiusBottomRight).toFloat(),
getSize(radiusBottomLeft).toFloat(),
)
} else {
// 判定是否设置了左侧圆角
if (radiusLeft != null) {
shapeUtils.setCornerRadiusLeft(getSize(radiusLeft).toFloat())
}
// 判定是否设置了上侧圆角
if (radiusTop != null) {
shapeUtils.setCornerRadiusTop(getSize(radiusTop).toFloat())
}
// 判定是否设置了右侧圆角
if (radiusRight != null) {
shapeUtils.setCornerRadiusRight(getSize(radiusRight).toFloat())
}
// 判定是否设置了下侧圆角
if (radiusBottom != null) {
shapeUtils.setCornerRadiusBottom(getSize(radiusBottom).toFloat())
}
}
shapeUtils.setDrawable(view)
}
/**
* 创建shape工具
* [ShapeUtils] 是Shape工具类,可以在代码库中获取
*/
private fun createShapeUtils(view: View): ShapeUtils {
return if (view.background is GradientDrawable) {
ShapeUtils.newShape(view.background as GradientDrawable)
} else {
ShapeUtils.newShape()
}
}
/**
* 获取颜色
*/
private fun getColor(color: Any): Int {
return if (color is Int) {
color
} else {
Color.parseColor(color.toString())
}
}
/**
* 获取尺寸
* 这里只支持dp
* [SizeUtils] 是尺寸工具类,可以在代码库中获取
*/
private fun getSize(size: Any?): Int {
return try {
SizeUtils.dp2px(size.toString().toFloat())
} catch (e: Exception) {
0
}
}
使用
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.shpe.ShapeActivity">
<TextView
shape_bg_color="@{@color/orange}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色"
android:textColor="@color/black" />
<TextView
shape_bg_color="@{@color/orange}"
shape_radius="@{20}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色 + 圆角"
android:textColor="@color/black" />
<TextView
shape_bg_color="@{@color/orange}"
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_radius="@{20}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色 + 圆角 + 边框"
android:textColor="@color/black" />
<TextView
shape_bg_color="@{@color/orange}"
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_dash_gap="@{3}"
shape_dash_width="@{5}"
shape_radius="@{20}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色 + 圆角 + 虚线边框"
android:textColor="@color/black" />
<TextView
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_dash_gap="@{3}"
shape_dash_width="@{5}"
shape_gradient_end_color="@{@color/white}"
shape_gradient_start_color="@{@color/orange}"
shape_radius="@{20}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="渐变色 + 圆角 + 虚线边框"
android:textColor="@color/black" />
<TextView
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_dash_gap="@{3}"
shape_dash_width="@{5}"
shape_gradient_angle="@{90}"
shape_gradient_end_color="@{@color/white}"
shape_gradient_start_color="@{@color/orange}"
shape_radius="@{20}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="渐变色 + 圆角 + 虚线边框 + 90度旋转"
android:textColor="@color/black" />
<TextView
shape_bg_color="@{@color/orange}"
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_dash_gap="@{3}"
shape_dash_width="@{5}"
shape_radius_top="@{8}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色 + Top圆角 + 虚线边框"
android:textColor="@color/black" />
<TextView
shape_bg_color="@{@color/orange}"
shape_border_color="@{@color/red}"
shape_border_width="@{1}"
shape_dash_gap="@{3}"
shape_dash_width="@{5}"
shape_radius_bottom_right="@{8}"
shape_radius_top_left="@{8}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="20dp"
android:layout_marginVertical="10dp"
android:gravity="center"
android:paddingVertical="10dp"
android:text="背景色 + TopLeft圆角 + BottomRight圆角 + 虚线边框"
android:textColor="@color/black" />
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>
解析
实现方式是依托于DataBinding
,如果同学不喜欢使用DataBinding
,那么这篇文章就帮不到您了。
通过@BindingAdapter
注释,实现布局xml中访问Kotlin代码的能力,通过设置shape
的各种属性值,轻松实现View
的背景色、圆角、渐变色、边框的功能设置。
其中圆角的设置做了比较多的需求适配,所以单独又拆分出了一个方法。
注意:任何View都可以通过该方法实现背景Shape
的自定义
缺点
必须搭配
DataBinding
框架使用无法实时渲染,也就是在编码过程中,无法在AndroidStudio中实时看到样式,需要运行到手机