View自定义边框-Android

Android开发中最烦的事,就是不停的定义不同角度不同颜色不同样式的边框,不仅很难重复使用,也很难命名管理

现有的样式实现方式大体分为以下几类

1.图片背景代替 (缺点:图片命名难以管理,且适配会存在变形)
2.drawable (缺点:命名难以管理,难以复用)
3.自定义view(缺点:太烦)
4.第三方框架(PS: 实现思路基本还是第三种,缺点:太烦)

需求场景

  需要一个只有一个右上圆弧为3dp的红色背景
  需要一个只有一个左上圆弧为3dp的蓝色背景
  需要一个只有一个右上左上圆弧为5dp的灰色背景
  右上左上圆弧为3dp蓝色背景
  等等等等....

想实现的效果

最近学iOS,对iOS上实现这个功能的简单羡慕不已,Android想实现以下的效果

view.setRadius(strokeWidth = 10f,strokeColor = R.color.redF45265,backgroundColor = R.color.color999999,leftBottomRadius = 10,leftTopRadius = 40,rightBottomRadius = 20,rightTopRadius = 30)
view.setRadius(strokeWidth = 0.6f, strokeColor = R.color.colorCCCCCC, radius = 3)
view1.setRadius(strokeWidth = 0.6f, strokeColor = R.color.colorCCCCCC, radius = 3)
view1.setShadow(shadowColor = R.color.redF45265,shadowWidth = 4f)
效果图.jpg
代码实现

自定义一个Drawable类,在里面重写部分代码,边框这些用paint画出来(canvas小白,又更好的办法,欢迎指出)

class CustomDrawable(val view: View) : Drawable() {

   private var leftTopRadius = 0f
   private var rightTopRadius = 0f
   private var leftBottomRadius = 0f
   private var rightBottomRadius = 0f
   private var radiusArray = floatArrayOf()

   private var shadowColor: Int = R.color.colorPrimaryDark.getColor()
   private var shadowRectF = RectF()         // 阴影部分的背景颜色
   private var shadowWidth = 0f              // 阴影宽度单位为dp  0为不需要阴影

   private var strokeWidth = 0f
   private var strokeColor: Int = R.color.color999999.getColor()

   private var backGroundColor: Int = R.color.white.getColor()    // 背景颜色
   private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

   /**
    * 设置圆角
    * @param backGroundColor  背景色
    * @param leftTopRadius     左上角
    * @param leftBottomRadius  左下角
    * @param rightBottomRadius 右下角
    * @param rightTopRadius    右上角
    * @param strokeWidth 边框宽度 dp f
    * @param strokeColor 边框颜色
    */
   fun setRadius(
       backGroundColor: Int = R.color.white,
       strokeWidth: Float = 0f,
       strokeColor: Int = R.color.colorB3B3B3,
       leftTopRadius: Int = 0,
       rightTopRadius: Int = 0,
       rightBottomRadius: Int = 0,
       leftBottomRadius: Int = 0
   ) {
       clearColorFilter()
       this.leftBottomRadius = leftBottomRadius.dp2px.toFloat()
       this.leftTopRadius = leftTopRadius.dp2px.toFloat()
       this.rightTopRadius = rightTopRadius.dp2px.toFloat()
       this.rightBottomRadius = rightBottomRadius.dp2px.toFloat()
       this.strokeWidth = Screen.dp2Px(strokeWidth).toFloat()
       this.strokeColor = strokeColor.getColor()
       this.backGroundColor = backGroundColor.getColor()
   }

   /**
    * 设置阴影
    * @param shadowWidth 阴影宽度 dp f
    * @param shadowColor 阴影颜色
    */
   fun setShadow(
       shadowWidth: Float = 0f,
       shadowColor: Int = R.color.colorB3B3B3
   ) {
       this.shadowWidth = Screen.dp2Px(shadowWidth).toFloat()
       this.shadowColor = shadowColor.getColor()
   }

   fun invalidate() {
       invalidateSelf()
   }

   override fun draw(canvas: Canvas) {
       getRadius()
       setDraw(canvas)
   }

