View->不同刻度大小的刻度尺View

news2024/9/23 5:24:23

XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/black"
    android:gravity="center">
    <com.gallery20.app.MyLineSeekBar
        android:layout_width="match_parent"
        android:layout_height="100dp"
        android:layout_marginStart="22dp"
        android:layout_marginEnd="22dp"
        android:layout_marginBottom="60dp" />
</LinearLayout>

自定义View代码

class MyLineSeekBar(context: Context, attrs: AttributeSet) : View(context, attrs) {
    private var mLineWidthArray = IntArray(5).apply {
        this[0] = 40
        this[1] = 60
        this[2] = 80
        this[3] = 100
        this[4] = 120
    }
    private var mProgress: Float = 0f
    private var mCurIndex: Int = 0
    private var mAnimator: Animator? = null

    private val mGreyCirclePaint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.FILL
        color = Color.GRAY
    }

    private val mGreyLinePaint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.STROKE
        strokeWidth = dpToPx(context, 2f)
        color = Color.GRAY
    }

    private val mWhiteCirclePaint = Paint().apply {
        isAntiAlias = true
        style = Paint.Style.FILL
        color = Color.WHITE
    }


    override fun onTouchEvent(event: MotionEvent): Boolean {
        val lineWidthArray = mLineWidthArray
        val curX = event.x
        val minLineWidth = lineWidthArray[0] / 2f
        val maxLineWidth = lineWidthArray[lineWidthArray.size - 1] / 2f
        val left = minLineWidth
        val right = measuredWidth - minLineWidth - maxLineWidth

        mProgress = max(0f, min(1f, (curX - left) / (right - left)))

        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mAnimator?.cancel()
            }

            MotionEvent.ACTION_MOVE -> {
                notifyLineWidthChanged()
            }

            MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                animateToClosestPoint()
            }
        }
        invalidate()
        return true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val lineWidthArray = mLineWidthArray
        val minLineWidth = lineWidthArray[0] / 2f
        val maxLineWidth = lineWidthArray[lineWidthArray.size - 1] / 2f
        val left = minLineWidth
        val right = measuredWidth - minLineWidth - maxLineWidth
        // 1. 绘制灰色线
        canvas.drawLine(left, measuredHeight / 2f, right, measuredHeight / 2f, mGreyLinePaint)
        val perPointDistance = (right - left) / (lineWidthArray.size - 1)

        // 2. 绘制灰色圆
        for (i in lineWidthArray.indices) {
            val lineWidth = lineWidthArray[i]
            val step = i * perPointDistance
            canvas.drawCircle(
                left + step, measuredHeight / 2f, lineWidth / 2f, mGreyCirclePaint
            )
        }

        // 3. 绘制白色圆
        canvas.drawCircle(
            left + mProgress * (right - left),
            measuredHeight / 2f,
            getCurrentCircleRadius() / 2f,
            mWhiteCirclePaint
        )
    }
    
    fun animateToClosestPoint() {
        mAnimator?.cancel()
        val animator = getAnimateToClosestPoint(mProgress)
        animator.addUpdateListener {
            mProgress = it.animatedValue as Float
            invalidate()
        }
        animator.addListener(object : AnimatorListenerAdapter() {
            override fun onAnimationEnd(animation: Animator) {
                notifyLineWidthChanged()
                mAnimator = null
            }
        })
        mAnimator = animator
        animator.start()
    }

    fun notifyLineWidthChanged() {
        val newIndex = getClosestPointIndex(mProgress)
        if (newIndex != mCurIndex) {
            mCurIndex = newIndex
        }
    }

    // 传入浮点数的进度,返回进度更新动画
    fun getAnimateToClosestPoint(progress: Float): ValueAnimator {
        val closestProgress = getClosestPointIndex(progress) * 1f / (mLineWidthArray.size - 1)
        val animator = ValueAnimator()
        animator.setFloatValues(progress, closestProgress)
        animator.interpolator = DecelerateInterpolator()
        animator.duration = 200
        return animator
    }

    // 传入浮点数的进度,返回最接近的pointIndex
    fun getClosestPointIndex(progress: Float): Int {
        val perPointProgress = 1f / (mLineWidthArray.size - 1)
        val minIndex = Math.floor((progress / perPointProgress).toDouble()).toInt()
        return if (minIndex < mLineWidthArray.size - 1) {
            val minIndexDistance = Math.abs(progress - minIndex * perPointProgress)
            val maxIndexDistance = Math.abs(progress - (minIndex + 1) * perPointProgress)
            if (minIndexDistance < maxIndexDistance) minIndex else minIndex + 1
        } else {
            minIndex
        }
    }
    
    private fun getCurrentCircleRadius(): Float {
        val lineWidthArray = mLineWidthArray
        val perPointProgress = 1f / (lineWidthArray.size - 1)
        // floorIndex 为当前进度所在的区间的左索引, 索引分别为 0, 1, 2, 3, 4
        val floorIndex = Math.floor((mProgress / perPointProgress).toDouble()).toInt() // 向下取整
        return if (floorIndex == 0 || floorIndex == lineWidthArray.size - 1) {
            lineWidthArray[floorIndex].toFloat()
        } else {
            // (lineWidthArray[floorIndex + 1] - lineWidthArray[floorIndex]) 是两种画笔宽度的差值
            lineWidthArray[floorIndex] + (lineWidthArray[floorIndex + 1] - lineWidthArray[floorIndex]) * (mProgress - floorIndex * perPointProgress) / perPointProgress
        }
    }
}

