最近在项目中发现好多Button背景颜色相同,但圆角大小不等的Button,这样就得写一大堆的shape或者selector,不便于管理及后期维护,于是乎变想能不能写一个支持边框、圆角、渐变色、透明度的万用Button呢。为了能够兼容button自带的属性,当然继承自AppCompatButton是最好的,剩下的就需要考虑selector各状态在我们自定义Button中怎么获取与渲染了。最开始想到,自己draw?不过这样有点low,需要我们处理一大堆的状态,譬如:state_pressed、state_enabled...

那有没有更好的实现方式呢?我们把这些状态交由系统管理呢?在一顿寻找后,发现还真有呢,真是踏破铁鞋无觅处,得来全不费工夫--------GradientDrawable,没错就是它,一个Drawable的子类。我们看看它的描叙:

A Drawable with a color gradient for buttons, backgrounds, etc.

并且通过查看它提供的相应方法,它不仅能替我们管理好各种state,也支持边框绘制、圆角设置,渐变色当然更不用说了,看它的名字就知道啦。好了废话就不多说了,下面就是GradientButton的代码实现过程:

const val TOP_BOTTOM = 0

const val TR_BL = 1

const val RIGHT_LEFT = 2

const val BR_TL = 3

const val BOTTOM_TOP = 4

const val BL_TR = 5

const val LEFT_RIGHT = 6

const val TL_BR = 7

class GradientButton(context: Context, attrs: AttributeSet? = null) :

AppCompatButton(context, attrs, android.R.attr.borderlessButtonStyle) {

@IntDef(TOP_BOTTOM, TR_BL, RIGHT_LEFT, BR_TL, BOTTOM_TOP, BL_TR, LEFT_RIGHT, TL_BR)

@kotlin.annotation.Retention(AnnotationRetention.SOURCE)

annotation class Orientation

private val radii by lazy { FloatArray(8) }

private var mBackgroundDrawable: GradientButtonDrawable? = null

private var mPaddingLeft = 0.0f

private var mPaddingTop = 0.0f

private var mPaddingRight = 0.0f

private var mPaddingBottom = 0.0f

private var mMinWidth = 0

private var mMinHeight = 0

init {

val stateListDrawable = StateListDrawable()

attrs?.also { it ->

context.obtainStyledAttributes(it, R.styleable.GradientButton).apply {

val borderColorStateList = getColorStateList(R.styleable.GradientButton_border_color)

val borderWidth = getDimension(R.styleable.GradientButton_border_width, 0.0f)

val isRadiusAdjustBounds = getBoolean(R.styleable.GradientButton_is_radius_adjust_bounds, false)

val radius = getDimension(R.styleable.GradientButton_all_radius, 0.0f)

val topLeftRadius = getDimension(R.styleable.GradientButton_top_left_radius, 0.0f)

val topRightRadius = getDimension(R.styleable.GradientButton_top_right_radius, 0.0f)

val bottomLeftRadius = getDimension(R.styleable.GradientButton_bottom_left_radius, 0.0f)

val bottomRightRadius = getDimension(R.styleable.GradientButton_bottom_right_radius, 0.0f)

val backgroundColorStateList = getColorStateList(R.styleable.GradientButton_background_color)

val orientation = getInt(R.styleable.GradientButton_orientation, LEFT_RIGHT)

val startBackgroundColorStateList =

getColorStateList(R.styleable.GradientButton_start_background_color)

val centerBackgroundColorStateList =

getColorStateList(R.styleable.GradientButton_center_background_color)

val endBackgroundColorStateList = getColorStateList(R.styleable.GradientButton_end_background_color)

val backgroundAlpha = getFraction(R.styleable.GradientButton_background_alpha, 1, 1, 0.0f)

val padding = getDimension(R.styleable.GradientButton_padding, -1.0f)

mPaddingLeft = (if (padding != -1.0f) {

padding

} else {

getDimension(R.styleable.GradientButton_padding_left, mPaddingLeft)

})

mPaddingTop = (if (padding != -1.0f) {

padding

} else {

getDimension(R.styleable.GradientButton_padding_top, mPaddingTop)

})

mPaddingRight = (if (padding != -1.0f) {

padding

} else {

getDimension(R.styleable.GradientButton_padding_right, mPaddingRight)

})

mPaddingBottom = (if (padding != -1.0f) {

padding

} else {

getDimension(R.styleable.GradientButton_padding_bottom, mPaddingBottom)

})

mMinWidth = getDimensionPixelSize(R.styleable.GradientButton_min_width, 0)

mMinHeight = getDimensionPixelSize(R.styleable.GradientButton_min_height, 0)

mBackgroundDrawable = createBackgroundDrawable(getOrientation(orientation))

if (borderWidth > 0.0f || backgroundColorStateList != null || (startBackgroundColorStateList != null && endBackgroundColorStateList != null)) {

setTopLeftRadius(topLeftRadius)

setTopRightRadius(topRightRadius)

setBottomLeftRadius(bottomLeftRadius)

setBottomRightRadius(bottomRightRadius)

setRadius(radius)

setBorder(borderWidth, borderColorStateList)

setBackgroundColorStateList(backgroundColorStateList)

setGradientBackgroundColorStateList(

startBackgroundColorStateList,

centerBackgroundColorStateList,

endBackgroundColorStateList

)

setBackgroundAlpha(backgroundAlpha)

mBackgroundDrawable?.setRadius(isRadiusAdjustBounds, radii)

}

recycle()

}

} ?: also {

mBackgroundDrawable = createBackgroundDrawable(getOrientation(LEFT_RIGHT))

}

mBackgroundDrawable?.also {

stateListDrawable.addState(it.state, mBackgroundDrawable)

setGradientDrawable(stateListDrawable)

}

setPadding(mPaddingLeft.toInt(), mPaddingTop.toInt(), mPaddingRight.toInt(), mPaddingBottom.toInt())

minWidth = mMinWidth

minimumWidth = mMinWidth

minHeight = mMinHeight

minimumHeight = mMinHeight

}

private fun setGradientDrawable(stateListDrawable: StateListDrawable) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

background = stateListDrawable

} else {

setBackgroundDrawable(stateListDrawable)

}

}