   private fun setDraw(canvas: Canvas) {
       paint.color = strokeColor
       paint.isAntiAlias = true
       paint.style = Paint.Style.STROKE
       paint.strokeWidth = strokeWidth + if (backGroundColor != strokeColor) strokeWidth else 0f
       paint.setShadowLayer(shadowWidth, 0f, 0f, shadowColor)

       val path = Path()
       path.addRoundRect(shadowRectF, radiusArray, Path.Direction.CW)
       canvas.drawPath(path, paint)

       // 渲染内部
       paint.reset()
       paint.color = backGroundColor
       paint.isAntiAlias = true
       paint.style = Paint.Style.FILL
       paint.strokeWidth = strokeWidth

       path.reset()
       path.addRoundRect(shadowRectF, radiusArray, Path.Direction.CW)
       canvas.drawPath(path, paint)
   }

   /**
    * 计算圆角等值
    */
   private fun getRadius() {

       radiusArray = floatArrayOf(
           leftTopRadius, leftTopRadius,
           rightTopRadius, rightTopRadius,
           rightBottomRadius, rightBottomRadius,
           leftBottomRadius, leftBottomRadius
       )

       shadowRectF = RectF(
           shadowWidth + strokeWidth,
           shadowWidth + strokeWidth,
           view.width - shadowWidth - strokeWidth,
           view.height - shadowWidth - strokeWidth
       )

   }

   override fun setAlpha(alpha: Int) {
   }

   override fun setColorFilter(colorFilter: ColorFilter?) {
   }

   override fun getOpacity(): Int {
       return PixelFormat.TRANSLUCENT
   }

}

至此自定义部分已经完成了,接下来看调用

fun View.setShadow(
    shadowWidth: Float = 0f,
    shadowColor: Int = R.color.colorB3B3B3
) {

   if (this is CustomImageView) {
        setImageViewShadow(shadowWidth,shadowColor)
        return
    }

    var newDrawable: CustomDrawable? = null
    // 如果类型是自定义类型drawable 直接修改
    if (backgroundDrawable != null && backgroundDrawable is CustomDrawable) {
        newDrawable = backgroundDrawable as CustomDrawable
    }
    
    // drawable类型不是自定义的CustomDrawable类型,则修改为CustomDrawable
    if (newDrawable == null) {
        newDrawable = CustomDrawable(this)
    }
    
    newDrawable.setShadow(shadowWidth, shadowColor)
    newDrawable.invalidate()
    background = newDrawable
}


fun View.setRadius(
    backgroundColor: Int = R.color.white,
    strokeWidth: Float = 0f,
    strokeColor: Int = R.color.colorB3B3B3,
    leftTopRadius: Int = 0,
    rightTopRadius: Int = 0,
    leftBottomRadius: Int = 0,
    rightBottomRadius: Int = 0
) {

 if (this is CustomImageView) {
        setImageViewRadius(
            backGroundColor = backgroundColor,
            strokeWidth = strokeWidth,
            strokeColor = strokeColor,
            leftTopRadius = leftTopRadius,
            rightTopRadius = rightTopRadius,
            leftBottomRadius = leftBottomRadius,
            rightBottomRadius = rightBottomRadius
        )
        return
    }

    var newDrawable: CustomDrawable? = null
    val oldDrawable = backgroundDrawable

    if (backgroundDrawable != null && oldDrawable is CustomDrawable) {
        newDrawable = oldDrawable
    }

    if (newDrawable == null) {
        newDrawable = CustomDrawable(this)
    }

    newDrawable.setRadius(
        backgroundColor,
        strokeWidth,
        strokeColor,
        leftTopRadius,
        rightTopRadius,
        rightBottomRadius,
        leftBottomRadius
    )
    newDrawable.invalidate()

    // 重新渲染 防止颜色叠加
    requestLayout()
    background = newDrawable
}

/**
 * 添加圆角
 */
fun View.setRadius(
    backgroundColor: Int = R.color.white,
    strokeWidth: Float = 0f,
    strokeColor: Int = backgroundColor,
    radius: Int = 0
) {

   // iamgeView 图层比较特殊,是浮在drawable之上,需要特殊处理 也可以直接调用
   if (this is CustomImageView) {
        setImageViewRadius(
            backGroundColor = backgroundColor,
            strokeWidth = strokeWidth,
            strokeColor = strokeColor,
            leftTopRadius = leftTopRadius,
            rightTopRadius = rightTopRadius,
            leftBottomRadius = leftBottomRadius,
            rightBottomRadius = rightBottomRadius
        )
        return
    }

    setRadius(
        backgroundColor = backgroundColor,
        strokeWidth = strokeWidth,
        strokeColor = strokeColor,
        leftTopRadius = radius,
        rightTopRadius = radius,
        leftBottomRadius = radius,
        rightBottomRadius = radius
    )
}

