效果如图所示:


可见要实现次效果,需要自定义View继承TextView(因为有文字,继承这个会好一些)。
外圈是一个橙色的背景,形状为圆角矩形,以及底部有一个小直角三角形倒过来的形状。里面是变色文字。这里我们先实现外圈的橙色背景。

绿色背景:

  1. 在res文件夹中的values,新建一个文件attrs.xml, 这个是对于自定义view的声明文件。
  2. 属性包括,圆角矩形的圆角半径长度、小直角三角形对角线一半的长度,以及颜色、圆角矩形高度、标题栏滑动方向。

    代码如下:
<declare-styleable name="LoryMessageTitle"><attr name="roudedRadius" format="dimension"></attr><!--矩形圆角半径径--><attr name="triangleLengthHalf" format="dimension"></attr><!--直角三角形对角线长度一半--><attr name="rectHeight" format="dimension"></attr><!--圆角矩形高度--><attr name="colorAll" format="color"></attr><!--控件颜色--><attr name="changeDirection">   <!--控件变化方向--><enum name="LEFT_TO_RIGHT" value="1"></enum><enum name="RIGHT_TO_LEFT" value="2"></enum></attr></declare-styleable>

3.自定义view名 LoryMessageTitle继承Textview,代码如下:

public class LoryMessageTitle extends androidx.appcompat.widget.AppCompatTextView {//背景图案private float progress = 1f;//移动比例参数progress,默认是0private int mRoundedRadius = 3;//圆角矩形直径默认长度private int mTraigleLength = 4;//直角三角形对角线边默认一半长度private int mRectHeight = 20;//圆角矩形默认高度private int mRectWidth = 20;//圆角矩形默认长度private int mColor = getResources().getColor(R.color.colorGreener,null);//控件默认颜色private int mDirection = 1; //默认变化从左到右private Paint mPaint;//控件绘制的画笔,由于控件背景色颜色统一,所以用一支画笔就可以了public LoryMessageTitle(Context context){this(context,null);}public LoryMessageTitle(Context context, AttributeSet attrs){this(context,attrs,0);}public LoryMessageTitle(Context context,AttributeSet attrs,int delStyleAttr){super(context,attrs,delStyleAttr);//初始化init(context,attrs);}//初始化函数public void init(Context context,AttributeSet attrs){TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoryMessageTitle);mRoundedRadius = array.getDimensionPixelSize(R.styleable.LoryMessageTitle_roudedRadius,sp2px(mRoundedRadius));//圆角半径大小mTraigleLength = array.getDimensionPixelSize(R.styleable.LoryMessageTitle_triangleLengthHalf,sp2px(mTraigleLength));//三角形对角线长度一半mRectHeight = array.getDimensionPixelSize(R.styleable.LoryMessageTitle_rectHeight,sp2px(mRectHeight));//圆角矩形高度mColor = array.getColor(R.styleable.LoryMessageTitle_colorAll,mColor);//控件颜色mDirection = array.getInteger(R.styleable.LoryMessageTitle_changeDirection,mDirection);//控件移动方向mPaint = new Paint();mPaint.setAntiAlias(true);//抗锯齿mPaint.setDither(true);mPaint.setColor(mColor);mDirection = array.getInteger(R.styleable.LoryMessageTitle_changeDirection,mDirection);}//sp  to  px  不多说,就是 sp转化为 px,要是控件使用者用dp怎么办,那就让他自己添加个dp转sp的函数,就你事多public int sp2px(int sp){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, this.getResources().getDisplayMetrics());}//测量方法@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthmode = MeasureSpec.getMode(widthMeasureSpec);//宽度模式int heightmode = MeasureSpec.getMode(heightMeasureSpec);//高度模式int widthsize = MeasureSpec.getSize(widthMeasureSpec);//宽度大小int heightsize = MeasureSpec.getSize(heightMeasureSpec);//高度大小//要是用户填的wrap_content,宽度、高度至少时if (widthmode==MeasureSpec.AT_MOST){int widthsize = sp2px(mRectWidth)+getPaddingLeft()+getPaddingRight();//背景宽度}if (heightmode==MeasureSpec.AT_MOST){int heightsize = mRectHeight+mTraigleLength+getPaddingTop()+getPaddingBottom();}setMeasuredDimension(widthsize,heightsize);}//绘制方法,一开始绘制一个静态的控件,然后这个控件会移动,有个0-1的参数,决定这个控件移动的比例,引入这个参数叫 progress。默认是0;@Overrideprotected void onDraw(Canvas canvas) {//静态控件如下
//        //首先绘制静态的圆角矩形
//        { canvas.save();
//        RectF rectf = new RectF(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom() - mTraigleLength);
//        canvas.drawRoundRect(rectf, mRoundedRadius, mRoundedRadius, mPaint);
//        canvas.restore();
//        //然后绘制三角形,是一个倒过来的直角三角形,可以用一个倒过来的正方形代替,对角线和矩形重合即可,也可以通过路径绘制
//        canvas.save();
//        Path path = new Path();
//        path.moveTo(getWidth() / 2 - mTraigleLength, getHeight() - getPaddingBottom() - mTraigleLength);//起点
//        path.lineTo(getWidth() / 2, getHeight() - getPaddingBottom());//往下移动
//        path.lineTo(getWidth() / 2 + mTraigleLength, getHeight() - getPaddingBottom() - mTraigleLength);//往右移动
//        path.close();//路径闭合
//        canvas.drawPath(path, mPaint);
//        canvas.restore();
//    }//颜色更改后,画笔也要及时更新哈mPaint.setColor(mColor);//绘制动态圆角矩形,随着progress运动,圆角矩形的左边x参数在增加,右边参数也在增加,由于控件长度固定,//所以可以出现圆角矩形右侧被吞噬的样子float x1;//圆角矩形左侧坐标float x2;//圆角矩形右侧坐标Rect bounds = new Rect();if (mDirection==2){//如果移动方向是从右到左x1 = (1-progress)*(getWidth())+getPaddingLeft();x2 = (2-progress)*(getWidth())-getPaddingRight();}else {//移动方向为从左到右x1 = (progress-1)*(getWidth())+getPaddingLeft();x2 = progress*(getWidth())-getPaddingRight();}canvas.save();RectF rectf =new RectF(x1,getPaddingTop(),x2,getHeight()-getPaddingBottom()-mTraigleLength);//矩形canvas.drawRoundRect(rectf,mRoundedRadius,mRoundedRadius,mPaint);//绘制圆角矩形//然后绘制三角形,是一个倒过来的直角三角形,可以用一个旋转的正方形代替,对角线和矩形重合即可,也可以通过路径绘制//我个人更喜欢路径Path,关于Path的方法百度上有canvas.save();Path path = new Path();path.moveTo(getWidth()/2-mTraigleLength+x1-getPaddingLeft(),getHeight()-getPaddingBottom()-mTraigleLength);//起点,三角形左边起点path.lineTo(getWidth()/2+x1-getPaddingLeft(),getHeight()-getPaddingBottom());//往下移动path.lineTo(getWidth()/2+mTraigleLength+x1-getPaddingLeft(),getHeight()-getPaddingBottom()-mTraigleLength);//往右移动,三角形右边的点path.close();//路径闭合,将绘制的三个点闭合canvas.drawPath(path,mPaint);//绘制三角形canvas.restore();}
//  最后就是这个控件随着progress变化而变化,每次变化都要重新绘制,
//invalidate方法会再次调用ondraw方法public void changeProgress(float progresser){//设置改变进度的方法progress = progresser;invalidate();//重新绘制}
// 也有人说,我不想在xml文件中设置控件状态,我想java文件中改变控件的圆角矩形高度、圆角大小,小三角形对角线宽一半,颜色等//你丫的需求真多呀,就你多事public void setSate(int roundedRadius,int roundedheight,int traigleLengthHalf,int color){//改变控件状态mRoundedRadius = roundedRadius;mRectHeight = roundedheight;mTraigleLength = traigleLengthHalf;mColor = color;invalidate();}public void setdirction(int c_direction){//设置控件变化方向mDirection = c_direction;invalidate();}public void setmColor(int color){//设置控件颜色mColor = color;invalidate();}
}