/**

* 设置渐变色方向

*/

fun setOrientation(@Orientation orientation: Int) {

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

mBackgroundDrawable?.orientation = getOrientation(orientation)

}

}

fun setBackgroundAlpha(@FloatRange(from = 0.0, to = MAX_VALUE) alpha: Float) {

mBackgroundDrawable?.alpha = ((1.0f - alpha) * 255).toInt()

}

/**

* 渐变色设置

*/

fun setGradientBackgroundColorStateList(

startBackgroundColorStateList: ColorStateList?,

centerBackgroundColorStateList: ColorStateList? = null,

endBackgroundColorStateList: ColorStateList?

) {

mBackgroundDrawable?.setGradientBackgroundColorStateList(

startBackgroundColorStateList,

centerBackgroundColorStateList,

endBackgroundColorStateList

)

}

fun setBackgroundColorStateList(backgroundColorStateList: ColorStateList?) {

mBackgroundDrawable?.setBackgroundColorStateList(backgroundColorStateList)

}

fun setBackgroundColorRes(@ColorRes backgroundColor: Int) {

setBackgroundColorRes(backgroundColor, backgroundColor, backgroundColor)

}

fun setBackgroundColorRes(@ColorRes startBackgroundColor: Int, @ColorRes centerBackgroundColor: Int?, @ColorRes endBackgroundColor: Int) {

val centerBackgroundColorStateList = centerBackgroundColor?.let {

ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled)), intArrayOf(context.resources.getColor(it)))

}

mBackgroundDrawable?.setGradientBackgroundColorStateList(ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled)),

intArrayOf(context.resources.getColor(startBackgroundColor))),

centerBackgroundColorStateList,

ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled)),

intArrayOf(context.resources.getColor(endBackgroundColor))))

}

