96SEO 2026-02-23 14:07 16
渐变的需求脑中也仅有一个shape渐变带来的大概思路回头来看想着学习一下这款自定义控件的内部实现故记录于此

Tip为表尊重源码中的注释声明并未做任何修改仅记录自身学习中的思考、想法
BuilderShapeDrawableBuilderTextColorBuilder
单标签固定样式产品要求不严格的话直接让设计切图简单便捷多标签固定样式产品要求不严格的话当样式标签固定在
2-5个之间可以让设计切图显示逻辑处理简单便捷多标签不固定样式例如内部可能字体可能是精选、常态、盈利等等当这种场景被不确定占据时只能通过代码来进行适配
关于渐变背景因仅为一种固定渐变背景且并非本文关键故在此直接写出如果你想更详细的学习和了解shape可前往
关于渐变位置主要有startColor、centerColor、endColor
如根据设计图的话可仅设置startColor、endColor
shape_staid_select_top_right背景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
剥离的产物所以如果不介意引入三方库的话最简单的方式肯定是直接依赖了只不过会导致项目体积更大一些毕竟有些东西我们用不到
此处项目结构为我Demo的结构目录主要通过减少三方库的引入从而减少项目体积、包体积
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
初始化背景属性、文本属性内部通过读取typefaceScale属性设置对应字体加粗效果内部增添动态设置Text、setTextColor、TypefaceScale方法支持外部调用
com.example.shapefontbg.shapeimport
androidx.appcompat.widget.AppCompatTextView
com.example.shapefontbg.shape.builder.ShapeDrawableBuilder
com.example.shapefontbg.shape.builder.TextColorBuilder
com.example.shapefontbg.shape.styleable.ShapeTextViewStyleable/***
https://github.com/getActivity/ShapeView*
context.obtainStyledAttributes(attrs,
R.styleable.ShapeTextView)//初始化背景属性shapeDrawableBuilder
STYLEABLE)//初始化文本属性textColorBuilder
typedArray.getInt(R.styleable.ShapeTextView_typefaceScale,
typedArrayTypefaceScale(scale)//
设置相关背景属性shapeDrawableBuilder.intoBackground()//
(textColorBuilder.isTextGradientColors)
textColorBuilder.buildLinearGradientSpannable(text)
textColorBuilder.intoTextColor()
}textColorBuilder.intoTextColor()}/***
{super.setTextColor(color)textColorBuilder?.textColor
colortextColorBuilder?.clearTextGradientColors()}/***
(textColorBuilder?.isTextGradientColors
{super.setText(textColorBuilder.buildLinearGradientSpannable(text),
Paint.Style.FILL_AND_STROKEsuper.onDraw(canvas)paint.strokeWidth
(textColorBuilder.isTextGradientColors)
textColorBuilder.buildLinearGradientSpannable(text)}
{textColorBuilder.intoTextColor()}改为可能多走了一层内层判断性能应该没有太大影响
textColorBuilder.intoTextColor()原因内部、外部判断逻辑重复去除外部判断即可
nameshape_textGradientOrientation!--
nameshape_buttonPressedDrawable
nameshape_buttonCheckedDrawable
nameshape_buttonDisabledDrawable
nameshape_buttonFocusedDrawable
nameshape_buttonSelectedDrawable
nameshape_textGradientOrientation
我觉得因为原始项目中具体实现Styleable类有很多个如果后续有扩展的话也可以再用工厂模式或策略模式二次封装
com.example.shapefontbg.shape.styleable/***
https://github.com/getActivity/ShapeView*
com.example.shapefontbg.shape.styleable/***
https://github.com/getActivity/ShapeView*
textGradientOrientationStyleable:
声明对应抽象类的自定义属性可用于固定属性、动态设置属性将动态属性和静态自定义属性做了一个基础映射
com.example.shapefontbg.shape.styleableimport
https://github.com/getActivity/ShapeView*
[IShapeDrawableStyleable]*/override
R.styleable.ShapeTextView_shapeoverride
R.styleable.ShapeTextView_shape_widthoverride
R.styleable.ShapeTextView_shape_heightoverride
R.styleable.ShapeTextView_shape_solidColoroverride
R.styleable.ShapeTextView_shape_solidPressedColoroverride
R.styleable.ShapeTextView_shape_solidDisabledColoroverride
R.styleable.ShapeTextView_shape_solidFocusedColoroverride
R.styleable.ShapeTextView_shape_solidSelectedColoroverride
R.styleable.ShapeTextView_shape_radiusoverride
R.styleable.ShapeTextView_shape_topLeftRadiusoverride
R.styleable.ShapeTextView_shape_topRightRadiusoverride
R.styleable.ShapeTextView_shape_bottomLeftRadiusoverride
R.styleable.ShapeTextView_shape_bottomRightRadiusoverride
R.styleable.ShapeTextView_shape_startColoroverride
R.styleable.ShapeTextView_shape_centerColoroverride
R.styleable.ShapeTextView_shape_endColoroverride
R.styleable.ShapeTextView_shape_useLeveloverride
R.styleable.ShapeTextView_shape_angleoverride
R.styleable.ShapeTextView_shape_gradientTypeoverride
R.styleable.ShapeTextView_shape_centerXoverride
R.styleable.ShapeTextView_shape_centerYoverride
R.styleable.ShapeTextView_shape_gradientRadiusoverride
R.styleable.ShapeTextView_shape_strokeColoroverride
R.styleable.ShapeTextView_shape_strokePressedColoroverride
R.styleable.ShapeTextView_shape_strokeDisabledColoroverride
R.styleable.ShapeTextView_shape_strokeFocusedColoroverride
R.styleable.ShapeTextView_shape_strokeSelectedColoroverride
R.styleable.ShapeTextView_shape_strokeWidthoverride
R.styleable.ShapeTextView_shape_dashWidthoverride
R.styleable.ShapeTextView_shape_dashGap/***
[ITextColorStyleable]*/override
R.styleable.ShapeTextView_shape_textColoroverride
R.styleable.ShapeTextView_shape_textPressedColoroverride
R.styleable.ShapeTextView_shape_textDisabledColoroverride
R.styleable.ShapeTextView_shape_textFocusedColoroverride
R.styleable.ShapeTextView_shape_textSelectedColoroverride
R.styleable.ShapeTextView_shape_textStartColoroverride
R.styleable.ShapeTextView_shape_textCenterColoroverride
R.styleable.ShapeTextView_shape_textEndColoroverride
textGradientOrientationStyleable
R.styleable.ShapeTextView_shape_textGradientOrientation
}扩展因为原作者的Shape使用场景、范围较广所以有挺多Layout-Styleable内部实现也均有所不同因为该篇主要记录渐变文本所以我们仅需关注ShapeTextViewStyleable即可
单一职责解耦分别作用于背景和TextView自身支持自定义属性设置方式含静态设置、动态设置建造者模式便于链式动态设置自定义属性封装一些通用型方法
com.example.shapefontbg.shape.builder;import
android.content.res.TypedArray;
android.graphics.drawable.Drawable;
android.graphics.drawable.GradientDrawable;
android.graphics.drawable.StateListDrawable;
androidx.annotation.Nullable;import
com.example.shapefontbg.shape.styleable.IShapeDrawableStyleable;/***
https://github.com/getActivity/ShapeView*
typedArray.getInt(styleable.getShapeTypeStyleable(),
typedArray.getDimensionPixelSize(styleable.getShapeWidthStyleable(),
typedArray.getDimensionPixelSize(styleable.getShapeHeightStyleable(),
typedArray.getColor(styleable.getSolidColorStyleable(),
(typedArray.hasValue(styleable.getSolidPressedColorStyleable()))
typedArray.getColor(styleable.getSolidPressedColorStyleable(),
(styleable.getSolidCheckedColorStyleable()
typedArray.hasValue(styleable.getSolidCheckedColorStyleable()))
typedArray.getColor(styleable.getSolidCheckedColorStyleable(),
(typedArray.hasValue(styleable.getSolidDisabledColorStyleable()))
typedArray.getColor(styleable.getSolidDisabledColorStyleable(),
(typedArray.hasValue(styleable.getSolidFocusedColorStyleable()))
typedArray.getColor(styleable.getSolidFocusedColorStyleable(),
(typedArray.hasValue(styleable.getSolidSelectedColorStyleable()))
typedArray.getColor(styleable.getSolidSelectedColorStyleable(),
typedArray.getDimensionPixelSize(styleable.getRadiusStyleable(),
typedArray.getDimensionPixelSize(styleable.getTopLeftRadiusStyleable(),
typedArray.getDimensionPixelSize(styleable.getTopRightRadiusStyleable(),
typedArray.getDimensionPixelSize(styleable.getBottomLeftRadiusStyleable(),
typedArray.getDimensionPixelSize(styleable.getBottomRightRadiusStyleable(),
(typedArray.hasValue(styleable.getStartColorStyleable())
typedArray.hasValue(styleable.getEndColorStyleable()))
(typedArray.hasValue(styleable.getCenterColorStyleable()))
int[]{typedArray.getColor(styleable.getStartColorStyleable(),
NO_COLOR),typedArray.getColor(styleable.getCenterColorStyleable(),
NO_COLOR),typedArray.getColor(styleable.getEndColorStyleable(),
int[]{typedArray.getColor(styleable.getStartColorStyleable(),
NO_COLOR),typedArray.getColor(styleable.getEndColorStyleable(),
typedArray.getBoolean(styleable.getUseLevelStyleable(),
typedArray.getFloat(styleable.getAngleStyleable(),
typedArray.getInt(styleable.getGradientTypeStyleable(),
GradientDrawable.LINEAR_GRADIENT);mCenterX
typedArray.getFloat(styleable.getCenterXStyleable(),
typedArray.getFloat(styleable.getCenterYStyleable(),
typedArray.getDimensionPixelSize(styleable.getGradientRadiusStyleable(),
typedArray.getColor(styleable.getStrokeColorStyleable(),
(typedArray.hasValue(styleable.getStrokePressedColorStyleable()))
typedArray.getColor(styleable.getStrokePressedColorStyleable(),
(styleable.getStrokeCheckedColorStyleable()
typedArray.hasValue(styleable.getStrokeCheckedColorStyleable()))
typedArray.getColor(styleable.getStrokeCheckedColorStyleable(),
(typedArray.hasValue(styleable.getStrokeDisabledColorStyleable()))
typedArray.getColor(styleable.getStrokeDisabledColorStyleable(),
(typedArray.hasValue(styleable.getStrokeFocusedColorStyleable()))
typedArray.getColor(styleable.getStrokeFocusedColorStyleable(),
(typedArray.hasValue(styleable.getStrokeSelectedColorStyleable()))
typedArray.getColor(styleable.getStrokeSelectedColorStyleable(),
typedArray.getDimensionPixelSize(styleable.getStrokeWidthStyleable(),
typedArray.getDimensionPixelSize(styleable.getDashWidthStyleable(),
typedArray.getDimensionPixelSize(styleable.getDashGapStyleable(),
color;clearGradientColors();return
topRightRadius;mBottomLeftRadius
bottomLeftRadius;mBottomRightRadius
createGradientDrawable(mSolidColor,
{defaultDrawable.setColors(mGradientColors);}if
defaultDrawable;}StateListDrawable
int[]{android.R.attr.state_pressed},
createGradientDrawable(mSolidPressedColor
mSolidColor,mStrokePressedColor
int[]{android.R.attr.state_checked},
createGradientDrawable(mSolidCheckedColor
mSolidColor,mStrokeCheckedColor
int[]{-android.R.attr.state_enabled},
createGradientDrawable(mSolidDisabledColor
mSolidColor,mStrokeDisabledColor
int[]{android.R.attr.state_focused},
createGradientDrawable(mSolidFocusedColor
mSolidColor,mStrokeFocusedColor
int[]{android.R.attr.state_selected},
createGradientDrawable(mSolidSelectedColor
mSolidColor,mStrokeSelectedColor
mStrokeColor));}drawable.addState(new
mView.setLayerType(View.LAYER_TYPE_SOFTWARE,
}mView.setBackground(drawable);}private
mBottomLeftRadius};GradientDrawable
GradientDrawable();gradientDrawable.setShape(mShape);
形状gradientDrawable.setSize(mShapeWidth,
尺寸gradientDrawable.setCornerRadii(radius);
圆角gradientDrawable.setColor(solidColor);
颜色gradientDrawable.setUseLevel(mUseLevel);gradientDrawable.setStroke(strokeColor,
边框gradientDrawable.setOrientation(toOrientation(mAngle));gradientDrawable.setGradientType(mGradientType);gradientDrawable.setGradientRadius(mGradientRadius);gradientDrawable.setGradientCenter(mCenterX,
GradientDrawable.Orientation.LEFT_RIGHT;case
GradientDrawable.Orientation.BL_TR;case
GradientDrawable.Orientation.BOTTOM_TOP;case
GradientDrawable.Orientation.BR_TL;case
GradientDrawable.Orientation.RIGHT_LEFT;case
GradientDrawable.Orientation.TR_BL;case
GradientDrawable.Orientation.TOP_BOTTOM;case
GradientDrawable.Orientation.TL_BR;default:break;}}return
GradientDrawable.Orientation.LEFT_RIGHT;}
com.example.shapefontbg.shape.builder;import
android.content.res.ColorStateList;
android.content.res.TypedArray;
android.text.SpannableStringBuilder;
androidx.annotation.Nullable;import
com.example.shapefontbg.shape.LinearGradientFontSpan;
com.example.shapefontbg.shape.styleable.ITextColorStyleable;/***
https://github.com/getActivity/ShapeView*
mTextGradientOrientation;public
typedArray.getColor(styleable.getTextColorStyleable(),
textView.getTextColors().getDefaultColor());if
(typedArray.hasValue(styleable.getTextPressedColorStyleable()))
typedArray.getColor(styleable.getTextPressedColorStyleable(),
(styleable.getTextCheckedColorStyleable()
typedArray.hasValue(styleable.getTextCheckedColorStyleable()))
typedArray.getColor(styleable.getTextCheckedColorStyleable(),
(typedArray.hasValue(styleable.getTextDisabledColorStyleable()))
typedArray.getColor(styleable.getTextDisabledColorStyleable(),
(typedArray.hasValue(styleable.getTextFocusedColorStyleable()))
typedArray.getColor(styleable.getTextFocusedColorStyleable(),
(typedArray.hasValue(styleable.getTextSelectedColorStyleable()))
typedArray.getColor(styleable.getTextSelectedColorStyleable(),
(typedArray.hasValue(styleable.getTextStartColorStyleable())
typedArray.hasValue(styleable.getTextEndColorStyleable()))
(typedArray.hasValue(styleable.getTextCenterColorStyleable()))
int[]{typedArray.getColor(styleable.getTextStartColorStyleable(),
mTextColor),typedArray.getColor(styleable.getTextCenterColorStyleable(),
mTextColor),typedArray.getColor(styleable.getTextEndColorStyleable(),
int[]{typedArray.getColor(styleable.getTextStartColorStyleable(),
mTextColor),typedArray.getColor(styleable.getTextEndColorStyleable(),
mTextColor)};}}mTextGradientOrientation
typedArray.getColor(styleable.getTextGradientOrientationStyleable(),LinearGradientFontSpan.GRADIENT_ORIENTATION_HORIZONTAL);}public
color;clearTextGradientColors();return
mTextGradientOrientation;}public
buildLinearGradientSpannable(CharSequence
LinearGradientFontSpan.buildLinearGradientSpannable(text,
mTextGradientOrientation);}public
ColorStateList.valueOf(mTextColor);}int
int[]{android.R.attr.state_pressed};colorsTemp[arraySize]
mTextPressedColor;arraySize;}if
int[]{android.R.attr.state_checked};colorsTemp[arraySize]
mTextCheckedColor;arraySize;}if
int[]{-android.R.attr.state_enabled};colorsTemp[arraySize]
mTextDisabledColor;arraySize;}if
int[]{android.R.attr.state_focused};colorsTemp[arraySize]
mTextFocusedColor;arraySize;}if
int[]{android.R.attr.state_selected};colorsTemp[arraySize]
mTextSelectedColor;arraySize;}statesTemp[arraySize]
对数组进行拷贝System.arraycopy(statesTemp,
arraySize);System.arraycopy(colorsTemp,
{mTextView.setText(buildLinearGradientSpannable(mTextView.getText()));return;}mTextView.setTextColor(buildColorState());}
com.example.shapefontbg.shape;import
android.graphics.LinearGradient;
android.text.SpannableStringBuilder;
android.text.style.ReplacementSpan;
android.widget.LinearLayout;import
androidx.annotation.NonNull;/***
https://github.com/getActivity/ShapeView*
GRADIENT_ORIENTATION_HORIZONTAL
buildLinearGradientSpannable(CharSequence
SpannableStringBuilder(text);//下面声明了建造方法所以支持链式设置LinearGradientFontSpan
LinearGradientFontSpan().setTextGradientColor(colors).setTextGradientOrientation(orientation).setTextGradientPositions(positions);builder.setSpan(span,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);return
mTextGradientPositions;Overridepublic
详情请见https://stackoverflow.com/questions/20069537/replacementspans-draw-method-isnt-calledPaint.FontMetricsInt
metrics.top;fontMetricsInt.ascent
metrics.ascent;fontMetricsInt.descent
metrics.descent;fontMetricsInt.bottom
mMeasureTextWidth;}Overridepublic
paint.ascent(),mTextGradientColor,
Shader.TileMode.REPEAT);}paint.setShader(linearGradient);int
如果是则设置不透明paint.setAlpha(255);}canvas.drawText(text,
绘制完成之后将画笔的透明度还原回去paint.setAlpha(alpha);}public
setTextGradientPositions(float[]
作为专业的SEO优化服务提供商,我们致力于通过科学、系统的搜索引擎优化策略,帮助企业在百度、Google等搜索引擎中获得更高的排名和流量。我们的服务涵盖网站结构优化、内容优化、技术SEO和链接建设等多个维度。
| 服务项目 | 基础套餐 | 标准套餐 | 高级定制 |
|---|---|---|---|
| 关键词优化数量 | 10-20个核心词 | 30-50个核心词+长尾词 | 80-150个全方位覆盖 |
| 内容优化 | 基础页面优化 | 全站内容优化+每月5篇原创 | 个性化内容策略+每月15篇原创 |
| 技术SEO | 基本技术检查 | 全面技术优化+移动适配 | 深度技术重构+性能优化 |
| 外链建设 | 每月5-10条 | 每月20-30条高质量外链 | 每月50+条多渠道外链 |
| 数据报告 | 月度基础报告 | 双周详细报告+分析 | 每周深度报告+策略调整 |
| 效果保障 | 3-6个月见效 | 2-4个月见效 | 1-3个月快速见效 |
我们的SEO优化服务遵循科学严谨的流程,确保每一步都基于数据分析和行业最佳实践:
全面检测网站技术问题、内容质量、竞争对手情况,制定个性化优化方案。
基于用户搜索意图和商业目标,制定全面的关键词矩阵和布局策略。
解决网站技术问题,优化网站结构,提升页面速度和移动端体验。
创作高质量原创内容,优化现有页面,建立内容更新机制。
获取高质量外部链接,建立品牌在线影响力,提升网站权威度。
持续监控排名、流量和转化数据,根据效果调整优化策略。
基于我们服务的客户数据统计,平均优化效果如下:
我们坚信,真正的SEO优化不仅仅是追求排名,而是通过提供优质内容、优化用户体验、建立网站权威,最终实现可持续的业务增长。我们的目标是与客户建立长期合作关系,共同成长。
Demand feedback