效果图

在这里插入图片描述

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

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

相关文章

如何在centos7安装Docker

在centOS7中我们可以使用火山引擎镜像源镜像安装Docker,以下是具体的安装步骤。 step 1: 安装必要的一些系统工具 sudo yum install -y yum-utils Step 2: 添加软件源信息 sudo yum-config-manager --add-repo https://mirrors.ivolces.com/docker/linux/centos/docker-ce.r…

改进Transformer模型其实也不难

声明&#xff1a;文章是从本人公众号中复制而来&#xff0c;因此&#xff0c;想最新最快了解各类智能优化算法及其改进的朋友&#xff0c;可关注我的公众号&#xff1a;强盛机器学习&#xff0c;不定期会有很多免费代码分享~ 目录 原理简介 数据介绍 结果展示 完整代码 之前…

WAIC2024 上海 | Gooxi 全面展示智算新成果,加速人工智能落地应用

浦江之畔&#xff0c;大咖云集&#xff1b;智能浪潮&#xff0c;奔涌不息。7月4日&#xff0c;被誉为人工智能界风向标的世界人工智能大会暨人工智能全球治理高级别会议在上海盛大召开&#xff0c;Gooxi此次携最新AI服务器以及解决方案参与&#xff0c;以算为擎赋能新质生产力&…

LLM之RAG实战(四十一)| 使用LLamaIndex和Gemini构建高级搜索引擎

Retriever 是 RAG&#xff08;Retrieval Augmented Generation&#xff09;管道中最重要的部分。在本文中&#xff0c;我们将使用 LlamaIndex 实现一个结合关键字和向量搜索检索器的自定义检索器&#xff0c;并且使用 Gemini大模型来进行多个文档聊天。 通过本文&#xff0c;我…

人工智能在三级淋巴结:肿瘤浸润淋巴细胞领域的系统研究进展|顶刊速递·24-07-08

小罗碎碎念 本期文献主题&#xff1a;人工智能在三级淋巴结/肿瘤浸润淋巴细胞领域的系统分析 关于三级淋巴结和肿瘤浸润淋巴细胞的文献&#xff0c;会是接下来的分析重点&#xff0c;期间也会穿插临床文献&项目复现的推文。 另外再说点科研道路上的题外话&#xff0c;也算是…

Python 爬虫 tiktok API接口获取tiktok用户关注列表

此接口可获取tiktok用户关注列表。若有需要&#xff0c;请点击文末链接联系我们。 详细采集页面如下https://www.tiktok.com/quanap_official 请求API http://api.xxxx.com/tt/user/following?user_id7252644648840381445&count10&offset0&tokentest 请求参数 返…

拿客户电脑,半小时完成轮播组件开发!被公司奖励500!

故事背景 原文链接&#xff1a;拿客户电脑&#xff0c;用豆包IDE逆天改命完成需求紧急开发&#xff01;被公司奖励500&#xff01; 前几天&#xff0c;业务拉了一个大客户&#xff0c;客户需要先看我们做的样本项目&#xff08;类似于官网首页&#xff09;&#xff0c;然后才会…

更新GCC版本问题处理(Could not resolve host: mirrorlist.centos.org;)更换SCL配置源/SCL后yum使用不了

SCL&#xff1a; 在 Linux 系统中&#xff0c;更新 GCC&#xff08;GNU Compiler Collection&#xff09;编译器需要使用 Software Collections (SCL) 库的原因主要有以下几点&#xff1a; https://wiki.centos.org/AdditionalResources/Repositories/SCLhttps://wiki.centos…

游戏开黑语音-使用云服务器部署teamspeak服务(系统Ubuntu 20.04 LTS)

目录 前置物品服务器调整及部署1.重装系统2.换源3.下载teamspeak服务端并部署 连接服务器参考 前置物品 一台云服务器&#xff08;系统&#xff1a;Ubuntu 20.04 LTS) 服务器调整及部署 1.重装系统 在腾讯云官网的主机控制台内&#xff0c;选择重装系统 (由于之前为了快速和…