private fun getOrientation(@Orientation orientation: Int): GradientDrawable.Orientation {

return when (orientation) {

TOP_BOTTOM -> GradientDrawable.Orientation.TOP_BOTTOM

TR_BL -> GradientDrawable.Orientation.TR_BL

RIGHT_LEFT -> GradientDrawable.Orientation.RIGHT_LEFT

BR_TL -> GradientDrawable.Orientation.BR_TL

BOTTOM_TOP -> GradientDrawable.Orientation.BOTTOM_TOP

BL_TR -> GradientDrawable.Orientation.BL_TR

TL_BR -> GradientDrawable.Orientation.TL_BR

else -> GradientDrawable.Orientation.LEFT_RIGHT

}

}

private fun createBackgroundDrawable(orientation: GradientDrawable.Orientation) =

GradientButtonDrawable(orientation, null)

fun setBorder(@FloatRange(from = 0.0, to = MAX_VALUE) borderWidth: Float, borderColorStateList: ColorStateList?) {

mBackgroundDrawable?.setBorder(borderWidth, borderColorStateList)

}

fun setBorder(@FloatRange(from = 0.0, to = MAX_VALUE) borderWidth: Float, @ColorRes borderColorRes: Int){

setBorder(borderWidth, ColorStateList(arrayOf(intArrayOf(android.R.attr.state_enabled)),

intArrayOf(context.resources.getColor(borderColorRes))))

}

/**

* 设置圆角自适应最小边

*/

fun setRadiusAdjustBounds(isRadiusAdjustBounds: Boolean) {

mBackgroundDrawable?.setRadius(isRadiusAdjustBounds, null)

}

fun setRadius(@FloatRange(from = 0.0, to = MAX_VALUE) radius: Float) {

if (radius > 0.0f) {

for (index in radii.indices) {

radii[index] = radius

}

mBackgroundDrawable?.setRadius(radius = radii)

}

}

fun setTopLeftRadius(@FloatRange(from = 0.0, to = MAX_VALUE) topLeftRadius: Float) {

if (topLeftRadius > 0.0f) {

radii[0] = topLeftRadius

radii[1] = topLeftRadius

mBackgroundDrawable?.setRadius(radius = radii)

}

}

fun setTopRightRadius(@FloatRange(from = 0.0, to = MAX_VALUE) topRightRadius: Float) {

if (topRightRadius > 0.0f) {

radii[2] = topRightRadius

radii[3] = topRightRadius

mBackgroundDrawable?.setRadius(radius = radii)

}

}

fun setBottomLeftRadius(@FloatRange(from = 0.0, to = MAX_VALUE) bottomLeftRadius: Float) {

if (bottomLeftRadius > 0.0f) {

radii[6] = bottomLeftRadius

radii[7] = bottomLeftRadius

mBackgroundDrawable?.setRadius(radius = radii)

}

}

fun setBottomRightRadius(@FloatRange(from = 0.0, to = MAX_VALUE) bottomRightRadius: Float) {

if (bottomRightRadius > 0.0f) {

radii[4] = bottomRightRadius

radii[5] = bottomRightRadius

mBackgroundDrawable?.setRadius(radius = radii)

}

}

}