好了这个控件定义完了,那么效果如何?就把它当作textview就测试一下,要是能够绘制出这个效果就说明可用了。
接下来就是字体的绘制,我们再造一个自定view,也是继承TextView.

可变色字体的绘制:

两种颜色的字体,而且可以随着progress变化而变化,那么如何实现呢?

很简单,只要将画布Canvas分割,裁成两个区域,一个是灰色,一个是白色,然后区域会随着progress变化而变化。

  1. 在attrs.xml文件中声明一个字体的自定义view
 <declare-styleable name="ChangeColorText"><attr name="changeColor" format="color"></attr><!--文字改变后的颜色--><attr name="orignColor" format="color"></attr><!--文字原颜色--><attr name="changeDirection">   <!--控件变化方向--><enum name="LEFT_TO_RIGHT" value="1"></enum><enum name="RIGHT_TO_LEFT" value="2"></enum></attr></declare-styleable>
  1. 自定义view, ChangeColorText继承TextView:
public class ChangeColorText extends androidx.appcompat.widget.AppCompatTextView {private float progress = 1f;//移动比例参数progress,默认是0//字体private int mOrignColor = getResources().getColor(R.color.colorChange,null);//默认字体原始颜色private int mChangeColor = Color.WHITE;//默认字体变化颜色private Paint mOrignPaint,mChangePaint;//绘制两种字体的画笔private int textsize = 50;//默认原字体大小pxprivate int textsize1 = 55;//默认字体变化的大小private int mDirection = 1; //默认变化从左到右public ChangeColorText(Context context){this(context,null);}public ChangeColorText(Context context, AttributeSet attrs){this(context,attrs,0);}public ChangeColorText(Context context,AttributeSet attrs,int delStyleAttr){super(context,attrs,delStyleAttr);//初始化init(context,attrs);}//初始化函数public void init(Context context,AttributeSet attrs){TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.LoryMessageTitle);//字体mOrignColor = array.getColor(R.styleable.LoryMessageTitle_orignColor,mOrignColor);//原始颜色mChangeColor = array.getColor(R.styleable.LoryMessageTitle_changeColor,mChangeColor);//变化颜色mDirection = array.getInteger(R.styleable.LoryMessageTitle_changeDirection,mDirection);//变化方向//orignPaint画笔初始化mOrignPaint = new Paint();mOrignPaint.setColor(mOrignColor);mOrignPaint.setAntiAlias(true);mOrignPaint.setDither(true);mOrignPaint.setTextSize(textsize);//changePaint画笔初始化mChangePaint = new Paint();mChangePaint.setColor(mChangeColor);mChangePaint.setAntiAlias(true);mChangePaint.setDither(true);mChangePaint.setTextSize(textsize1);}//sp  to  px  不多说,就是 sp转化为 px,要是控件使用者用dp怎么办,那就让他自己添加个dp转sp的函数,就你事多public int sp2px(int sp){return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, this.getResources().getDisplayMetrics());}//测量方法@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {int widthmode = MeasureSpec.getMode(widthMeasureSpec);int heightmode = MeasureSpec.getMode(heightMeasureSpec);int widthsize = MeasureSpec.getSize(widthMeasureSpec);int heightsize = MeasureSpec.getSize(heightMeasureSpec);//文字的长宽,用画笔测量文字的宽度,长度Rect bounds = new Rect();mChangePaint.getTextBounds(getText().toString(),0,getText().length(),bounds);//要是用户填的wrap_content,宽度、高度至少if (widthmode==MeasureSpec.AT_MOST){int widthsize = bounds.width() + getPaddingLeft()+getPaddingRight();//文字宽度}if (heightmode==MeasureSpec.AT_MOST){int heightsize = bounds.height() + +getPaddingTop()+getPaddingBottom();}setMeasuredDimension(widthsize,heightsize);}@Overrideprotected void onDraw(Canvas canvas) {//文字的横坐标int x = getWidth()/2-bounds.width()/2;Paint.FontMetricsInt fontMetricsInt = mOrignPaint.getFontMetricsInt();int dy = (fontMetricsInt.bottom - fontMetricsInt.top)/2 - fontMetricsInt.bottom;int baseline = getHeight()/2+dy;//基线//在绘制变色文字if(mDirection==1){//从左到右canvas.save();canvas.clipRect(0,0,progress*getWidth()-getPaddingLeft(),getHeight());//裁剪坐左边的区域//绘制左边文字canvas.drawText(getText().toString(),x,baseline,mChangePaint);canvas.restore();canvas.save();canvas.clipRect(progress*getWidth()-getPaddingLeft(),0,getWidth(),getHeight());//裁剪右边的区域canvas.drawText(getText().toString(),x,baseline,mOrignPaint);canvas.restore();//绘制右边的文字}else{//从右到左canvas.save();canvas.clipRect(0,0,(1-progress)*getWidth()+getPaddingRight(),getHeight());canvas.drawText(getText().toString(),x,baseline,mOrignPaint);canvas.restore();canvas.save();canvas.clipRect((1-progress)*getWidth()+getPaddingRight(),0,getWidth(),getHeight());canvas.drawText(getText().toString(),x,baseline,mChangePaint);canvas.restore();}}
//  最后就是这个控件随progress变化,而重新绘制。public void changeProgress(float progresser){progress = progresser;invalidate();//重新绘制}public void setmOrignColor(int c_color){//原文字颜色this.mOrignColor = c_color;invalidate();}public void setmChangeColor(int c_color){//变化文字颜色this.mChangeColor = c_color;invalidate();}//设置绘制时字体 大小 粗细于一体public void setmWordSize(int orignSize,int changSize){this.textsize = orignSize;this.textsize1 = changSize;invalidate();}public void setdirction(int c_direction){mDirection = c_direction;invalidate();}}

文字也绘制出来了,那么如何将两种效果放在一个控件中呢?简单呀,将两种控件的属性放到一个控件就可以了。这个工作就交给读者吧,我想你通过这两个自定义view的绘制后,应该不难。当然,期间你肯定也会有一些和我当初学的时候的坑。

大坑汇集:

  1. 标题1在过度标题2 的过程中, 标题1的颜色由白色逐渐变为灰色,标题2的颜色由灰色逐渐变为白色,但是这两个标题的OriginColor和ChangeColor代码中都写成了一样的,导致标题1的颜色由白色逐渐变为灰色,标题2的颜色由白色逐渐变为灰色,这样并没有文字变色移动的效果,理论上 标题1的OriginColor本应该为白色,而标题2的OriginColor应该为灰色,如何解决这个冲突呢?

答:OriginColor为灰色,ChangeColor为白色。 标题1的移动方向为“从右往左”,而标题2的移动方向设置为“从左到右”。因为代码中,direction的变化会让原色和变色转换。从左边往右边,标题1是由白色变灰色,而从右边往左边,标题1是由灰色变白色。并且, 标题1的进度=1-progress,标题2的进度为 progress.

  1. 可变颜色字体和橙色背景结合时,控件的宽高测量冲突怎么解决?

答: 可变颜色字体的宽高测量是通过画笔测量单个文字后给出的Bounds决定的,而橙色背景的宽高是人为给定的。要让文字和背景的放置比例位置合理,是一个头疼的问题。作者的方法是基于文字的宽高,再加上固定的长度。至于固定的长度是多长,这个需要读者自己测试把握。作者并没有好的办法。