自定义边框各圆角的imageView


class CustomImageView: ImageView {

    private var leftTopRadius = 0f
    private var rightTopRadius = 0f
    private var leftBottomRadius = 0f
    private var rightBottomRadius = 0f
    private var radiusArray = floatArrayOf()

    private var shadowColor: Int = R.color.colorPrimaryDark.getColor()
    private var shadowRectF = RectF()         // 阴影部分的背景颜色
    private var shadowWidth = 0f              // 阴影宽度单位为dp  0为不需要阴影

    private var strokeWidth = 0f
    private var strokeColor: Int = R.color.color999999.getColor()

    private var backGroundColor: Int = R.color.white.getColor()    // 背景颜色
    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
    
    constructor(context: Context) : super(context)
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)

    /**
     * 设置圆角
     * @param backGroundColor  背景色
     * @param leftTopRadius     左上角
     * @param leftBottomRadius  左下角
     * @param rightBottomRadius 右下角
     * @param rightTopRadius    右上角
     * @param strokeWidth 边框宽度 dp f
     * @param strokeColor 边框颜色
     */
    fun setImageViewRadius(
        backGroundColor: Int = R.color.white,
        strokeWidth: Float = 0f,
        strokeColor: Int = R.color.colorB3B3B3,
        leftTopRadius: Int = 0,
        rightTopRadius: Int = 0,
        rightBottomRadius: Int = 0,
        leftBottomRadius: Int = 0
    ) {
        clearColorFilter()
        this.leftBottomRadius = leftBottomRadius.dp2px.toFloat()
        this.leftTopRadius = leftTopRadius.dp2px.toFloat()
        this.rightTopRadius = rightTopRadius.dp2px.toFloat()
        this.rightBottomRadius = rightBottomRadius.dp2px.toFloat()
        this.strokeWidth = Screen.dp2Px(strokeWidth).toFloat()
        this.strokeColor = strokeColor.getColor()
        this.backGroundColor = backGroundColor.getColor()
    }

    /**
     * 设置阴影
     * @param shadowWidth 阴影宽度 dp f
     * @param shadowColor 阴影颜色
     */
    fun setImageViewShadow(
        shadowWidth: Float = 0f,
        shadowColor: Int = R.color.colorB3B3B3
    ) {
        this.shadowWidth = Screen.dp2Px(shadowWidth).toFloat()
        this.shadowColor = shadowColor.getColor()
    }

    override fun onDraw(canvas: Canvas?) {
        getRadius()
        setDraw(canvas)
        super.onDraw(canvas)
    }

    private fun setDraw(canvas: Canvas?) {
        paint.color = strokeColor
        paint.isAntiAlias = true
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = strokeWidth + if (backGroundColor != strokeColor) strokeWidth else 0f
        paint.setShadowLayer(shadowWidth, 0f, 0f, shadowColor)

        val path = Path()
        path.addRoundRect(shadowRectF, radiusArray, Path.Direction.CW)
        canvas?.drawPath(path, paint)
        canvas?.clipPath(path)
    }

    /**
     * 计算圆角等值
     */
    private fun getRadius() {
        radiusArray = floatArrayOf(
            leftTopRadius, leftTopRadius,
            rightTopRadius, rightTopRadius,
            rightBottomRadius, rightBottomRadius,
            leftBottomRadius, leftBottomRadius
        )
        shadowRectF = RectF(
            shadowWidth + strokeWidth,
            shadowWidth + strokeWidth,
            width - shadowWidth - strokeWidth,
            height - shadowWidth - strokeWidth
        )
    }

}
使用及效果
 
效果图.png

不创建大量的drawable,也不用为太多的背景图命名烦恼,基本能满足开发的大部分需求, 欢迎指出问题和改进。

你可能感兴趣的:(View自定义边框-Android)