internal class GradientButtonDrawable(orientation: Orientation = Orientation.LEFT_RIGHT, @ColorInt colors: IntArray?) : GradientDrawable(orientation, colors) {

private var mBackgroundColorStateList: ColorStateList? = null

private var mStartBackgroundColorStateList: ColorStateList? = null

private var mCenterBackgroundColorStateList: ColorStateList? = null

private var mEndBackgroundColorStateList: ColorStateList? = null

private var mBorderColorStateList: ColorStateList? = null

private var mBorderWidth = 0.0f

private var mIsRadiusAdjustBounds = false

internal fun setBackgroundColorStateList(backgroundColorStateList: ColorStateList?) {

mBackgroundColorStateList = backgroundColorStateList

mStartBackgroundColorStateList = null

mCenterBackgroundColorStateList = null

mEndBackgroundColorStateList = null

setBackgroundColor()

}

internal fun setGradientBackgroundColorStateList(startBackgroundColorStateList: ColorStateList?, centerBackgroundColorStateList: ColorStateList?, endBackgroundColorStateList: ColorStateList?) {

mBackgroundColorStateList = null

mStartBackgroundColorStateList = startBackgroundColorStateList

mCenterBackgroundColorStateList = centerBackgroundColorStateList

mEndBackgroundColorStateList = endBackgroundColorStateList

setBackgroundColor()

}

internal fun setBorder(@FloatRange(from = 0.0, to = MAX_VALUE) borderWidth: Float = 0.0f, borderColorStateList: ColorStateList?) {

mBorderWidth = borderWidth

mBorderColorStateList = borderColorStateList

setBorderColor()

}

internal fun setRadius(radiusAdjustBounds: Boolean = false, radius: FloatArray?) {

mIsRadiusAdjustBounds = radiusAdjustBounds

if (!mIsRadiusAdjustBounds) {

cornerRadii = radius

}

}

private fun getColorForState(colorStateList: ColorStateList?): Int {

return colorStateList?.getColorForState(state, 0) ?: 0

}

private fun setBackgroundColor() {

mBackgroundColorStateList?.also {

if (hasNativeStateListAPI()) {

color = mBackgroundColorStateList

} else {

setColor(getColorForState(mBackgroundColorStateList))

}

} ?: also {

if (mStartBackgroundColorStateList != null && mEndBackgroundColorStateList != null) {

val colors = IntArray(mCenterBackgroundColorStateList?.let { 3 } ?: let { 2 })

colors[0] = getColorForState(mStartBackgroundColorStateList)

mCenterBackgroundColorStateList?.also {

colors[1] = getColorForState(mCenterBackgroundColorStateList)

colors[2] = getColorForState(mEndBackgroundColorStateList)

} ?: also {

colors[1] = getColorForState(mEndBackgroundColorStateList)

}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {

setColors(colors)

} else {

setColor(colors[0])

}

}

}

}

private fun setBorderColor() {

mBorderColorStateList?.also {

if (mBorderWidth > 0.0f) {

if (hasNativeStateListAPI()) {

setStroke(mBorderWidth.toInt(), mBorderColorStateList)

} else {

setStroke(mBorderWidth.toInt(), getColorForState(mBorderColorStateList))

}

}

}

}

private fun hasNativeStateListAPI() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP

override fun onStateChange(stateSet: IntArray?): Boolean {

return super.onStateChange(stateSet).let {

if (mBorderColorStateList != null || mBackgroundColorStateList != null || mStartBackgroundColorStateList != null) {

setBorderColor()

setBackgroundColor()

true

} else {

it

}

}

}

override fun onBoundsChange(r: Rect?) {

super.onBoundsChange(r)

r?.also {

if (mIsRadiusAdjustBounds) {

cornerRadius = min(it.width() / 2.0f, it.height() / 2.0f)

}

}

}

}

就是这么简单,我们只需要提供相应的color选择器,或者背景色值即可完成我们平时需要使用一大堆shape或selector才能实现的效果,最后再看看效果图吧,正所谓无图无真相,嘿嘿

gradientbutton.png

好了,今天的收获就是这么多O(∩_∩)O

