• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

android 自定义view: 跑马灯-光圈

武飞扬头像
s10g
帮助1

本系列自定义View全部采用kt

**系统: **mac

android studio: 4.1.3

**kotlin version:**1.5.0

gradle: gradle-6.5-bin.zip

本篇效果:

学新通

前沿

最近在bilibili看到一个跑马灯光圈效果挺好, 参考着思路写了一下.

bilibili地址,美中不足的是这是html代码 QaQ

实现思路

  • 将效果分为3层
    • 第一层: 背景
    • 第二层: 跑马灯光圈
    • 第三层: 展示区

如图所示:

学新通

tips: 图片截取自上方bilibili视频

换到android中直接将view当作背景层, 在利用Canvas绘制跑马灯层即可

将View圆角化

// 设置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.dp
        val DEF_HEIGHT = DEF_WIDTH
        private val RADIUS = 20.dp
    }

    private val paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val rectF by lazy {
        val left = 0f   RADIUS / 2f
        val top = 0f   RADIUS / 2f
        val right = left   DEF_WIDTH - RADIUS
        val bottom = top   DEF_HEIGHT - RADIUS
        RectF(left, top, right, bottom)
    }

    override fun onDraw(canvas: Canvas) {
        val left = rectF.left   rectF.width() / 2f
        val right = rectF.right   rectF.width()
        val top = rectF.top   rectF.height() / 2f
        val bottom = rectF.bottom   rectF.height() / 2f

        // 绘制渐变view1
        paint.color = Color.GREEN
        canvas.drawRect(left, top, right, bottom, paint)

        // 绘制渐变view2
        paint.color = Color.RED
        canvas.drawRect(left, top, -right, -bottom, paint)

    }
}
学新通

这里就是计算偏移量等,都比较简单:

学新通

因为咋们是view,并且已经测量了view的宽和高,所以超出的部分就不展示了

跑马灯动起来

这段代码比较简单,直接开一个animator即可

 private val animator by lazy {
   val animator = ObjectAnimator.ofFloat(this, "currentSpeed", 0f, 360f)
   animator.repeatCount = -1
   animator.interpolator = null
   animator.duration = 2000L
   animator
 }

var currentSpeed = 0f
  set(value) {
    field = value
    invalidate()
  }
        
override fun onDraw(canvas: Canvas) {

  // withSave 保存画布
  canvas.withSave {
    
  // 画布中心点旋转
  canvas.rotate(currentSpeed, width / 2f, height / 2f)
    // 绘制渐变view1 绘制渐变view2
    ...
  }
}
学新通

学新通

'去掉’里面

去除里面部分有2种方式

  • 方式一: 利用 clipOutPath() 来clip掉中间区域, 这个api对版本有要求
  • 方式二: 重新绘制一个 RoundRect() 来覆盖掉中间区域

方式一:

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.BLACK
  canvas.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)
   ...
    // 绘制渐变view1
    paint.shader = color1
    canvas.drawRect(left1, top1, right1, bottom1, paint)
    paint.shader = null

    // 绘制渐变view2
    paint.shader = color2
    canvas.drawRect(left1, top1, -right1, -bottom1, paint)
    paint.shader = null
  }

  // 中间rect
  canvas.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.kt

override fun dispatchDraw(canvas: Canvas) {
        val left1 = width / 2f
        val top1 = height / 2f

        val right1 = left1   width
        val bottom1 = top1   width
        canvas.withSave {
            canvas.rotate(currentSpeed, width / 2f, height / 2f
            // 绘制渐变view1
            paint.shader = color1
            canvas.drawRect(left1, top1, right1, bottom1, paint)
            paint.shader = null

            if (mColor2 != -1) {
                // 绘制渐变view2
                paint.shader = color2
                canvas.drawRect(left1, top1, -right1, -bottom1, paint)
                paint.shader = null
            }
        }

        paint.color = mMiddleColor
        canvas.drawRoundRect(rectF, mBorderAngle, mBorderAngle, paint)

				// 一定要写到最后一行,否则children会被跑马灯覆盖掉
        super.dispatchDraw(canvas)
    }
学新通

最后在调用的时候直接:

<ApertureViewGroup
    android:layout_width="200dp"
    android:layout_height="200dp"

    // 边框颜色
    android:background="@color/cccccc"
                                                      
		// 边框宽度                                            
    app:aperture_border_width="50dp"
                      
		// 边框角度
    app:aperture_border_angle="20dp"                                               
		
		// 渐变颜色1
    app:aperture_color1="@color/purple_200"
                                                                                              
		// 渐变颜色2 如果不写,默认只有一个渐变在跑马灯
    app:aperture_color2="@color/color_FFC107"
                                                      	
		// 旋转时间
    app:aperture_duration="3000"
                                                      
		// 中间空心颜色
    app:aperture_middle_color="@color/white">

    <XXXX View
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center" />
</com.example.customviewproject.f.f2.ApertureViewGroup>
学新通

本篇代码比较简单,不过这个思路确实挺好玩的!

最终效果:

学新通

完整代码

原创不易,您的点赞就是对我最大的帮助!

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgbjkca
系列文章
更多 icon
同类精品
更多 icon
继续加载