重庆交通大学数学与统计学院携手泰迪智能科技共建的“智能工作室”

2024年7月4日&#xff0c;重庆交通大学数学与统计学院与广东泰迪智能科技股份有限公司携手共建的“智能工作室”授牌仪式在南岸校区阳光会议室举行。此举标志着数统学院与广东泰迪公司校企合作新篇章的开启&#xff0c;也预示着学院在智能科技教育领域的深入探索和实践。 广东…

电源中电感底部需要铺地平面吗?

感有交变电流&#xff0c;电感底部铺铜会在地平面上产生涡流&#xff0c;涡流效应会影响功率电感的电感量&#xff0c;涡流也会增加系统的损耗&#xff0c;同时交变电流产生的噪声会增加地平面的噪声&#xff0c;会影响其他信号的稳定性。 在EMC方面来看&#xff0c;在电感底部…

作为产品经理,如何用大模型给我们赋能?非常详细,收藏我这篇就够了

作为一名产品经理&#xff0c;如果您考虑转行至大模型领域&#xff0c;您将能够将产品管理技能与大模型技术相结合&#xff0c;从而在产品开发和创新方面获得一系列好处。以下是转行大模型对产品经理的一些潜在益处&#xff1a; 更深入的技术理解&#xff1a;了解大模型技术将…

deepspeed huggingface传入参数 optimizer和lr_scheduler测试

Trainer中 首先&#xff1a; WarmupDecayLR --lr_scheduler_type linear WarmupLR --lr_scheduler_type constant_with_warmup 1 TrainArgument不传lr_scheduler_type、optim&#xff0c;warmup_steps15 ds config文件中定义如下&#xff1a; 注意&#xff1a;如果不在Trai…

java-spring boot光速入门教程(超详细!!)

目录 一、引言 1.1 初始化配置 1.2 整合第三方框架 1.3 后期维护 1.4 部署工程 1.5 敏捷式开发 二、SpringBoot介绍 spring boot 2.1 搭建一个spring boot工程 2.2 使用idea创建项目 2.3 在线创建姿势 2.4 项目的目录结构 2.5 项目的运行方式 2.6 yml文件格式 2…

无线麦克风哪个品牌音质最好,揭秘手机收音麦克风哪个牌子好!

随着全球直播和短视频行业的蓬勃发展&#xff0c;领夹麦克风因其便携性和出色的录音质量而备受青睐。用户在各种场合下追求清晰、真实的录音效果&#xff0c;领夹麦克风无疑是一个理想的选择。 然而&#xff0c;面对市场上琳琅满目的品牌和型号&#xff0c;想要挑选一款性能优…

计算机网络之无线局域网

1.无线局域网工作方式 工作方式&#xff1a;每台PC机上有一个无线收发机&#xff08;无线网卡&#xff09;&#xff0c; 它能够向网络上的其他PC机发送和接受无线电信号。 与有线以太网相似&#xff0c;无线局域网也是打包方式发送数据的。每块网卡都有一个永久的、唯一的ID号…

【原理+使用】DeepCache: Accelerating Diffusion Models for Free

论文&#xff1a;arxiv.org/pdf/2312.00858 代码&#xff1a;horseee/DeepCache: [CVPR 2024] DeepCache: Accelerating Diffusion Models for Free (github.com) 介绍 DeepCache是一种新颖的无训练且几乎无损的范式&#xff0c;从模型架构的角度加速了扩散模型。DeepCache利…

小白·使用Tesseract-OCR工具读取图片

1、直接pip安装 工具使用vscode和pycharm都可以。 这里介绍使用vscode的方法。 (1)、调出终端 (2)、安装依赖 (3)、编写代码 import pyocr import pyocr.builders from PIL import Image import re# 获取Tesseract-OCR工具 tools pyocr.get_available_tools() tool tools[…

使用 MFA 保护对企业应用程序的访问

多因素身份验证&#xff08;MFA&#xff09;是在授予用户访问特定资源的权限之前&#xff0c;使用多重身份验证来验证用户身份的过程&#xff0c;仅使用单一因素&#xff08;传统上是用户名和密码&#xff09;来保护资源&#xff0c;使它们容易受到破坏&#xff0c;添加其他身份…

C# 实现基于exe内嵌HTTPS监听服务、从HTTP升级到HTTPS 后端windows服务

由于客户需要把原有HTTP后端服务升级为支持https的服务&#xff0c;因为原有的HTTP服务是一个基于WINDOWS服务内嵌HTTP监听服务实现的&#xff0c;并不支持https, 也不像其他IIS中部署的WebAPI服务那样直接加载HTTPS证书&#xff0c;所以这里需要修改原服务支持https和服务器环…