金融行业网站建设,北京网站建设公司案例,html音乐播放器代码,wordpress必要插件那天做需求的时候#xff0c;遇到一个小功能#xff0c;建立在前人栽树#xff0c;后人乘凉的情况下#xff0c;仅用片刻就写完了#xff1b;说来惭愧#xff0c;我以前并未写过文本渐变的需求#xff0c;脑中也仅有一个shape渐变带来的大概思路#xff0c;回头来看想着… 那天做需求的时候遇到一个小功能建立在前人栽树后人乘凉的情况下仅用片刻就写完了说来惭愧我以前并未写过文本渐变的需求脑中也仅有一个shape渐变带来的大概思路回头来看想着学习一下这款自定义控件的内部实现故记录于此 很多时候通过阅读原作者源码总能为我们带来一些思考一些成长
Tip为表尊重源码中的注释声明并未做任何修改仅记录自身学习中的思考、想法 效果需求效果实现效果 基础思考开发实践项目结构使用方式 集成学习ShapeTextView 自定义控件shape_attr 自定义属性Styleable 动态属性IShapeDrawableStyleable 背景属性抽象类ITextColorStyleable 文本属性抽象类ShapeTextViewStyleable 具体实现类 BuilderShapeDrawableBuilderTextColorBuilder LinearGradientFontSpan 文本渐变核心类 效果
可以先看看效果是不是你所需要的以免浪费开发时间… 如有需要可直接 下载Demo
需求效果 渐变背景 渐变文本 实现效果
本文只针对item样式中的 右上角的双重渐变渐变背景、渐变文本实现 基础思考
如果让你实现右上角的标签你考虑了哪些实现方式
Tip右上角标签仅可能有一个只是样式、描述不同
单标签固定样式产品要求不可严格的话直接让设计切图简单便捷多标签固定样式产品要求不可严格的话当样式标签固定在 2-5个之间可以让设计切图显示逻辑处理简单便捷多标签不固定样式例如内部可能字体可能是精选、常态、盈利等等当这种场景被不确定占据时只能通过代码来进行适配
关于渐变背景因仅为一种固定渐变背景且并非本文关键故在此直接写出如果你想更详细的学习和了解shape可前往 shape保姆级手册
关于渐变位置主要有startColor、centerColor、endColor 如根据设计图的话可仅设置startColor、endColor
shape_staid_select_top_right背景shape- Demo中可能未打包
?xml version1.0 encodingutf-8?
shape xmlns:androidhttp://schemas.android.com/apk/res/androidcornersandroid:bottomLeftRadius5dpandroid:topRightRadius5dp /paddingandroid:bottom1dpandroid:leftdimen/mp_12android:rightdimen/mp_12android:top1dp /gradientandroid:angle45android:centerColor#F8E2C7android:endColor#F8E2C8android:startColor#F8E2C7 /
/shape开发实践
因为我们的文本渐变效果是直接从 ShapeView 剥离的产物所以如果不介意引入三方库的话最简单的方式肯定是直接依赖了只不过会导致项目体积更大一些毕竟有些东西我们用不到
关于 ShapeView 的集成可自行前往作者项目主页查看此处仅做部分摘要
项目结构
此处项目结构为我Demo的结构目录主要通过减少三方库的引入从而减少项目体积、包体积 使用方式
ShapeTextView 就是我们这次学习的控件现在开始一步步倒推看一下
?xml version1.0 encodingutf-8?
RelativeLayout xmlns:androidhttp://schemas.android.com/apk/res/androidxmlns:apphttp://schemas.android.com/apk/res-autoxmlns:toolshttp://schemas.android.com/toolsandroid:layout_widthmatch_parentandroid:layout_heightmatch_parentandroid:gravitycenter_horizontaltools:context.MainActivitycom.example.shapefontbg.shape.ShapeTextViewandroid:idid/tv_shapeandroid:layout_widthwrap_contentandroid:layout_heightwrap_contentandroid:layout_marginTop30dpandroid:backgrounddrawable/shape_staid_select_top_rightandroid:text我爱洗澡皮肤好好~android:textSize14spapp:shape_textEndColor#501512app:shape_textStartColor#AD5C22app:typefaceScalemedium //RelativeLayout集成学习
ShapeTextView 自定义控件
我感觉自定义控件内主要有以下几点需要总结、注意
初始化背景属性、文本属性内部通过读取typefaceScale属性设置对应字体加粗效果内部增添动态设置Text、setTextColor、TypefaceScale方法支持外部调用
package com.example.shapefontbg.shapeimport android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView
import com.example.shapefontbg.R
import com.example.shapefontbg.shape.builder.ShapeDrawableBuilder
import com.example.shapefontbg.shape.builder.TextColorBuilder
import com.example.shapefontbg.shape.styleable.ShapeTextViewStyleable/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/07/17* desc : 支持直接定义 Shape 背景的 TextView*/
class ShapeTextView JvmOverloads constructor(context: Context, attrs: AttributeSet? null, defStyleAttr: Int 0) :AppCompatTextView(context, attrs, defStyleAttr) {private val shapeDrawableBuilder: ShapeDrawableBuilderprivate val textColorBuilder: TextColorBuilder?private var typefaceScale: Floatcompanion object {private val STYLEABLE ShapeTextViewStyleable()}enum class TypefaceScale {MEDIUM, MEDIUM_SMALL, DEFAULT,}init {val typedArray context.obtainStyledAttributes(attrs, R.styleable.ShapeTextView)//初始化背景属性shapeDrawableBuilder ShapeDrawableBuilder(this, typedArray, STYLEABLE)//初始化文本属性textColorBuilder TextColorBuilder(this, typedArray, STYLEABLE)// 读取字体加粗程度val scale typedArray.getInt(R.styleable.ShapeTextView_typefaceScale, 0)typefaceScale typedArrayTypefaceScale(scale)// 资源回收typedArray.recycle()// 设置相关背景属性shapeDrawableBuilder.intoBackground()// 设置相关文本属性
// 原始部分
// if (textColorBuilder.isTextGradientColors) {
// text textColorBuilder.buildLinearGradientSpannable(text)
// } else {
// textColorBuilder.intoTextColor()
// }textColorBuilder.intoTextColor()}/*** 字体粗度* */private fun typedArrayTypefaceScale(scale: Int): Float when (scale) {1 - 0.6f2 - 1.1felse - 0.0f}override fun setTextColor(color: Int) {super.setTextColor(color)textColorBuilder?.textColor colortextColorBuilder?.clearTextGradientColors()}/*** 渐变入口* */override fun setText(text: CharSequence, type: BufferType) {if (textColorBuilder?.isTextGradientColors true) {super.setText(textColorBuilder.buildLinearGradientSpannable(text), type)} else {super.setText(text, type)}}override fun onDraw(canvas: Canvas?) {if (typefaceScale 0f) {return super.onDraw(canvas)}val strokeWidth paint.strokeWidthval style paint.stylepaint.strokeWidth typefaceScalepaint.style Paint.Style.FILL_AND_STROKEsuper.onDraw(canvas)paint.strokeWidth strokeWidthpaint.style style}fun setTypefaceScale(scale: TypefaceScale TypefaceScale.DEFAULT) {typefaceScale when (scale) {TypefaceScale.DEFAULT - 0.0fTypefaceScale.MEDIUM_SMALL - 0.6fTypefaceScale.MEDIUM - 1.1f}invalidate()}}修改部分
原始 if (textColorBuilder.isTextGradientColors) {text textColorBuilder.buildLinearGradientSpannable(text)} else {textColorBuilder.intoTextColor()}改为可能多走了一层内层判断性能应该没有太大影响 textColorBuilder.intoTextColor()原因内部、外部判断逻辑重复去除外部判断即可 内部实现为文本渐变核心类后续会单独说明 shape_attr 自定义属性
?xml version1.0 encodingutf-8?
resources!-- Shape 形状默认是矩形 --attr nameshape!-- 矩形 --enum namerectangle value0 /!-- 椭圆形 --enum nameoval value1 /!-- 线条 --enum nameline value2 /!-- 圆环 --enum namering value3 //attr!-- Shape 宽度 --attr nameshape_width formatdimension /!-- Shape 高度 --attr nameshape_height formatdimension /!-- 填充色默认状态 --attr nameshape_solidColor formatcolor|reference /!-- 填充色按下状态 --attr nameshape_solidPressedColor formatcolor|reference /!-- 填充色选中状态 --attr nameshape_solidCheckedColor formatcolor|reference /!-- 填充色禁用状态 --attr nameshape_solidDisabledColor formatcolor|reference /!-- 填充色焦点状态 --attr nameshape_solidFocusedColor formatcolor|reference /!-- 填充色选择状态 --attr nameshape_solidSelectedColor formatcolor|reference /!-- 圆角大小 --attr nameshape_radius formatdimension /!-- 左上角的圆角大小 --attr nameshape_topLeftRadius formatdimension /!-- 右上角的圆角大小 --attr nameshape_topRightRadius formatdimension /!-- 左下角的圆角大小 --attr nameshape_bottomLeftRadius formatdimension /!-- 右下角的圆角大小 --attr nameshape_bottomRightRadius formatdimension /!-- 渐变色起始颜色 --attr nameshape_startColor formatcolor /!-- 渐变色中间颜色可不设置 --attr nameshape_centerColor formatcolor /!-- 渐变色结束颜色 --attr nameshape_endColor formatcolor /!-- 是否将用于缩放渐变 --attr nameshape_useLevel formatboolean /!-- 渐变角度仅用于线性渐变。必须是 0-315 范围内的值并且是 45 的倍数 --attr nameshape_angle formatfloat /!-- 渐变类型默认类型是线性渐变 --attr nameshape_gradientType!-- 线性渐变 --enum namelinear value0 /!-- 径向渐变 --enum nameradial value1 /!-- 扫描渐变 --enum namesweep value2 //attr!-- 渐变中心 X 点坐标的相对位置默认值为 0.5--attr nameshape_centerX formatfloat|fraction /!-- 渐变中心 Y 点坐标的相对位置默认值为 0.5--attr nameshape_centerY formatfloat|fraction /!-- 渐变色半径仅用于径向渐变 --attr nameshape_gradientRadius formatfloat|fraction|dimension /!-- 边框色默认状态 --attr nameshape_strokeColor formatcolor|reference /!-- 边框色按下状态 --attr nameshape_strokePressedColor formatcolor|reference /!-- 边框色选中状态 --attr nameshape_strokeCheckedColor formatcolor|reference /!-- 边框色禁用状态 --attr nameshape_strokeDisabledColor formatcolor|reference /!-- 边框色焦点状态 --attr nameshape_strokeFocusedColor formatcolor|reference /!-- 边框色选择状态 --attr nameshape_strokeSelectedColor formatcolor|reference /!-- 边框宽度 --attr nameshape_strokeWidth formatdimension /!-- 边框虚线宽度为 0 就是实线大于 0 就是虚线 --attr nameshape_dashWidth formatdimension /!-- 边框虚线间隔虚线与虚线之间的间隔 --attr nameshape_dashGap formatdimension /!-- 文本色默认状态 --attr nameshape_textColor formatcolor|reference /!-- 文本色按下状态 --attr nameshape_textPressedColor formatcolor|reference /!-- 文本色选中状态 --attr nameshape_textCheckedColor formatcolor|reference /!-- 文本色禁用状态 --attr nameshape_textDisabledColor formatcolor|reference /!-- 文本色焦点状态 --attr nameshape_textFocusedColor formatcolor|reference /!-- 文本色选择状态 --attr nameshape_textSelectedColor formatcolor|reference /!-- 文本渐变色起始颜色 --attr nameshape_textStartColor formatcolor /!-- 文本渐变色中间颜色可不设置 --attr nameshape_textCenterColor formatcolor /!-- 文本渐变色结束颜色 --attr nameshape_textEndColor formatcolor /!-- 文本渐变方向默认类型是水平渐变 --attr nameshape_textGradientOrientation!-- 水平渐变 --enum namehorizontal value0 /!-- 垂直渐变 --enum namevertical value1 //attr!-- CheckBox 或者 RadioButton 图标默认状态 --attr nameshape_buttonDrawable formatreference /!-- CheckBox 或者 RadioButton 图标按下状态 --attr nameshape_buttonPressedDrawable formatreference /!-- CheckBox 或者 RadioButton 图标选中状态 --attr nameshape_buttonCheckedDrawable formatreference /!-- CheckBox 或者 RadioButton 图标禁用状态 --attr nameshape_buttonDisabledDrawable formatreference /!-- CheckBox 或者 RadioButton 图标焦点状态 --attr nameshape_buttonFocusedDrawable formatreference /!-- CheckBox 或者 RadioButton 图标选择状态 --attr nameshape_buttonSelectedDrawable formatreference /attr nametypefaceScaleenum namenormal value0 /enum namemedium_small value1 /enum namemedium value2 //attrdeclare-styleable nameShapeTextViewattr nameshape /attr nameshape_width /attr nameshape_height /attr nameshape_solidColor /attr nameshape_solidPressedColor /attr nameshape_solidDisabledColor /attr nameshape_solidFocusedColor /attr nameshape_solidSelectedColor /attr nameshape_radius /attr nameshape_topLeftRadius /attr nameshape_topRightRadius /attr nameshape_bottomLeftRadius /attr nameshape_bottomRightRadius /attr nameshape_startColor /attr nameshape_centerColor /attr nameshape_endColor /attr nameshape_useLevel /attr nameshape_angle /attr nameshape_gradientType /attr nameshape_centerX /attr nameshape_centerY /attr nameshape_gradientRadius /attr nameshape_strokeColor /attr nameshape_strokePressedColor /attr nameshape_strokeDisabledColor /attr nameshape_strokeFocusedColor /attr nameshape_strokeSelectedColor /attr nameshape_strokeWidth /attr nameshape_dashWidth /attr nameshape_dashGap /attr nameshape_textColor /attr nameshape_textPressedColor /attr nameshape_textDisabledColor /attr nameshape_textFocusedColor /attr nameshape_textSelectedColor /attr nameshape_textStartColor /attr nameshape_textCenterColor /attr nameshape_textEndColor /attr nameshape_textGradientOrientation /attr nametypefaceScale //declare-styleable
/resourcesStyleable 动态属性 分别针对 ShapeView背景 和 TextView自身 通用型自定义属性 我觉得因为原始项目中具体实现Styleable类有很多个如果后续有扩展的话也可以再用工厂模式或策略模式二次封装 IShapeDrawableStyleable 背景属性抽象类
package com.example.shapefontbg.shape.styleable/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/28* desc : ShapeDrawable View 属性收集接口*/
interface IShapeDrawableStyleable {val shapeTypeStyleable: Intval shapeWidthStyleable: Intval shapeHeightStyleable: Intval solidColorStyleable: Intval solidPressedColorStyleable: Intval solidCheckedColorStyleable: Intget() 0val solidDisabledColorStyleable: Intval solidFocusedColorStyleable: Intval solidSelectedColorStyleable: Intval radiusStyleable: Intval topLeftRadiusStyleable: Intval topRightRadiusStyleable: Intval bottomLeftRadiusStyleable: Intval bottomRightRadiusStyleable: Intval startColorStyleable: Intval centerColorStyleable: Intval endColorStyleable: Intval useLevelStyleable: Intval angleStyleable: Intval gradientTypeStyleable: Intval centerXStyleable: Intval centerYStyleable: Intval gradientRadiusStyleable: Intval strokeColorStyleable: Intval strokePressedColorStyleable: Intval strokeCheckedColorStyleable: Intget() 0val strokeDisabledColorStyleable: Intval strokeFocusedColorStyleable: Intval strokeSelectedColorStyleable: Intval strokeWidthStyleable: Intval dashWidthStyleable: Intval dashGapStyleable: Int
}ITextColorStyleable 文本属性抽象类
package com.example.shapefontbg.shape.styleable/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/28* desc : 文本颜色 View 属性收集接口*/
interface ITextColorStyleable {val textColorStyleable: Intval textPressedColorStyleable: Intval textCheckedColorStyleable: Intget() 0val textDisabledColorStyleable: Intval textFocusedColorStyleable: Intval textSelectedColorStyleable: Intval textStartColorStyleable: Intval textCenterColorStyleable: Intval textEndColorStyleable: Intval textGradientOrientationStyleable: Int
}ShapeTextViewStyleable 具体实现类
我感觉主要有俩点作用
声明对应抽象类的自定义属性可用于固定属性、动态设置属性将动态属性和静态自定义属性做了一个基础映射
package com.example.shapefontbg.shape.styleableimport com.example.shapefontbg.R/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/28* desc : TextView 的 Shape 属性值*/
class ShapeTextViewStyleable : IShapeDrawableStyleable, ITextColorStyleable {/*** [IShapeDrawableStyleable]*/override val shapeTypeStyleable R.styleable.ShapeTextView_shapeoverride val shapeWidthStyleable R.styleable.ShapeTextView_shape_widthoverride val shapeHeightStyleable R.styleable.ShapeTextView_shape_heightoverride val solidColorStyleable R.styleable.ShapeTextView_shape_solidColoroverride val solidPressedColorStyleable R.styleable.ShapeTextView_shape_solidPressedColoroverride val solidDisabledColorStyleable R.styleable.ShapeTextView_shape_solidDisabledColoroverride val solidFocusedColorStyleable R.styleable.ShapeTextView_shape_solidFocusedColoroverride val solidSelectedColorStyleable R.styleable.ShapeTextView_shape_solidSelectedColoroverride val radiusStyleable R.styleable.ShapeTextView_shape_radiusoverride val topLeftRadiusStyleable R.styleable.ShapeTextView_shape_topLeftRadiusoverride val topRightRadiusStyleable R.styleable.ShapeTextView_shape_topRightRadiusoverride val bottomLeftRadiusStyleable R.styleable.ShapeTextView_shape_bottomLeftRadiusoverride val bottomRightRadiusStyleable R.styleable.ShapeTextView_shape_bottomRightRadiusoverride val startColorStyleable R.styleable.ShapeTextView_shape_startColoroverride val centerColorStyleable R.styleable.ShapeTextView_shape_centerColoroverride val endColorStyleable R.styleable.ShapeTextView_shape_endColoroverride val useLevelStyleable R.styleable.ShapeTextView_shape_useLeveloverride val angleStyleable R.styleable.ShapeTextView_shape_angleoverride val gradientTypeStyleable R.styleable.ShapeTextView_shape_gradientTypeoverride val centerXStyleable R.styleable.ShapeTextView_shape_centerXoverride val centerYStyleable R.styleable.ShapeTextView_shape_centerYoverride val gradientRadiusStyleable R.styleable.ShapeTextView_shape_gradientRadiusoverride val strokeColorStyleable R.styleable.ShapeTextView_shape_strokeColoroverride val strokePressedColorStyleable R.styleable.ShapeTextView_shape_strokePressedColoroverride val strokeDisabledColorStyleable R.styleable.ShapeTextView_shape_strokeDisabledColoroverride val strokeFocusedColorStyleable R.styleable.ShapeTextView_shape_strokeFocusedColoroverride val strokeSelectedColorStyleable R.styleable.ShapeTextView_shape_strokeSelectedColoroverride val strokeWidthStyleable R.styleable.ShapeTextView_shape_strokeWidthoverride val dashWidthStyleable R.styleable.ShapeTextView_shape_dashWidthoverride val dashGapStyleable R.styleable.ShapeTextView_shape_dashGap/*** [ITextColorStyleable]*/override val textColorStyleable R.styleable.ShapeTextView_shape_textColoroverride val textPressedColorStyleable R.styleable.ShapeTextView_shape_textPressedColoroverride val textDisabledColorStyleable R.styleable.ShapeTextView_shape_textDisabledColoroverride val textFocusedColorStyleable R.styleable.ShapeTextView_shape_textFocusedColoroverride val textSelectedColorStyleable R.styleable.ShapeTextView_shape_textSelectedColoroverride val textStartColorStyleable R.styleable.ShapeTextView_shape_textStartColoroverride val textCenterColorStyleable R.styleable.ShapeTextView_shape_textCenterColoroverride val textEndColorStyleable R.styleable.ShapeTextView_shape_textEndColoroverride val textGradientOrientationStyleable R.styleable.ShapeTextView_shape_textGradientOrientation
}扩展因为原作者的Shape使用场景、范围较广所以有挺多Layout-Styleable内部实现也均有所不同因为该篇主要记录渐变文本所以我们仅需关注ShapeTextViewStyleable即可 Builder
关于 ShapeDrawableBuilder 和 TextColorBuilder 的封装剥离个人认为这样的封装方式主要有以下考虑
单一职责解耦分别作用于背景和TextView自身支持自定义属性设置方式含静态设置、动态设置建造者模式便于链式动态设置自定义属性封装一些通用型方法
ShapeDrawableBuilder
主要作用于Shape背景相关属性设置
package com.example.shapefontbg.shape.builder;import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.view.View;import androidx.annotation.Nullable;import com.example.shapefontbg.shape.styleable.IShapeDrawableStyleable;/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/28* desc : ShapeDrawable 构建类*/
SuppressWarnings(unused)
public final class ShapeDrawableBuilder {private static final int NO_COLOR Color.TRANSPARENT;private final View mView;private int mShape;private int mShapeWidth;private int mShapeHeight;private int mSolidColor;private Integer mSolidPressedColor;private Integer mSolidCheckedColor;private Integer mSolidDisabledColor;private Integer mSolidFocusedColor;private Integer mSolidSelectedColor;private float mTopLeftRadius;private float mTopRightRadius;private float mBottomLeftRadius;private float mBottomRightRadius;private int[] mGradientColors;private boolean mUseLevel;private int mAngle;private int mGradientType;private float mCenterX;private float mCenterY;private int mGradientRadius;private int mStrokeColor;private Integer mStrokePressedColor;private Integer mStrokeCheckedColor;private Integer mStrokeDisabledColor;private Integer mStrokeFocusedColor;private Integer mStrokeSelectedColor;private int mStrokeWidth;private int mDashWidth;private int mDashGap;public ShapeDrawableBuilder(View view, TypedArray typedArray, IShapeDrawableStyleable styleable) {mView view;mShape typedArray.getInt(styleable.getShapeTypeStyleable(), 0);mShapeWidth typedArray.getDimensionPixelSize(styleable.getShapeWidthStyleable(), -1);mShapeHeight typedArray.getDimensionPixelSize(styleable.getShapeHeightStyleable(), -1);mSolidColor typedArray.getColor(styleable.getSolidColorStyleable(), NO_COLOR);if (typedArray.hasValue(styleable.getSolidPressedColorStyleable())) {mSolidPressedColor typedArray.getColor(styleable.getSolidPressedColorStyleable(), NO_COLOR);}if (styleable.getSolidCheckedColorStyleable() 0 typedArray.hasValue(styleable.getSolidCheckedColorStyleable())) {mSolidCheckedColor typedArray.getColor(styleable.getSolidCheckedColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getSolidDisabledColorStyleable())) {mSolidDisabledColor typedArray.getColor(styleable.getSolidDisabledColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getSolidFocusedColorStyleable())) {mSolidFocusedColor typedArray.getColor(styleable.getSolidFocusedColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getSolidSelectedColorStyleable())) {mSolidSelectedColor typedArray.getColor(styleable.getSolidSelectedColorStyleable(), NO_COLOR);}int radius typedArray.getDimensionPixelSize(styleable.getRadiusStyleable(), 0);mTopLeftRadius typedArray.getDimensionPixelSize(styleable.getTopLeftRadiusStyleable(), radius);mTopRightRadius typedArray.getDimensionPixelSize(styleable.getTopRightRadiusStyleable(), radius);mBottomLeftRadius typedArray.getDimensionPixelSize(styleable.getBottomLeftRadiusStyleable(), radius);mBottomRightRadius typedArray.getDimensionPixelSize(styleable.getBottomRightRadiusStyleable(), radius);if (typedArray.hasValue(styleable.getStartColorStyleable()) typedArray.hasValue(styleable.getEndColorStyleable())) {if (typedArray.hasValue(styleable.getCenterColorStyleable())) {mGradientColors new int[]{typedArray.getColor(styleable.getStartColorStyleable(), NO_COLOR),typedArray.getColor(styleable.getCenterColorStyleable(), NO_COLOR),typedArray.getColor(styleable.getEndColorStyleable(), NO_COLOR)};} else {mGradientColors new int[]{typedArray.getColor(styleable.getStartColorStyleable(), NO_COLOR),typedArray.getColor(styleable.getEndColorStyleable(), NO_COLOR)};}}mUseLevel typedArray.getBoolean(styleable.getUseLevelStyleable(), false);mAngle (int) typedArray.getFloat(styleable.getAngleStyleable(), 0);mGradientType typedArray.getInt(styleable.getGradientTypeStyleable(), GradientDrawable.LINEAR_GRADIENT);mCenterX typedArray.getFloat(styleable.getCenterXStyleable(), 0.5f);mCenterY typedArray.getFloat(styleable.getCenterYStyleable(), 0.5f);mGradientRadius typedArray.getDimensionPixelSize(styleable.getGradientRadiusStyleable(), radius);mStrokeColor typedArray.getColor(styleable.getStrokeColorStyleable(), NO_COLOR);if (typedArray.hasValue(styleable.getStrokePressedColorStyleable())) {mStrokePressedColor typedArray.getColor(styleable.getStrokePressedColorStyleable(), NO_COLOR);}if (styleable.getStrokeCheckedColorStyleable() 0 typedArray.hasValue(styleable.getStrokeCheckedColorStyleable())) {mStrokeCheckedColor typedArray.getColor(styleable.getStrokeCheckedColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getStrokeDisabledColorStyleable())) {mStrokeDisabledColor typedArray.getColor(styleable.getStrokeDisabledColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getStrokeFocusedColorStyleable())) {mStrokeFocusedColor typedArray.getColor(styleable.getStrokeFocusedColorStyleable(), NO_COLOR);}if (typedArray.hasValue(styleable.getStrokeSelectedColorStyleable())) {mStrokeSelectedColor typedArray.getColor(styleable.getStrokeSelectedColorStyleable(), NO_COLOR);}mStrokeWidth typedArray.getDimensionPixelSize(styleable.getStrokeWidthStyleable(), 0);mDashWidth typedArray.getDimensionPixelSize(styleable.getDashWidthStyleable(), 0);mDashGap typedArray.getDimensionPixelSize(styleable.getDashGapStyleable(), 0);}public ShapeDrawableBuilder setShape(int shape) {mShape shape;return this;}public int getShape() {return mShape;}public ShapeDrawableBuilder setShapeWidth(int width) {mShapeWidth width;return this;}public int getShapeWidth() {return mShapeWidth;}public ShapeDrawableBuilder setShapeHeight(int height) {mShapeHeight height;return this;}public int getShapeHeight() {return mShapeHeight;}public ShapeDrawableBuilder setSolidColor(int color) {mSolidColor color;clearGradientColors();return this;}public int getSolidColor() {return mSolidColor;}public ShapeDrawableBuilder setSolidPressedColor(Integer color) {mSolidPressedColor color;return this;}Nullablepublic Integer getSolidPressedColor() {return mSolidPressedColor;}public ShapeDrawableBuilder setSolidCheckedColor(Integer color) {mSolidCheckedColor color;return this;}Nullablepublic Integer getSolidCheckedColor() {return mSolidCheckedColor;}public ShapeDrawableBuilder setSolidDisabledColor(Integer color) {mSolidDisabledColor color;return this;}Nullablepublic Integer getSolidDisabledColor() {return mSolidDisabledColor;}public ShapeDrawableBuilder setSolidFocusedColor(Integer color) {mSolidFocusedColor color;return this;}Nullablepublic Integer getSolidFocusedColor() {return mSolidFocusedColor;}public ShapeDrawableBuilder setSolidSelectedColor(Integer color) {mSolidSelectedColor color;return this;}Nullablepublic Integer getSolidSelectedColor() {return mSolidSelectedColor;}public ShapeDrawableBuilder setRadius(float radius) {return setRadius(radius, radius, radius, radius);}public ShapeDrawableBuilder setRadius(float topLeftRadius, float topRightRadius, float bottomLeftRadius, float bottomRightRadius) {mTopLeftRadius topLeftRadius;mTopRightRadius topRightRadius;mBottomLeftRadius bottomLeftRadius;mBottomRightRadius bottomRightRadius;return this;}public float getTopLeftRadius() {return mTopLeftRadius;}public float getTopRightRadius() {return mTopRightRadius;}public float getBottomLeftRadius() {return mBottomLeftRadius;}public float getBottomRightRadius() {return mBottomRightRadius;}public ShapeDrawableBuilder setGradientColors(int startColor, int endColor) {return setGradientColors(new int[]{startColor, endColor});}public ShapeDrawableBuilder setGradientColors(int startColor, int centerColor, int endColor) {return setGradientColors(new int[]{startColor, centerColor, endColor});}public ShapeDrawableBuilder setGradientColors(int[] colors) {mGradientColors colors;return this;}Nullablepublic int[] getGradientColors() {return mGradientColors;}public boolean isGradientColors() {return mGradientColors ! null mGradientColors.length 0;}public void clearGradientColors() {mGradientColors null;}public ShapeDrawableBuilder setUseLevel(boolean useLevel) {mUseLevel useLevel;return this;}public boolean isUseLevel() {return mUseLevel;}public ShapeDrawableBuilder setAngle(int angle) {mAngle angle;return this;}public int getAngle() {return mAngle;}public ShapeDrawableBuilder setGradientType(int type) {mGradientType type;return this;}public int getGradientType() {return mGradientType;}public ShapeDrawableBuilder setCenterX(float x) {mCenterX x;return this;}public float getCenterX() {return mCenterX;}public ShapeDrawableBuilder setCenterY(float y) {mCenterY y;return this;}public float getCenterY() {return mCenterY;}public ShapeDrawableBuilder setGradientRadius(int radius) {mGradientRadius radius;return this;}public int getGradientRadius() {return mGradientRadius;}public ShapeDrawableBuilder setStrokeColor(int color) {mStrokeColor color;return this;}public int getStrokeColor() {return mStrokeColor;}public ShapeDrawableBuilder setStrokePressedColor(Integer color) {mStrokePressedColor color;return this;}Nullablepublic Integer getStrokePressedColor() {return mStrokePressedColor;}public ShapeDrawableBuilder setStrokeCheckedColor(Integer color) {mStrokeCheckedColor color;return this;}Nullablepublic Integer getStrokeCheckedColor() {return mStrokeCheckedColor;}public ShapeDrawableBuilder setStrokeDisabledColor(Integer color) {mStrokeDisabledColor color;return this;}Nullablepublic Integer getStrokeDisabledColor() {return mStrokeDisabledColor;}public ShapeDrawableBuilder setStrokeFocusedColor(Integer color) {mStrokeFocusedColor color;return this;}Nullablepublic Integer getStrokeFocusedColor() {return mStrokeFocusedColor;}public ShapeDrawableBuilder setStrokeSelectedColor(Integer color) {mStrokeSelectedColor color;return this;}Nullablepublic Integer getStrokeSelectedColor() {return mStrokeSelectedColor;}public ShapeDrawableBuilder setStrokeWidth(int width) {mStrokeWidth width;return this;}public int getStrokeWidth() {return mStrokeWidth;}public ShapeDrawableBuilder setDashWidth(int width) {mDashWidth width;return this;}public int getDashWidth() {return mDashWidth;}public ShapeDrawableBuilder setDashGap(int gap) {mDashGap gap;return this;}public int getDashGap() {return mDashGap;}public boolean isDashLineEnable() {return mDashGap 0;}public Drawable buildBackgroundDrawable() {if (!isGradientColors() mSolidColor NO_COLOR mStrokeColor NO_COLOR) {return null;}GradientDrawable defaultDrawable createGradientDrawable(mSolidColor, mStrokeColor);// 判断是否设置了渐变色if (isGradientColors()) {defaultDrawable.setColors(mGradientColors);}if (mSolidPressedColor ! null mStrokePressedColor ! null mSolidCheckedColor ! null mStrokeCheckedColor ! null mSolidDisabledColor ! null mStrokeDisabledColor ! null mSolidFocusedColor ! null mStrokeFocusedColor ! null mSolidSelectedColor ! null mStrokeSelectedColor ! null) {return defaultDrawable;}StateListDrawable drawable new StateListDrawable();if (mSolidPressedColor ! null || mStrokePressedColor ! null) {drawable.addState(new int[]{android.R.attr.state_pressed}, createGradientDrawable(mSolidPressedColor ! null ? mSolidPressedColor : mSolidColor,mStrokePressedColor ! null ? mStrokePressedColor : mStrokeColor));}if (mSolidCheckedColor ! null || mStrokeCheckedColor ! null) {drawable.addState(new int[]{android.R.attr.state_checked}, createGradientDrawable(mSolidCheckedColor ! null ? mSolidCheckedColor : mSolidColor,mStrokeCheckedColor ! null ? mStrokeCheckedColor : mStrokeColor));}if (mSolidDisabledColor ! null || mStrokeDisabledColor ! null) {drawable.addState(new int[]{-android.R.attr.state_enabled}, createGradientDrawable(mSolidDisabledColor ! null ? mSolidDisabledColor : mSolidColor,mStrokeDisabledColor ! null ? mStrokeDisabledColor : mStrokeColor));}if (mSolidFocusedColor ! null || mStrokeFocusedColor ! null) {drawable.addState(new int[]{android.R.attr.state_focused}, createGradientDrawable(mSolidFocusedColor ! null ? mSolidFocusedColor : mSolidColor,mStrokeFocusedColor ! null ? mStrokeFocusedColor : mStrokeColor));}if (mSolidSelectedColor ! null || mStrokeSelectedColor ! null) {drawable.addState(new int[]{android.R.attr.state_selected}, createGradientDrawable(mSolidSelectedColor ! null ? mSolidSelectedColor : mSolidColor,mStrokeSelectedColor ! null ? mStrokeSelectedColor : mStrokeColor));}drawable.addState(new int[]{}, defaultDrawable);return drawable;}public void intoBackground() {Drawable drawable buildBackgroundDrawable();if (drawable null) {return;}
// if (isDashLineEnable() || isShadowEnable()) {
// // 需要关闭硬件加速否则虚线或者阴影在某些手机上面无法生效
// mView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
// }mView.setBackground(drawable);}private GradientDrawable createGradientDrawable(int solidColor, int strokeColor) {//top-left, top-right, bottom-right, bottom-left.float[] radius {mTopLeftRadius, mTopLeftRadius, mTopRightRadius, mTopRightRadius, mBottomRightRadius, mBottomRightRadius, mBottomLeftRadius, mBottomLeftRadius};GradientDrawable gradientDrawable new GradientDrawable();gradientDrawable.setShape(mShape); // 形状gradientDrawable.setSize(mShapeWidth, mShapeHeight); // 尺寸gradientDrawable.setCornerRadii(radius); // 圆角gradientDrawable.setColor(solidColor); // 颜色gradientDrawable.setUseLevel(mUseLevel);gradientDrawable.setStroke(strokeColor, mStrokeWidth, mDashWidth, mDashGap); // 边框gradientDrawable.setOrientation(toOrientation(mAngle));gradientDrawable.setGradientType(mGradientType);gradientDrawable.setGradientRadius(mGradientRadius);gradientDrawable.setGradientCenter(mCenterX, mCenterY);return gradientDrawable;}public GradientDrawable.Orientation toOrientation(int angle) {angle % 360;// angle 必须为 45 的整数倍if (angle % 45 0) {switch (angle) {case 0:return GradientDrawable.Orientation.LEFT_RIGHT;case 45:return GradientDrawable.Orientation.BL_TR;case 90:return GradientDrawable.Orientation.BOTTOM_TOP;case 135:return GradientDrawable.Orientation.BR_TL;case 180:return GradientDrawable.Orientation.RIGHT_LEFT;case 225:return GradientDrawable.Orientation.TR_BL;case 270:return GradientDrawable.Orientation.TOP_BOTTOM;case 315:return GradientDrawable.Orientation.TL_BR;default:break;}}return GradientDrawable.Orientation.LEFT_RIGHT;}
}设置shape背景场景可以 一 一对应属性 场景判断 背景属性具体设置方式包含角度处理 TextColorBuilder
主要作用于 TextView本身属性设置
package com.example.shapefontbg.shape.builder;import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.text.SpannableStringBuilder;
import android.widget.TextView;import androidx.annotation.Nullable;import com.example.shapefontbg.shape.LinearGradientFontSpan;
import com.example.shapefontbg.shape.styleable.ITextColorStyleable;/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/28* desc : TextColor 构建类*/
SuppressWarnings(unused)
public final class TextColorBuilder {private final TextView mTextView;private int mTextColor;private Integer mTextPressedColor;private Integer mTextCheckedColor;private Integer mTextDisabledColor;private Integer mTextFocusedColor;private Integer mTextSelectedColor;private int[] mTextGradientColors;private int mTextGradientOrientation;public TextColorBuilder(TextView textView, TypedArray typedArray, ITextColorStyleable styleable) {mTextView textView;mTextColor typedArray.getColor(styleable.getTextColorStyleable(), textView.getTextColors().getDefaultColor());if (typedArray.hasValue(styleable.getTextPressedColorStyleable())) {mTextPressedColor typedArray.getColor(styleable.getTextPressedColorStyleable(), mTextColor);}if (styleable.getTextCheckedColorStyleable() 0 typedArray.hasValue(styleable.getTextCheckedColorStyleable())) {mTextCheckedColor typedArray.getColor(styleable.getTextCheckedColorStyleable(), mTextColor);}if (typedArray.hasValue(styleable.getTextDisabledColorStyleable())) {mTextDisabledColor typedArray.getColor(styleable.getTextDisabledColorStyleable(), mTextColor);}if (typedArray.hasValue(styleable.getTextFocusedColorStyleable())) {mTextFocusedColor typedArray.getColor(styleable.getTextFocusedColorStyleable(), mTextColor);}if (typedArray.hasValue(styleable.getTextSelectedColorStyleable())) {mTextSelectedColor typedArray.getColor(styleable.getTextSelectedColorStyleable(), mTextColor);}if (typedArray.hasValue(styleable.getTextStartColorStyleable()) typedArray.hasValue(styleable.getTextEndColorStyleable())) {if (typedArray.hasValue(styleable.getTextCenterColorStyleable())) {mTextGradientColors new int[]{typedArray.getColor(styleable.getTextStartColorStyleable(), mTextColor),typedArray.getColor(styleable.getTextCenterColorStyleable(), mTextColor),typedArray.getColor(styleable.getTextEndColorStyleable(), mTextColor)};} else {mTextGradientColors new int[]{typedArray.getColor(styleable.getTextStartColorStyleable(), mTextColor),typedArray.getColor(styleable.getTextEndColorStyleable(), mTextColor)};}}mTextGradientOrientation typedArray.getColor(styleable.getTextGradientOrientationStyleable(),LinearGradientFontSpan.GRADIENT_ORIENTATION_HORIZONTAL);}public TextColorBuilder setTextColor(int color) {mTextColor color;clearTextGradientColors();return this;}public int getTextColor() {return mTextColor;}public TextColorBuilder setTextPressedColor(Integer color) {mTextPressedColor color;return this;}Nullablepublic Integer getTextPressedColor() {return mTextPressedColor;}public TextColorBuilder setTextCheckedColor(Integer color) {mTextCheckedColor color;return this;}Nullablepublic Integer getTextCheckedColor() {return mTextCheckedColor;}public TextColorBuilder setTextDisabledColor(Integer color) {mTextDisabledColor color;return this;}Nullablepublic Integer getTextDisabledColor() {return mTextDisabledColor;}public TextColorBuilder setTextFocusedColor(Integer color) {mTextFocusedColor color;return this;}Nullablepublic Integer getTextFocusedColor() {return mTextFocusedColor;}public TextColorBuilder setTextSelectedColor(Integer color) {mTextSelectedColor color;return this;}Nullablepublic Integer getTextSelectedColor() {return mTextSelectedColor;}public TextColorBuilder setTextGradientColors(int startColor, int endColor) {return setTextGradientColors(new int[]{startColor, endColor});}public TextColorBuilder setTextGradientColors(int startColor, int centerColor, int endColor) {return setTextGradientColors(new int[]{startColor, centerColor, endColor});}public TextColorBuilder setTextGradientColors(int[] colors) {mTextGradientColors colors;return this;}Nullablepublic int[] getTextGradientColors() {return mTextGradientColors;}public boolean isTextGradientColors() {return mTextGradientColors ! null mTextGradientColors.length 0;}public void clearTextGradientColors() {mTextGradientColors null;}public TextColorBuilder setTextGradientOrientation(int orientation) {mTextGradientOrientation orientation;return this;}public int getTextGradientOrientation() {return mTextGradientOrientation;}public SpannableStringBuilder buildLinearGradientSpannable(CharSequence text) {return LinearGradientFontSpan.buildLinearGradientSpannable(text, mTextGradientColors, null, mTextGradientOrientation);}public ColorStateList buildColorState() {if (mTextPressedColor null mTextCheckedColor null mTextDisabledColor null mTextFocusedColor null mTextSelectedColor null) {return ColorStateList.valueOf(mTextColor);}int maxSize 6;int arraySize 0;int[][] statesTemp new int[maxSize][];int[] colorsTemp new int[maxSize];if (mTextPressedColor ! null) {statesTemp[arraySize] new int[]{android.R.attr.state_pressed};colorsTemp[arraySize] mTextPressedColor;arraySize;}if (mTextCheckedColor ! null) {statesTemp[arraySize] new int[]{android.R.attr.state_checked};colorsTemp[arraySize] mTextCheckedColor;arraySize;}if (mTextDisabledColor ! null) {statesTemp[arraySize] new int[]{-android.R.attr.state_enabled};colorsTemp[arraySize] mTextDisabledColor;arraySize;}if (mTextFocusedColor ! null) {statesTemp[arraySize] new int[]{android.R.attr.state_focused};colorsTemp[arraySize] mTextFocusedColor;arraySize;}if (mTextSelectedColor ! null) {statesTemp[arraySize] new int[]{android.R.attr.state_selected};colorsTemp[arraySize] mTextSelectedColor;arraySize;}statesTemp[arraySize] new int[]{};colorsTemp[arraySize] mTextColor;arraySize;int[][] states;int[] colors;if (arraySize maxSize) {states statesTemp;colors colorsTemp;} else {states new int[arraySize][];colors new int[arraySize];// 对数组进行拷贝System.arraycopy(statesTemp, 0, states, 0, arraySize);System.arraycopy(colorsTemp, 0, colors, 0, arraySize);}return new ColorStateList(states, colors);}public void intoTextColor() {if (isTextGradientColors()) {mTextView.setText(buildLinearGradientSpannable(mTextView.getText()));return;}mTextView.setTextColor(buildColorState());}
}LinearGradientFontSpan 文本渐变核心类
通过 LinearGradient 设置渐变效果
package com.example.shapefontbg.shape;import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Shader;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.ReplacementSpan;
import android.widget.LinearLayout;import androidx.annotation.NonNull;/*** author : Android 轮子哥* github : https://github.com/getActivity/ShapeView* time : 2021/08/17* desc : 支持直接定义文本渐变色的 Span*/
public class LinearGradientFontSpan extends ReplacementSpan {/*** 水平渐变方向*/public static final int GRADIENT_ORIENTATION_HORIZONTAL LinearLayout.HORIZONTAL;/*** 垂直渐变方向*/public static final int GRADIENT_ORIENTATION_VERTICAL LinearLayout.VERTICAL;/*** 构建一个文字渐变色的 Spannable 对象*/public static SpannableStringBuilder buildLinearGradientSpannable(CharSequence text, int[] colors, float[] positions, int orientation) {SpannableStringBuilder builder new SpannableStringBuilder(text);//下面声明了建造方法所以支持链式设置LinearGradientFontSpan span new LinearGradientFontSpan().setTextGradientColor(colors).setTextGradientOrientation(orientation).setTextGradientPositions(positions);builder.setSpan(span, 0, builder.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);return builder;}/*** 测量的文本宽度*/private float mMeasureTextWidth;/*** 文字渐变方向*/private int mTextGradientOrientation;/*** 文字渐变颜色组*/private int[] mTextGradientColor;/*** 文字渐变位置组*/private float[] mTextGradientPositions;Overridepublic int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fontMetricsInt) {mMeasureTextWidth paint.measureText(text, start, end);// 这段不可以去掉字体高度没设置会出现 draw 方法没有被调用的问题// 详情请见https://stackoverflow.com/questions/20069537/replacementspans-draw-method-isnt-calledPaint.FontMetricsInt metrics paint.getFontMetricsInt();if (fontMetricsInt ! null) {fontMetricsInt.top metrics.top;fontMetricsInt.ascent metrics.ascent;fontMetricsInt.descent metrics.descent;fontMetricsInt.bottom metrics.bottom;}return (int) mMeasureTextWidth;}Overridepublic void draw(NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, NonNull Paint paint) {LinearGradient linearGradient;if (mTextGradientOrientation GRADIENT_ORIENTATION_VERTICAL) {linearGradient new LinearGradient(0, 0, 0, paint.descent() - paint.ascent(),mTextGradientColor, mTextGradientPositions, Shader.TileMode.REPEAT);} else {linearGradient new LinearGradient(x, 0, x mMeasureTextWidth, 0,mTextGradientColor, mTextGradientPositions, Shader.TileMode.REPEAT);}paint.setShader(linearGradient);int alpha paint.getAlpha();// 判断是否给画笔设置了透明度if (alpha ! 255) {// 如果是则设置不透明paint.setAlpha(255);}canvas.drawText(text, start, end, x, y, paint);// 绘制完成之后将画笔的透明度还原回去paint.setAlpha(alpha);}public LinearGradientFontSpan setTextGradientOrientation(int orientation) {mTextGradientOrientation orientation;return this;}public LinearGradientFontSpan setTextGradientColor(int[] colors) {mTextGradientColor colors;return this;}public LinearGradientFontSpan setTextGradientPositions(float[] positions) {mTextGradientPositions positions;return this;}
}测量渐变文本的宽度 渐变方向、渐变颜色、画笔透明度处理等~