本系列自定义View全部采用kt
**系统: **mac
android studio: 4.1.3
**kotlin version:**1.5.0
gradle: gradle-6.5-bin.zip
本篇效果:
最近在bilibili看到一个跑马灯光圈效果挺好, 参考着思路写了一下.
bilibili地址,美中不足的是这是html代码 QaQ
如图所示:
tips: 图片截取自上方bilibili视频
换到android中直接将view当作背景层, 在利用Canvas绘制跑马灯层即可
// 设置view圆角
outlineProvider = object : ViewOutlineProvider() {override fun getOutline(view: View, outline: Outline) {// 设置圆角率为outline.setRoundRect(0, 0, view.width, view.height, RADIUS)}
}
clipToOutline = true
这段代码网上找的,源码还没有看, 有机会再看吧.

来看看当前效果:
这几个字可能有点抽象,所以来看看要完成的效果:
接下来只需要吧黄框外面和里面的的去掉就完成了旋转的效果:
去掉外面:

去掉里面:

这都是html效果,接下来看看android怎么写:
class ApertureView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {companion object {val DEF_WIDTH = 200.dpval DEF_HEIGHT = DEF_WIDTHprivate val RADIUS = 20.dp}private val paint = Paint(Paint.ANTI_ALIAS_FLAG)private val rectF by lazy {val left = 0f + RADIUS / 2fval top = 0f + RADIUS / 2fval right = left + DEF_WIDTH - RADIUSval bottom = top + DEF_HEIGHT - RADIUSRectF(left, top, right, bottom)}override fun onDraw(canvas: Canvas) {val left = rectF.left + rectF.width() / 2fval right = rectF.right + rectF.width()val top = rectF.top + rectF.height() / 2fval bottom = rectF.bottom + rectF.height() / 2f// 绘制渐变view1paint.color = Color.GREENcanvas.drawRect(left, top, right, bottom, paint)// 绘制渐变view2paint.color = Color.REDcanvas.drawRect(left, top, -right, -bottom, paint)}
}
这里就是计算偏移量等,都比较简单:
因为咋们是view,并且已经测量了view的宽和高,所以超出的部分就不展示了
这段代码比较简单,直接开一个animator即可
private val animator by lazy {val animator = ObjectAnimator.ofFloat(this, "currentSpeed", 0f, 360f)animator.repeatCount = -1animator.interpolator = nullanimator.duration = 2000Lanimator}var currentSpeed = 0fset(value) {field = valueinvalidate()}override fun onDraw(canvas: Canvas) {// withSave 保存画布canvas.withSave {// 画布中心点旋转canvas.rotate(currentSpeed, width / 2f, height / 2f)// 绘制渐变view1 绘制渐变view2...}
}
去除里面部分有2种方式
方式一:
private val path by lazy {Path().also { it.addRoundRect(rectF, RADIUS, RADIUS, Path.Direction.CCW) }
}override fun onDraw(canvas: Canvas) {// withSave 保存画布canvas.withSave {canvas.clipOutPath(path)// 画布中心点旋转canvas.rotate(currentSpeed, width / 2f, height / 2f)// 绘制渐变view1 ..view2...}
}
方式二:
override fun onDraw(canvas: Canvas) {// withSave 保存画布canvas.withSave {// 画布中心点旋转canvas.rotate(currentSpeed, width / 2f, height / 2f)// 绘制渐变view1// 绘制渐变view2}paint.color = Color.BLACKcanvas.drawRoundRect(rectF, RADIUS, RADIUS, paint)
}
来看看当前效果:
但是现在看起来还是有一点生硬, 可以让view渐变一下
private val color1 by lazy {LinearGradient(width * 1f,height / 2f,width * 1f,height * 1f,intArrayOf(Color.TRANSPARENT, Color.RED), floatArrayOf(0f, 1f),Shader.TileMode.CLAMP)
}private val color2 by lazy {LinearGradient( width / 2f,height / 2f,width / 2f, 0f,intArrayOf(Color.TRANSPARENT, Color.GREEN), floatArrayOf(0f, 1f),Shader.TileMode.CLAMP)
}override fun onDraw(canvas: Canvas) {
//canvas.withSave {canvas.rotate(currentSpeed, width / 2f, height / 2f)...// 绘制渐变view1paint.shader = color1canvas.drawRect(left1, top1, right1, bottom1, paint)paint.shader = null// 绘制渐变view2paint.shader = color2canvas.drawRect(left1, top1, -right1, -bottom1, paint)paint.shader = null}// 中间rectcanvas.drawRoundRect(rectF, RADIUS, RADIUS, paint)
}
这样一来,就更有感觉了
效果图:
基本效果就完成了,那么如何给其他view也可以轻松的添加这个炫酷的边框呢?
很显然,view是办不到的,所以我们只能自定义viewgroup
代码没有改变,只是在自定义viewgroup时,onDraw() 不会回调, 因为viewgroup主要就是用来管理view的,所以要想绘制viewgroup最好是重写dispatchDraw()方法,
在dispatchDraw()方法中,需要注意的是 super.dispatchDraw(canvas), 这个super中会绘制children,
所以为了避免 view被跑马灯背景覆盖,需要将super.dispatchDraw(canvas) 写到最后一行
#ApertureViewGroup.ktoverride fun dispatchDraw(canvas: Canvas) {val left1 = width / 2fval top1 = height / 2fval right1 = left1 + widthval bottom1 = top1 + widthcanvas.withSave {canvas.rotate(currentSpeed, width / 2f, height / 2f// 绘制渐变view1paint.shader = color1canvas.drawRect(left1, top1, right1, bottom1, paint)paint.shader = nullif (mColor2 != -1) {// 绘制渐变view2paint.shader = color2canvas.drawRect(left1, top1, -right1, -bottom1, paint)paint.shader = null}}paint.color = mMiddleColorcanvas.drawRoundRect(rectF, mBorderAngle, mBorderAngle, paint)// 一定要写到最后一行,否则children会被跑马灯覆盖掉super.dispatchDraw(canvas)}
最后在调用的时候直接:
本篇代码比较简单,不过这个思路确实挺好玩的!
最终效果:
完整代码
原创不易,您的点赞就是对我最大的帮助!
android 自定义View 视差动画
android自定义View: 绘制图表(一)
android 自定义view: 矩形图表(二)
android 自定义View:仿QQ拖拽效果
android 浅析RecyclerView回收复用机制及实战(仿探探效果)