  1. 标题1移动到标题2过程中,颜色变化和橙色背景移动都是由参数progress决定的,那这个progress的数据来源从哪来呢?换句话说,我怎样可以用手滑动屏幕时,progress随滑动比例而变化呢?

答: 可滑动标题栏下方的内容是一个ViewPager, viewPager可以适应多个适配器Adapter,至于用哪个Adapter看你个人习惯,不过由于ViewPager内放的是Fragment,所以我建议用FragmentPageAdapter(名字有没有打错哈)。ViewPager有3个关于滑动方法:

看到第一个方法,那个postionOffset,这个就是ViewPager监听滑动比例,范围由(0—1),progress的数据来源就是它。

以上就是我遇到的坑,其他问题要是读者遇到了,那就评论区留言吧

Android自定义view变色字体实现相关推荐

  1. Android自定义View —— TypedArray

    在上一篇中Android 自定义View Canvas -- Bitmap写到了TypedArray 这个属性 下面也简单的说一下TypedArray的使用 TypedArray 的作用: 用于从该结 ...

  2. Android 自定义View —— Canvas

    上一篇在android 自定义view Paint 里面 说了几种常见的Point 属性 绘制图形的时候下面总有一个canvas ,Canvas 是是画布 上面可以绘制点,线,正方形,圆,等等,需要和 ...

