注:该文章同步发布到稀土掘金:链接

前言

由于通勤时间较长,在路上总会有时间刷刷文章。稀土掘金就是常用的一个app(这里非广告,哈哈哈)。前段时间,发表了篇文章:# 使用CollapsingToolbarLayout高仿稀土掘金个人中心页,也是跟它相关的。今天再来一篇,不是什么大技术,而是我们常用的自定义view那套东西,只是觉得效果精美,就想自己实现下~先上图:

实现

先分析下效果:

  • 字体部分内容高亮
  • 高亮部分为平行四边形,而非矩形
    实现思路:先绘制浅色字体,再绘制深色字体,不过深色字体只显示平行四边形部分区域。下边直接上代码:

自定义属性

在values目录,创建attrs.xml文件,用于定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="FlickerText"><attr name="text" format="string" /><attr name="text_size" format="dimension" /><attr name="flick_precent" format="float" /><attr name="text_normal_color" format="color|reference" /><attr name="text_flick_color" format="color|reference" /></declare-styleable></resources>

这里主要包含了几个属性:

  • 显示的文本内容
  • 显示的文本字体大小
  • 高亮的四边形的宽度比例
  • 默认的字体颜色
  • 高亮的字体颜色

自定义FlickerView,继承于View

class FlickerText : View {private var minWidth = 0private var minHeight = 0private lateinit var paint: Paintprivate var textSize = 120private var showText: String = ""private var normalColor = Color.parseColor("#F0F0F2")private var flickColor = Color.parseColor("#DCDCDC")private var flickPercent = 0.16fprivate var clipLeft = -VERTICALOFFSETprivate var path: Path = Path()constructor(context: Context) : super(context) {init(null, 0, 0)}constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) {init(attributeSet, 0, 0)}constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int) : super(context,attributeSet,defStyleAttr) {init(attributeSet, defStyleAttr, 0)}

这里给出了自定义属性的默认值

获取配置属性值

private fun init(attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) {// 获取配置属性context.theme.obtainStyledAttributes(attrs,R.styleable.FlickerText,0, 0).apply {try {showText = getString(R.styleable.FlickerText_text).toString()textSize =getDimensionPixelSize(R.styleable.FlickerText_text_size, textSize)normalColor = getColor(R.styleable.FlickerText_text_normal_color, normalColor)flickColor = getColor(R.styleable.FlickerText_text_flick_color, flickColor)flickPercent = getFloat(R.styleable.FlickerText_flick_precent, flickPercent)} finally {recycle()}}// 初始化画笔相关paint = Paint()paint.isAntiAlias = truepaint.textSize = textSize.toFloat()val textBound = Rect()paint.getTextBounds(showText, 0, showText.length, textBound)minWidth = textBound.width()minHeight = textBound.height()
}

通过obtainStyledAttributes获取到在xml中配置的自定义属性值。这里还根据设置画笔的字体大小,计算出需要正常显示完整文本需要的宽高

计算View高度

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {val widthMode = MeasureSpec.getMode(widthMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val heightMode = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)var width = 0var height = 0if (widthMode == MeasureSpec.EXACTLY) {width = widthSize} else {width = min(widthSize, minWidth)}if (heightMode == MeasureSpec.EXACTLY) {height = heightSize} else {height = min(heightSize, minHeight)}setMeasuredDimension(width, height)
}

这里主要做了两层判断:

  • 假如mode为EXACTLY,说明指定了具体值,则直接使用
  • 假如mode为AT_MOST或UNSPECIFIED,判断父布局提供的大小与上方计算出的显示完整文本需要的大小,取最小值,保证不会超过父布局提供的大小

绘制

这才是显示效果的重点~

override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)paint.color = normalColorcanvas?.drawText(showText, 0f, height * 0.5f, paint)path.reset()path.moveTo(clipLeft, 0f)path.lineTo(clipLeft + width * flickPercent, 0f)path.lineTo(clipLeft + width * flickPercent + VERTICALOFFSET, height.toFloat())path.lineTo(clipLeft + VERTICALOFFSET, height.toFloat())paint.color = flickColorcanvas?.clipPath(path)canvas?.drawText(showText, 0f, height * 0.5f, paint)clipLeft += 5fif (clipLeft > width) {clipLeft = -VERTICALOFFSET}invalidate()
}
  1. 这里主要使用了canvas的clipPath函数,该函数会裁剪画布,并根据设置的模式,显示特定效果(这里将先绘制的描述为A,后绘制的描述为B):
  • DIFFERENCE:A不同于B的部分显示出来
  • REPLACE:显示B的部分
  • REVERSE_DIFFERENCE:B中不同于A的部分显示出来
  • INTERSECT:A和B的交集
  • UNION:A和B的全集
  • XOR:全集形状减去交集形状之后的部分
// 查看api,默认使用的是Region.Op.INTERSECT
public boolean clipPath(@NonNull Path path) {return clipPath(path, Region.Op.INTERSECT);
}
  1. Path的定义,就是组装成一个平行四边形。每次重绘后,需要调用path.reset()清空之前的路径。
  2. clipLeft自增是为了让高亮部分逐渐往右滚动显示

