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

news2025/1/17 23:04:48

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

**系统: **mac

android studio: 4.1.3

**kotlin version:**1.5.0

gradle: gradle-6.5-bin.zip

本篇效果:

8140FE3CF87738708E0C5D0E4F59704F

前沿

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

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

实现思路

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

如图所示:

Nov-28-2022 17-19-34

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

这段代码网上找的,源码还没有看, 有机会再看吧.

image-20221128173221355

来看看当前效果:

CD09F6ED6DBE6895E487C703B7DB64F0

自定义跑马灯光圈

这几个字可能有点抽象,所以来看看要完成的效果:

Nov-28-2022 17-45-34

接下来只需要吧黄框外面和里面的的去掉就完成了旋转的效果:

去掉外面:

Nov-28-2022 17-47-38

去掉里面:

Nov-28-2022 17-47-32

这都是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)

    }
}

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

542DD72464B89550F97E8BAD9EFE6FD5

因为咋们是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
    ...
  }
}
14162A8D36FFE0BEB6CD9B9D5A67446F

'去掉’里面

去除里面部分有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)
}

来看看当前效果:

B9B3733C51780A7AFB53CBA080582B20

但是现在看起来还是有一点生硬, 可以让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)
}

这样一来,就更有感觉了

效果图:

FBFD3920C18DA5E6821CA08C9CFB8052

基本效果就完成了,那么如何给其他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>

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

最终效果:

A051CC6A0481AE320B2371E271889D04

完整代码

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

  • android 自定义View 视差动画

  • android自定义View: 绘制图表(一)

  • android 自定义view: 矩形图表(二)

  • android 自定义View:仿QQ拖拽效果

  • android 浅析RecyclerView回收复用机制及实战(仿探探效果)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/44590.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spark系列之Spark启动与基础使用

title: Spark系列 第三章 Spark启动与基础使用 3.1 Spark Shell 3.1.1 Spark Shell启动 安装目录的bin目录下面&#xff0c;启动命令&#xff1a; spark-shell$SPARK_HOME/bin/spark-shell \ --master spark://hadoop10:7077 \ --executor-memory 512M \ --total-executor-…

长短期记忆网络(LSTM)重点!(自己汇集了很多资料在这篇博客)

文章目录参考资料推荐基础知识评论区精髓代码实现底层实现简洁实现参考资料推荐 心心念念 学了这么久 &#xff0c;终于学到第57集了。 参考一篇掘金的图文LSTM 李宏毅老师的手撕视频配套课件 27:39 开始手撕 看完了李沐老师的LSTM又去找了李宏毅老师的课程然后发现又多了个导…

西门子机床联网

一、设备信息确认 1、确认型号 数控面板拍照确认&#xff1a; 此系统为&#xff1a;西门子828D 还有一种情况是面板无任何版本信息&#xff0c;这时就需要进入系统里面再确认。 2、确认通讯接口 1、数控面板的后面 X130为网络标号 2、其他位置 其他位置一般是前面位置用…

H3C mstp+vrrp实验 新华三杯拆解

H3C mstpvrrp实验一、实验拓扑二、实验要求局域网规划&#xff1a;可靠性&#xff1a;三、实验配置&#xff08;一&#xff09;链路聚合1.创建链路聚合组2.检查&#xff08;二&#xff09;VLAN1.创建vlan2.放行vlan3.检查&#xff08;三&#xff09;MSTP1.配置MSTP域2.配置主备…

通俗易懂的java设计模式(1)-单例模式

什么是单例模式&#xff1f; 单例模式是java中最简单的一种设计模式 需要注意的问题&#xff1a; 1.单例类有且只能有一个实例 2.单例类必须自己创建出这个实例&#xff0c;并提供给外界 那么如何自己创建实例而不让外界创建呢&#xff1f;很简单&#xff0c;我们将无参的构造函…

麦芽糖-刀豆球蛋白A,maltose-ConcanavalinA,刀豆球蛋白A-PEG-麦芽糖

麦芽糖-刀豆球蛋白A,maltose-ConcanavalinA,刀豆球蛋白A-PEG-麦芽糖 中文名称&#xff1a;麦芽糖-刀豆球蛋白A 英文名称&#xff1a;maltose-ConcanavalinA 别称&#xff1a;刀豆球蛋白A修饰麦芽糖&#xff0c;ConA-麦芽糖 还可以提供PEG接枝修饰麦芽糖&#xff0c;麦芽糖…

SpringCloud-alibaba-Nacos 从理论到落地使用

Nacos: Dynamic Naming and Configuration Service 就是&#xff1a; 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。、 下面是生态图&#xff1a; Nacos EurekaConfig Bus 即 Nacos就是注册中心 配置中心的组合 他能干什么呢&#xff1f; 1、替代Eu…

防火墙firewalld