  3. Android自定义view详解,使用实例,自定义属性,贝塞尔曲线

    //只会触发执行onDraw方法,只会改变绘制里面的内容,条目的绘制 invalidate(); //只会触发执行onDraw方法,但是可以在子线程中刷新 postInvalidate(); //vi ...

  4. Android自定义View(七)_Canvas之图片文字

    在上一篇文章Canvas之画布操作中我们了解了画布的一些基本操作方法,本次了解一些绘制图片文字相关的内容.如果你对前几篇文章讲述的内容熟练掌握的话,那么恭喜你,本篇结束之后,大部分的自定义View已经 ...

  5. Android自定义View之画圆环(进阶篇:圆形进度条)

    前言: 如果你想读懂或者更好的理解本篇文章关于自定义圆环或圆弧的内容.请你务必提前阅读下Android自定义View之画圆环(手把手教你如何一步步画圆环).在这篇文章中,详细描述了最基本的自定义圆环的 ...

  6. Android 自定义view 图片编辑(画圆,画长方形,手势缩放)

    Android 自定义view 图片编辑(画圆,画长方形,手势缩放) package com.bridgetek.yqm.view; import android.annotation.Suppres ...

  7. Android自定义View之Paint绘制文字和线

    Android自定义View系列 Android自定义View注意事项 Android自定义View之图像的色彩处理 Android自定义View之Canvas Android自定义View之轻松实现 ...

  8. Android自定义View精品(CustomCalendar-定制日历控件)

    版权声明:本文为openXu原创文章[openXu的博客],未经博主允许不得以任何形式转载 目录: 文章目录 1.分析 2.自定义属性 3.onMeasure() 4.onDraw() ①.绘制月份 ...

  9. Android自定义View初探(二)——仿360垃圾清理

    明天就是五一劳动节了,在这里先祝各位程序猿劳动节快乐,别在加班了! 自从尝试过写自定义View(Android自定义View初探(一)--饼图)之后,每当看到别人的应用时,总是在想别人的实现方式,或许 ...

最新文章

  1. 450刀的eGPU vs 谷歌Colab,谁更划算?
  2. 关于Nginx的limit_conn模块的思考
  3. 【百家稷学】深度学习在计算摄影中的方法与应用(vivo技术分享)
  4. 数据库连接池技术--c3p0
  5. RBAC模型:设计思路
  6. python创建脚本文件_python创建文件备份的脚本
  7. 微信支付+服务器+php代码,php 微信支付企业付款(示例代码)
  8. 菜鸟教程 之 JavaScript 函数(function)
  9. 踵事增华:新形势下如何高效撰写科技论文!
  10. c语言查看变量类型_c语言外部链接的静态变量的四种类型
  11. 深入实践Spring Boot1.3 使用Spring Boot
  12. flink source和sink
  13. 03-26 网络流量分析
  14. 安卓c语言文档下载,C语言.NET技术09.doc
  15. Mac OS X上安装 Ruby运行环境
  16. 切底删掉顽固360的.dll文件、删除删不掉的一些文件
  17. 打表法判断素数 c语言,素数打表(4种方法)
  18. Nginx中安装免费SSL证书开启Https请求
  19. php 正则 tr,js正则匹配table tr
  20. DIV+CSS布局-PxCook工具的简单使用

热门文章

  1. element ui Table表格数据筛选功能实现
  2. 使用PIL库获取图片的二进制/01文本
  3. Java的二十三种设计模式
  4. python通过SMIELS查询CAS号
  5. PostgreSQL 实时高效搜索 - 全文检索、模糊查询、正则查询、相似查询、ADHOC查询...
  6. LaTex 插入表格
  7. 【MATLAB第1期】LSTM/GRU网络回归/分类预测改进与优化合集(含录屏操作,持续更新)
  8. IDEA解决clean,install时报Java head speace异常
  9. uni-app vue axios跨域访问问题
  10. 浅析用IRR判断贷款是否贵了