android 圆角边框边框渐变,支持边框、圆角、渐变色、透明度的GradientButton相关推荐

  1. html5 border边框颜色渐变,css边框颜色渐变

    在实际开发中,我们经常遇见边框需要背景渐变的实现要求,那么如何去实现呢,今天给大家分享依稀几种情况 1.直角的背景渐变 border渐变 button{ background:transparent; ...

  2. android 圆角边框边框渐变,Android中用shape做渐变,边框,圆角等效果

    以前没接触到shape的时候,做圆角,渐变等效果都是依赖图片效果:如果对PS不熟悉,光是做图就要花去大把时间. 废话不讲了,把总结的内容记录下来,俺们不是在写博客,算是做个云笔记吧. shape用法与 ...

  3. html圆角边框背景颜色,CSS之圆角边框渐变的实现

    注:测试浏览器版本号--chrome 75.0.3770.80:opera 60.0.3255.109:firefox 67.0:ie 11. 对于普通的边框渐变,其作用于圆角边框渐变时会覆盖掉圆角的 ...

  4. CSS3_01_圆角_边框_渐变_字体

    CSS3 是最新的 CSS 标准. 我们的 CSS3 教程向您讲解 CSS3 中的新特性. 手册说明: CSS3使用了层叠样式表技术,可以对网页布局.字体.颜色.背景灯效果做出控制. css3作为cs ...

  5. java如何设置圆角边框_巧妙实现带圆角的渐变边框

    如何实现下面这个渐变的边框效果: 这个问题本身不难,实现的方法也有一些,主要是有一些细节需要注意. border-image border-image 是 CSS 规范 CSS Backgrounds ...

  6. java button 圆角_UIButton具有渐变边框和圆角

    我想要的是一个自定义UIButton,它有一个渐变边框(只是边框是渐变)和圆角 . 我几乎到了我想去的地方,但是角落有问题 . 这是我目前拥有的: 这是我的代码: override func view ...

  7. (六)Flutter 基础部件 TextView 和TextStyle Flutter 容器 装饰盒子 边框 圆角 阴影 形状 渐变 背景图像

    RichText:行内多样式的文字 import 'package:flutter/material.dart';class BasicDemo extends StatelessWidget {@o ...

  8. 自定义字体样式引入使用方法、文本阴影、边框阴影、(边框)圆角、渐变、理解重绘与回流、渐进增强和优雅降级的区别

    目录 1.文本阴影 text-shadow 2.边框阴影 box-shadow 3.自定义字体样式方法 引入与使用方法举例: 4.(边框)圆角 5.渐变 6.过渡  transition 7.理论知识 ...

  9. Android 对控件设置边框样式(边框颜色,圆角)和图片样式(圆角)

    1.设置边框.圆角.背景色案例 在drawable中 新建一个edge.xml文件 <?xml version="1.0" encoding="utf-8" ...

最新文章

  1. hive lock命令的使用
  2. Javascript 检查一组 radio 中的哪一个被勾选
  3. 在商业中,如何与人工智能建立共生关系?
  4. 清华计算机系上热搜!近 9 成优秀毕业生放弃留学,前 50 名 41 人留校深造......
  5. 使用 Python 进行稳定可靠的文件操作
  6. mysql数据库套件_MySQL数据库管理开发套件(EMS SQL Management Studio For MySQL)下载 v1.3.0.46170 官方版 - 比克尔下载...
  7. linux shell解析1
  8. C#中的@符号的使用
  9. 正确的CentOS系统配置
  10. Drupal 曝出代码执行高危漏洞,数百万网站受影响
  11. 用FCM函数实现模糊C均值聚类算法
  12. October cms(数据库-Mutators)
  13. App crash原因以及解决办法
  14. GCN学习:Pytorch-Geometric教程(二)
  15. Openstack Ironic Bare metal 实操
  16. 失控--阅读笔记群蜂思维
  17. OID,唯一性的标志
  18. 首屏优化,减少白屏时间
  19. linux设置网卡接受组播,linux下双网卡接收组播需要修改的参数
  20. 一个小点阵图像JPG图片做吗?

热门文章

  1. pandas无法打开.xlsx文件,xlrd.biffh.XLRDError: Excel xlsx file; not supported
  2. php 获取 table,php – 获取表对象(App_Model_TableName)作为获取结果(Zend Framework)
  3. c++实现插入和冒泡排序
  4. winpe镜像文件iso下载_精品软件:最喜爱的也是最纯净WinPE-微PE工具箱
  5. php获取页面中的指定内容,php 获取页面中指定内容的实现类
  6. java md5 密钥_Java 生成16/32位 MD5密钥串
  7. 面积积分_袁颖妍:用定理积分求平面区域面积(有代表性的9个例题)
  8. linux安装python2环境_Python基础手册 2 —— Python 环境搭建(Linux)
  9. mobi格式电子书_进阶能力 | 了解常见的电子书格式
  10. python expect模块_Python尚学堂高淇|第二季0408P119P123with上常见的异常的解决tryexcept...else结构,...