总结与拓展

总结

其实实现起来,效果很简单,主要就是使用了canvas的clipPath函数。但是可能由于平时少用,所以没有注意到。所以有空还是多看下源码,可以发现些有趣的东西。

拓展

  1. 这里主要涉及到自定义view的一些知识,官方有相关的一些介绍:官方自定义view教程
  2. Paint也有个类似的api:setXfermode
Set or clear the transfer mode object. A transfer mode defines how source pixels (generate by a drawing command) are composited with the destination pixels (content of the render target).
Pass null to clear any previous transfer mode. As a convenience, the parameter passed is also returned.public Xfermode setXfermode(Xfermode xfermode) {return installXfermode(xfermode);
}

可以通过该api实现图像混合模式,PorterDuffXfermode主要包含了以下几种模式:

之前一些抽奖的橡皮擦功能就可以通过这种方式实现。具体的就不多说~

最后,按照惯例附上demo地址:gitee-demo

自定义view高仿稀土掘金loading闪动字体效果相关推荐

  1. 使用CollapsingToolbarLayout高仿稀土掘金个人中心页

    注:该文章也同步更新到稀土掘金:链接 前言 CollapsingToolbarLayout是android MaterialDeign提供的一个组件,通过搭配AppBarLayout可实现toolba ...

  2. 自定义View高仿懂球帝我是教练效果

    前言 这几天很多欧洲球队来中国进行热身赛,不知道喜欢足球的各位小伙伴们有没有看球.喜欢足球的朋友可能知道懂球帝APP,鄙人也经常使用这个应用,里面有一个我是教练的功能挺好玩,就是可以模拟教练员的身份, ...

  3. Android自定义View分享——仿微信朋友圈图片合并效果

    写在前面 笔者近来在学习Android自定义View,收集了一些不算复杂但又"长得"还可以的自定义View效果实现,之前分享过两个效果:一个水平的进度条,一个圆形温度显示器,如果你 ...

  4. Android 自定义view 高仿小米闹钟

    效果图: 代码github地址:https://github.com/chenzongwen/MiClockView

  5. android 自定义取色器,【Android自定义View】仿Photoshop取色器ColorPicker(二)

    ColorPicker 一款仿Photoshop取色器的Android版取色器. 前言 上一篇已经简单介绍了ColorPicker的项目结构以及两种颜色空间,接下来我们详细解析一下ColorPicke ...

  6. 自定义xy组 android,Android自定义view之仿支付宝芝麻信用仪表盘示例

    自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 接着在构造方法里初始化自定义属性和画笔: private void initAttr(At ...

  7. Android自定义View之仿QQ运动步数进度效果

    文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...

  8. Android自定义view之仿支付宝芝麻信用仪表盘 ---by ccy

    自定义view练习 仿支付宝芝麻信用的仪表盘 对比图: 首先是自定义一些属性,可自己再添加,挺基础的,上代码 <?xml version="1.0" encoding=&qu ...

  9. 自定义View之仿淘宝详情页

    自定义View之仿淘宝详情页 转载请标明出处: http://blog.csdn.net/lisdye2/article/details/52353071 本文出自:[Alex_MaHao的博客] 项 ...

最新文章

  1. html边框绕着图片,CSS 边框
  2. P1772 [ZJOI2006]物流运输
  3. 【SmartJob】【隔离】每天定时掉线问题解决:隔离定期重启脚本更新
  4. linux suse 安装redis,suse 安装redis(示例代码)
  5. 多决策决策树_授权员工决策时,意图就是一切
  6. 小牛485通讯原理_plc和变频器通讯接线图详解
  7. 从零开始刷Leetcode——数组(532.561)
  8. oracle没有正常启动,Oracledbstart无法正常启动处理办法
  9. python 遍历对象_Python遍历对象属性
  10. 【有手就行】电脑蓝屏恶搞(附带一键复原方法)
  11. [源码]vb6鼠标连点器
  12. 网络赚钱从入门到精通
  13. Android UI开发:AlertDialog对话框
  14. 新买的笔记本计算机内存不足,笔记本内存不够怎么办_笔记本内存不足如何解决...
  15. 我对“渗透性测试”的理解
  16. Gabor滤波器进行纹理分割
  17. hdu校赛—1004
  18. 新媒体环境下“沉默的双螺旋”
  19. 边云协同的优点_边缘计算的云边协同
  20. c++ IO多路复用

热门文章

  1. 织梦dede建站仿站入门到精通
  2. java中利用itext编辑pdf
  3. andriod studio 的textview控件字体的设置
  4. 【优化求解】基于缎蓝园丁鸟优化 (SBO)求解单目标问题matlab源码
  5. 三星新技术使WiFi提速5倍:每秒传输575MB数据
  6. [589]IDM下载器
  7. 用Java实现邮件收发
  8. 学习软件设计的非功能性需求
  9. 谷歌Android Beta,谷歌Android P Beta发布 八大品牌可尝鲜
  10. KCon 2019兵器谱正式招募 免费邀约安全神兵利器