写在前面

首先祝大家新年快乐,开工大吉。
最新刚换了工作,大部分精力还是放到新工作上面,所以这次还是先给大家带来一个小而实用的库:Silhouette。另外,考虑到Kotlin越来越普及,作者在开发过程中也切实感受到HTTPSKotlin相较于Jahttp 404va带来的便利,后续的IM系列文章及项目考虑用Kotlin重写,而且考虑到由于工作Kotlin业务需求过多可能出现断更的情况,所以打算一次性写完再放出来,避免大家学习不方便。
废话不多说,直接开始吧。

Silhouette是什么?

Silhouette意为“剪影”,取名并没有特别的含义,只是单纯地觉得意境较美。例如上一篇文章Shi开源ne——更简单的Android网络请求库封装的网络请求库:Shihttpclientne即意为“闪耀”,也没有特别的含义,只是作者认为开源库起名较难,特意找一些比较优美的单词。
Silhouette是一系列基于Gradient开源中国DrawableStateListDrawable封装的组件集合,主要用开源阅读于实现在Android LayouGitt开源是什么意思 XML中直https和http的区别接支持Shape/Seleckotlin和javator等功能。
我们都知道在Android开发中,不同的TextViewButton各种样式(形状、背景色kotlin现在不火了、描边、圆角、渐变等)的传统实现方式是在drawable文件夹中编写各种shape/selector等文件,这种方式至少会存在以下几种弊端:

  1. shape/selector文件过多,项目体积增大;
  2. shape/selector文件命名困难,命名规范时开源阅读app下载安装往往会存在功能重复的文件;
  3. 功能存在局限开源代码网站github性:例如gradient渐变色。传统shape方式只支持三种颜色过渡(startColor/centerColor/endhttp 302Color)HTTPS,如果设计稿存在四种以上颜色渐变,shape gradient无能为力。再比如TextView在常态和按下态需要同时改变背景色及文字颜色时,传统方式开源矿工只能在代码中动HTTP态设置等。
  4. 开发效开源率低;
  5. 难以维护等;

综上所述,我们迫切需要一个库来解决以上问题,Silhouette正具备这些能力。接下来,我们来具体看看S开源众包ilhouette能做什么吧。gitlab

Silhouette能做什么?

上面说到Silhogithub开放私库uette是一系http 302列组件集合,具体包含以下组件:

  • SleTextButton
    基于AppCompatTextView封装;
    具备定义各种样式(形状、背景色、描边、圆角、渐变等)的能力 ;
    具备不同状态(常态、按下态、不可点http://www.baidu.com击态)下文字颜色指定等。

  • SleImageButton
    基于ShapeableImagkotlin怎么读eView封装;
    通过指定sle_gitiib_type属性使ImageView支持按下态遮罩层、透明度改变、自定义图片,同时支持CheckBox功能;
    通过指定sle_ib_style属性使ImageView支持Normal、圆角、圆形等形状。

  • SleConstraintLayout
    基于Ckotlin现在不火了onstraintLayoutgithub中文官网网页封装;
    http 302备定开源义各种样式(形状、背景色、描边、圆角、渐变等)的功能。

  • SleRelativeLayout
    基于RelativeLayout封装;
    具备http代理定义各种样式(形状、背景色、github中文官网网页描边、圆角、渐变等开源软件)的功能。

  • SleLinearLayout
    基于LinearLayo开源中国ut封装;
    具备定义各种样式(形状、背景色、描边、圆角、渐变等)的功能。

  • SleFrameLayout
    基于FrameLayout封装;
    具备定义各种样式(形状、https认证背景色、描kotlin语言边、圆角、渐变等)的功能。

设计、封装思路及原理

  • 项目结构
    com.freddy.silhouette

    • config(配置相关,存放全局注解及公共常量、默认值等)
    • extkotlin扩展相关,可选择用kotlin和java或不用)
    • utils(工具类相关,可选择用或不用)
    • widgehttp 500t(控件相关)
      • butt开源阅读on
      • layout

    由此可见,项目结构非常简单,所以Silhouette也是一个比较轻量级的库。github是干什么的

  • 封装思路及原理
    开源节流于该库非常简单,实际上就是根据Shape/Selector进行自定义属开源节流性,从而利用GradientDrawGitableStateListDrawable提供的APIgithub中文官网网页进行封装,不存在什么难度,在此就不展开讲了。

    下面贴一下代码片段,基本上几个组件的实现原理都大同小异,都是利用GradientDrawahttp协议bleStateListDrawablehttps和http的区别现组件的ShapeSelector功能:

private fun init() {
    val normalDrawable =
        getDrawable(normalBackgroundColor, normalStrokeColor, normalGradientColors)
    var pressedDrawable: GradientDrawable? = null
    var disabledDrawable: GradientDrawable? = null
    var selectedDrawable: GradientDrawable? = null
    when (type) {
        TYPE_MASK -> {
            pressedDrawable = getDrawable(
                normalBackgroundColor,
                normalStrokeColor,
                normalGradientColors
            ).apply {
                colorFilter =
                    PorterDuffColorFilter(maskBackgroundColor, PorterDuff.Mode.SRC_ATOP)
            }
            disabledDrawable =
                getDrawable(disabledBackgroundColor, disabledBackgroundColor)
        }
        TYPE_SELECTOR -> {
            pressedDrawable =
                getDrawable(pressedBackgroundColor, pressedStrokeColor, pressedGradientColors)
            disabledDrawable = getDrawable(
                disabledBackgroundColor,
                disabledStrokeColor,
                disabledGradientColors
            )
        }
    }
    selectedDrawable = getDrawable(
        selectedBackgroundColor,
        selectedStrokeColor,
        selectedGradientColors
    )
    setTextColor(normalTextColor)
    background = StateListDrawable().apply {
        if (type != TYPE_NONE) {
            addState(intArrayOf(android.R.attr.state_pressed), pressedDrawable)
        }
        addState(intArrayOf(-android.R.attr.state_enabled), disabledDrawable)
        addState(intArrayOf(android.R.attr.state_selected), selectedDrawable)
        addState(intArrayOf(), normalDrawable)
    }
    setOnTouchListener(this)
}
private fun getDrawable(
    backgroundColor: Int,
    strokeColor: Int,
    gradientColors: IntArray? = null
): GradientDrawable {
    // 背景色相关
    val drawable = GradientDrawable()
    setupColor(drawable, backgroundColor)
    // 形状相关
    (drawable.mutate() as GradientDrawable).shape = shape
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        drawable.innerRadius = innerRadius
        if (innerRadiusRatio > 0f) {
            drawable.innerRadiusRatio = innerRadiusRatio
        }
        drawable.thickness = thickness
        if (thicknessRatio > 0f) {
            drawable.thicknessRatio = thicknessRatio
        }
    }
    // 描边相关
    if (strokeColor != 0) {
        (drawable.mutate() as GradientDrawable).setStroke(
            strokeWidth,
            strokeColor,
            dashWidth,
            dashGap
        )
    }
    // 圆角相关
    setupCornersRadius(
        drawable,
        cornersRadius,
        cornersTopLeftRadius,
        cornersTopRightRadius,
        cornersBottomRightRadius,
        cornersBottomLeftRadius
    )
    // 渐变相关
    (drawable.mutate() as GradientDrawable).gradientType = gradientType
    if (gradientCenterX != 0.0f || gradientCenterY != 0.0f) {
        (drawable.mutate() as GradientDrawable).setGradientCenter(
            gradientCenterX,
            gradientCenterY
        )
    }
    gradientColors?.let { colors ->
        (drawable.mutate() as GradientDrawable).colors = colors
    }
    var orientation: GradientDrawable.Orientation? = null
    when (gradientOrientation) {
        GRADIENT_ORIENTATION_TOP_BOTTOM -> {
            orientation = GradientDrawable.Orientation.TOP_BOTTOM
        }
        GRADIENT_ORIENTATION_TR_BL -> {
            orientation = GradientDrawable.Orientation.TR_BL
        }
        GRADIENT_ORIENTATION_RIGHT_LEFT -> {
            orientation = GradientDrawable.Orientation.RIGHT_LEFT
        }
        GRADIENT_ORIENTATION_BR_TL -> {
            orientation = GradientDrawable.Orientation.BR_TL
        }
        GRADIENT_ORIENTATION_BOTTOM_TOP -> {
            orientation = GradientDrawable.Orientation.BOTTOM_TOP
        }
        GRADIENT_ORIENTATION_BL_TR -> {
            orientation = GradientDrawable.Orientation.BL_TR
        }
        GRADIENT_ORIENTATION_LEFT_RIGHT -> {
            orientation = GradientDrawable.Orientation.LEFT_RIGHT
        }
        GRADIENT_ORIENTATION_TL_BR -> {
            drawable.orientation = GradientDrawable.Orientation.TL_BR
        }
    }
    orientation?.apply {
        (drawable.mutate() as GradientDrawable).orientation = this
    }
    return drawable
}