RHEL7中有几种防火墙共存&#xff1a;firewalld、iptables、ebtables等。基于iptables的防火墙默认不启动&#xff0c;但仍然可以继续使用。RHEL7默认使用firewalld作为防火墙&#xff0c;管理工具是firewall-cmd。RHEL7的内核版本是3.10&#xff0c;在此版本的内核里防火墙的包…

论文阅读2 Learning Hierarchy-Aware Knowledge Graph Embeddings for Link Prediction

目录 问题 创新 1、Introduction 2、相关工作 3、HAKE模型 原文&#xff1a;[1911.09419] Learning Hierarchy-Aware Knowledge Graph Embeddings for Link Prediction (arxiv.org) 问题 现有的知识图嵌入模型主要关注对称/反对称、反转和复合symmetry/antisymmetry, i…

EquiVSet

又搬来一个于最优子集的神经集合函数学习方法 集合函数被广泛应用于各种场景之中&#xff0c;例如商品推荐、异常检测和分子筛选等。在这些场景中&#xff0c;集合函数可以被视为一个评分函数&#xff1a;其将一个集合作为输入并输出该集合的分数。我们希望从给定的集合中选取…

【新知实验室-TRTC开发】实时音视频之欢度世界杯

目录 一、什么是TRTC 二、用5分钟跑通一个demo 1、开通腾讯云-TRTC 2、获取demo必须的两把钥匙 2.1输入应用名称 2.2下载对应的源码包&#xff08;手机、web、小程序等&#xff09; 2.3拿到钥匙 2.4完成 三、搭建一起看世界杯应用 1、解压源码&#xff08;耗时30S&#x…

Linux下top命令详解

Linux下top命令用法详解 作为一名Linux软件攻城狮&#xff0c;top命令大家应该并不陌生。top命令是Linux下常用的性能分析工具&#xff0c;能够实时显示系统中各个进程的资源占用状况。top可以动态显示过程,不断刷新当前状态。top命令提供了实时的对系统处理器的状态监视。它将…

Kotlin高仿微信-第5篇-主页-通讯录

Kotlin高仿微信-项目实践58篇详细讲解了各个功能点&#xff0c;包括&#xff1a;注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。 Kotlin高仿…

【博客547】keepalived实现vip的原理剖析

keepalived实现vip的原理剖析 keepalived实现vip的原理&#xff1a;vrrp gratuitous arp 1、vrrp vrrp更多细节参考&#xff1a;vrrp技术白皮书 相关术语&#xff1a; VRRP工作过程 VRRP的工作过程为&#xff1a; 1、虚拟路由器中的路由器根据优先级选举出Master。Master…

【Python】五、程序循环结构

文章目录实验目的一、掌握while语句二、掌握for循环和range()内建函数三、掌握循环语句嵌套四、掌握break语句和continue语句五、编写程序实现猜数字的游戏1.设计思路2.设计算法3.参考代码4.实验截图实验目的 掌握循环结构&#xff1b;培养学生动手查阅资料能力和解决实际问题的…

matlab实现线性参数的最小二乘法处理

一、实验目的 最小二乘法原理是一种在多学科领域中获得广泛应用的数据处理方法。通过实验要求掌握最小二乘法基本原理、正规方程以及组合测量的最小二乘法处理办法。 二、实验原理 &#xff08;1&#xff09;测量结果的最可信赖值应在残余误差平方和为最小的条件下求出&#…

破局模块总结 -- 宁向东的清华管理学课总结

1. 管理学就是要破局而出 为什么需要管理学&#xff1f;德鲁克说&#xff0c;我们需要选择正确的事情去做&#xff0c;并且把事情作对。 管理学是一个分析、权衡和决策的学问&#xff0c;分析、权衡和选择的目的是什么&#xff1f;就是要破局而出。 管理學是研究人類管理活動…

《Linux运维总结:基于快照模式迁移单节点elasticsearch数据(方案二)》

一、背景信息 说明&#xff1a;由于整个系统需要从互联网迁移到政务外网&#xff0c;elasticsearch作为其中一个组件&#xff0c;也需要将 所有索引数据 迁移到政务外网。 由于数据量比较大&#xff0c;所以使用快照的模式对elasticsearch数据进行备份及恢复操作&#xff0c;提…

[附源码]计算机毕业设计springboot病人跟踪治疗信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

接口测试当中的权限限制测试和状态机测试【杭州多测师_王sir】【杭州多测师】...

一、权限限制 权限限制我需要单独拎出来讲&#xff0c;它很重要&#xff01;很多系统中都存在系统管理员、普通用户等不同角色的用户&#xff0c;系统管理员拥有一些普通用户没有的权限&#xff0c;比如系统管理员可以发布公告&#xff0c;而普通用户则只能查看&#xff0c;系统…