感兴趣的同学可以到官方文档了解GHTTPSradientDrawableStateListDrawable的原理。

自定义属性列表

自定义属性分为通用属性特有属性

  • 通用属性

    • 类型
    属性名称 类型 开源中国 备注
    sle_type enumgiti轮胎 类型
    mask:遮罩
    selector:自定义样式
    none:无
    默认值:mask
    默认的mask为90%透明度黑色,可通过sle_mhttpwatchaskBackgroundColors属性设置
    若不指定为selector,则自定义样式无效
    • 形状相关
    属性名称 类型 说明 备注
    sle_shape enum 形状
    rectangle:矩形
    ova开源中国l:椭圆形
    lingitie:线性形状
    ring:环形
    默认值:rectangle
    sle_innerRadius dimensionkotlin语言|reference 尺寸,内环的半径 shape=”ring”可用
    sle_innerRadiusRatio float 以环的宽度比率来表示内环的半径httpwatch shape=”httpclientring”可用
    sle_thickness dimension|reference 尺寸,环的厚度 shape=”ringgithub中文官网网页“可用
    sle_thicknessRatio float 以环的https安全问题宽度比率来表示环的厚度 shape=”ring”可用
    • 背景色相关
    属性http 404名称 类型 说明 备注
    sle_normalBackgroundColor color|reference github开放私库态背景颜色 /
    sle_pressedBackgroundColor color|refegiteerence 按下态背开源节流什么意思景颜色 /
    sgitile_disabledBackgroundColor color|reference 不可点击态背景颜色 默认值:#CCCCCC
    slHTTPe_selectedBackgroundColohttp代理rgit命令 color|refegithubrence 选中态背景颜色 /
    • 描边相关
    属性名称 类型 说明 Git
    sle_normalStrokeColor cologitir|reference 常态描边颜色 /
    sle_presGitsedStrokeColor color|reference 按下态描边颜色 /
    sle_disabledSthttp 500rokeColor color|reference 不可点击态描边颜色 /
    sle_selectedStrokeColor color|reference 选中kotlin怎么读态描边颜色 /https协议
    slhttpwatche_strokeWidth dimension|reference 描边宽度 /
    sle_dashWidth dimension|reference 虚线宽度 /
    sle_dashGap dimension|reference 虚线间隔 /
    • 圆角相关
    属性名称 类型 说明 备注
    sgitlable_cornersRadius dime开源阅读nsihttp代理on|reference 开源阅读app下载安装圆角半径 /
    sle_cogiteernersTopLeftRadi开源节流us dimension|refegitlabrengitice 左上角圆角半径 /
    sle_cornersTopRightRadius dimension|reference 右上角圆角半径 /
    sle_cornehttp 500rsBottomLeftRadhttp 500ius dimension|reference 左下角圆角半径 /
    sle_cornersBotkotlin和javathttp 302omRightRadius dimension|reference 右下角圆角半径 /
    • 渐变开源矿工相关
    属性名称 类型 说明 备注
    sle_normalGradientColors reference 常态渐变背景色 支持在res/array下定开源代码网站github义数组实现多个颜色渐变
    sle_pressedGradientColors reference 按下态渐变背景色 支持在rgithub是干什么的es/array下定义数组实现多开源中国个颜色渐变
    sle_disabledGradientColors reference 不可点击态渐变背景色 支持在res/array下定义数组实现多个颜色渐变
    sle_selectedGradientColors reference 选中态渐变背景色 支持在res/array下定义数组实现多个颜色渐变
    sle_gradientOrientation enum 渐变方向
    TOP_BOTTOM:从开源是什么意思上到下
    TR_BL:从开源阅读app下载安装右上到左下
    RIGHT_LEFT:从右到左
    BR_TL:从右下到左上
    BOTTOM_TOP:从下到上
    Bhttps安全问题L_TR:从左下到右上
    LEFT_RIGHT:从左到右开源代码网站github
    TL_BR:从左上到右下
    /
    sle_gradientType enum 渐变类型
    linear:线性渐变
    radial:圆形渐变,起始颜色从gradientgithub中文官网网页CenterX、gradientCenterY点开始
    sweep:A sweeping line gradient
    /
    sle_gradient开源节流什么意思CenterX float 渐变中心放射点x坐标 注意,这里的坐giti标是整个背景的百分比的点,并github开放私库不是确切Git点,0.2就是20%的点
    sle_gradientCenterY fl开源oat 渐变中心放射点y坐标 注意,这里的坐标是整个背景的百分比的点,并不是确切点,0.2就是20%的点
    sle_gradientRadius dimension|reference 渐变半径 需要配合gradientType=radial使用,如果设置gradientType=radiahttps和http的区别l而没有设置gradientRadius,将会报错
    • 其它
    属性名称 类型 说明 备注
    sle_maskBackgroundhttp 404Color color|reference 当s开源是什么意思le_type=mask时,按钮按下状态的遮罩颜色 默认值:90%透明度黑色(#1A000000)
    sle_cancelOffset dimensiokotlin为什么流行不起来n|reference 用于解决手指移出控件区域判断为cancel的偏移量 默认值:8dp
  • 特有属性

    • SleConstraintLayout/SleRelativeLayout/SleFrameLayout/SleLinearLayout
    属性名称 类型 说明 备注kotlin现在不火了
    sle_interceptType enum 事件拦截类型
    intercept_super:return super
    intercehttps安全问题pt_true:return thttp 500rue
    intercept_false:return false
    Layout组件设置此值,可实现是否拦截事件,如果设置为intercept_true,事开源众包件将不传递到子控件,在某些场景比较实用
    • Slehttp 500TextButton
    属性名称 类型 说明 备注
    sle_norHTTPmalTextColor color|reference 常态文字颜色 /
    sle_pressedTextColor color|reference 按下态文字颜色 /
    sle_disabledTextColor cgithub是干什么的olor|referen开源阅读app下载安装ce 不可点击态文字颜色 /
    sle_selectedTextColor color|reference 选中态文字颜色 /
    • SleImageButton
    属性名称 类型 说明 备注
    sle_ib_type enum https和http的区别
    mask:图片遮罩
    alpha:图片透明度改变
    selector:自定义图片
    checkBox:CheckBox场景
    none:无
    1.指定为mask时,自定义图片资源无效;
    2.指定为alph开源节流什么意思a时,sle_pressedAlpha/sle_disabled开源代码网站githubAlpha生效;
    3.指定为selector时,sle_normalRekotlin怎么读sId/sle_pressedResId/sle_disabledhttp协议ResId生效;
    4.指定为checkBox时,sle_checkedResId/sle_http 404uncheckedResId/sle_isChecked生效;
    5.指定为nonehttps安全问题时,图片资源均不生效,圆角相关配置有效
    sle_ib_开源代码网站githubstyle enum ImageView形状
    norm开源al:普通形状
    rounded:圆角
    oval:圆形
    默认值:normal
    skotlin下载le_normalResId color|refergithub开放私库ence 常态图gitlab片资源 /
    sle_pressedResId color|reference 按下态图片资源 /
    sle_disabledResId color|reference 不可点击态图片资源 /
    sle_checkedResId color|reference 选中态checkBox图片资源 /
    sle_uncheckedResId color|reference 非选中态checkBox图片资源 /
    shttp 302le_isChecked boolean CheckBox是否选中 默认值:false
    sle_pressedAlpha float 按下态图片透明度 默认值:70%
    sle_disabledAlpha float 不可点击态图片透明度http://192.168.1.1登录 默认值:30%

使用方式

  1. 添加依赖
implementation "io.github.freddychen:silhouette:$lastest_version"

Note:最新版本可在maven central silhouette中找到。

  1. 使用

由于自定义属性太kotlin语言多,在此就不一一列举了。下面给出几种常见的场景示例,大家可以根据自定义属性表自行编写:

  • 常态

Silhouette——更方便的Shape/Selector实现方案

  • 按下态

Silhouette——更方便的Shape/Selector实现方案

以上布局代码为:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    android:gravity="center_horizontal"
    android:orientation="vertical">
    <com.freddy.silhouette.widget.button.SleTextButton
        android:id="@+id/stb_1"
        android:layout_width="match_parent"
        android:layout_height="54dp"
        android:layout_marginHorizontal="48dp"
        android:layout_marginTop="14dp"
        android:gravity="center"
        android:text="SleTextButton1"
        android:textSize="20sp"
        app:sle_cornersRadius="28dp"
        app:sle_normalBackgroundColor="#f88789"
        app:sle_normalTextColor="@color/white"
        app:sle_type="mask" />
    <com.freddy.silhouette.widget.button.SleTextButton
        android:id="@+id/stb_2"
        android:layout_width="match_parent"
        android:layout_height="54dp"
        android:layout_marginHorizontal="48dp"
        android:layout_marginTop="14dp"
        android:gravity="center"
        android:text="SleTextButton2"
        android:textSize="20sp"
        app:sle_cornersBottomRightRadius="24dp"
        app:sle_cornersTopLeftRadius="14dp"
        app:sle_normalBackgroundColor="#338899"
        app:sle_normalTextColor="@color/white"
        app:sle_pressedBackgroundColor="#aeeacd"
        app:sle_type="selector" />
    <com.freddy.silhouette.widget.button.SleTextButton
        android:id="@+id/stb_3"
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:layout_marginHorizontal="48dp"
        android:layout_marginTop="14dp"
        android:enabled="false"
        android:gravity="center"
        android:text="SleTextButton2"
        android:textSize="14sp"
        app:sle_cornersBottomRightRadius="24dp"
        app:sle_cornersTopLeftRadius="14dp"
        app:sle_normalBackgroundColor="#cc688e"
        app:sle_normalTextColor="@color/white"
        app:sle_pressedBackgroundColor="#34eeac"
        app:sle_shape="oval"
        app:sle_type="selector" />
    <com.freddy.silhouette.widget.button.SleImageButton
        android:id="@+id/sib_1"
        android:layout_width="84dp"
        android:layout_height="84dp"
        android:layout_marginTop="14dp"
        app:sle_ib_type="mask"
        app:sle_normalResId="@drawable/ic_launcher_background" />
    <com.freddy.silhouette.widget.button.SleImageButton
        android:id="@+id/sib_2"
        android:layout_width="128dp"
        android:layout_height="128dp"
        android:layout_marginTop="14dp"
        app:sle_ib_type="alpha"
        app:sle_normalResId="@drawable/ic_launcher_background" />
    <com.freddy.silhouette.widget.button.SleImageButton
        android:id="@+id/sib_3"
        android:layout_width="72dp"
        android:layout_height="72dp"
        android:layout_marginTop="14dp"
        app:sle_ib_type="selector"
        app:sle_normalResId="@mipmap/ic_launcher"
        app:sle_pressedResId="@drawable/ic_launcher_foreground" />
    <com.freddy.silhouette.widget.layout.SleConstraintLayout
        android:id="@+id/scl_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="48dp"
        android:layout_marginTop="14dp"
        android:paddingHorizontal="14dp"
        android:paddingVertical="8dp"
        app:sle_cornersRadius="10dp"
        app:sle_interceptType="intercept_super"
        app:sle_normalBackgroundColor="@color/white">
        <ImageView
            android:layout_width="72dp"
            android:layout_height="48dp"
            android:scaleType="centerCrop"
            android:src="https://juejin.im/post/7063098095969501191/@mipmap/ic_launcher_round" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="UserName"
            android:textColor="@color/black"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </com.freddy.silhouette.widget.layout.SleConstraintLayout>
    <com.freddy.silhouette.widget.layout.SleLinearLayout
        android:id="@+id/sll_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="48dp"
        android:layout_marginTop="14dp"
        android:gravity="center_vertical"
        android:paddingHorizontal="14dp"
        app:sle_type="selector"
        android:paddingVertical="8dp"
        app:sle_cornersTopRightRadius="24dp"
        app:sle_cornersBottomRightRadius="18dp"
        app:sle_interceptType="intercept_true"
        app:sle_pressedBackgroundColor="#fe9e87"
        app:sle_normalBackgroundColor="#aee949">
        <ImageView
            android:layout_width="72dp"
            android:layout_height="48dp"
            android:scaleType="centerCrop"
            android:src="https://juejin.im/post/7063098095969501191/@mipmap/ic_launcher_round" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="14dp"
            android:text="UserName"
            android:textColor="@color/black"
            android:textSize="18sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </com.freddy.silhouette.widget.layout.SleLinearLayout>
</LinearLayout>

Note:需要给组件设置setOnClickListene开源代码网站githubr才能看开源是什么意思到效果。
至于更多的功能,就让大家去试试吧,篇幅有限,就不一一列举了。有任何疑问,欢迎通过QQ群微信公众号联系我。

版本记录

版本号 修改时间 版本说明
0.0.1 2022.02.10 首次提交
0.0.2 2022.02.12 修改minSdk为19

写在最后

终于写完了,Shape/Selector在每个项目中基kotlin怎么读本都会用到,而且频率还不算低。Silhouette原理虽然简单,但确实能解决很多问题,git命令这些都是平时开发中的积累,http://www.baidu.com希望对大家能有所帮助。欢迎大家starfork,让我们为Android开发共同贡献一份力量。另外如果有疑问欢迎加入我的QQ群:1015178804,同时也欢迎大家关注我的公众号:FreddyChen,让我们共同进步和成长。

GitHub地址