Android-自定义三角形评分控件

效果图

序言

在移动应用开发中,显示数据的方式多种多样,直观的图形展示常常能带给用户更好的体验。本文将介绍如何使用Flutter创建一个自定义三角形纬度评分控件,该控件可以通过动画展示评分的变化,让应用界面更加生动。

实现思路及步骤

  1. 定义控件属性:首先需要定义控件的基本属性,如宽度、高度、最大评分以及每个顶点的评分值。
  2. 自定义绘制:使用自定义View绘制三角形和评分三角形,并在顶点处绘制空心圆点。
  3. 实现动画效果:使用属性动画ValueAnimator来控制评分动画,使每个顶点的评分从0逐渐增加到对应的评分值。

代码实现

定义自定义属性和布局文件

在res/values/attrs.xml中定义自定义属性:

   <declare-styleable name="TriangleRatingAnimView"><attr name="maxRating" format="integer" /><attr name="upRating" format="integer" /><attr name="leftRating" format="integer" /><attr name="rightRating" format="integer" /><attr name="strokeColor" format="color" /><attr name="strokeWidth" format="dimension" /><attr name="ratingStrokeColor" format="color" /><attr name="ratingStrokeWidth" format="dimension" /></declare-styleable>
创建自定义View类

首先,创建一个自定义View类TriangleRatingAnimView,用于绘制三角形和动画效果。

package com.yxlh.androidxy.demo.ui.ratingimport android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.util.TypedValue
import android.view.View
import androidx.core.content.withStyledAttributes
import androidx.core.graphics.ColorUtils
import androidx.interpolator.view.animation.LinearOutSlowInInterpolator
import com.yxlh.androidxy.Rfun Context.dpToPx(dp: Float): Float {return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, resources.displayMetrics)
}/*** 三角形评分控件* https://github.com/yixiaolunhui/AndroidXY*/
class TriangleRatingAnimView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0,
) : View(context, attrs, defStyleAttr) {var maxRating: Int = 5set(value) {field = valueinvalidate()}var upRating: Int = 0set(value) {field = valueanimateRating()}var leftRating: Int = 0set(value) {field = valueanimateRating()}var rightRating: Int = 0set(value) {field = valueanimateRating()}private var strokeColor: Int = Color.GRAYprivate var strokeWidth: Float = context.dpToPx(1.5f)private var ratingStrokeColor: Int = Color.REDprivate var ratingStrokeWidth: Float = context.dpToPx(2.5f)private var animatedUpRating = 0private var animatedLeftRating = 0private var animatedRightRating = 0private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {style = Paint.Style.STROKEcolor = strokeColorstrokeWidth = this@TriangleRatingAnimView.strokeWidth}private val outerPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {style = Paint.Style.STROKEcolor = ratingStrokeColorstrokeWidth = this@TriangleRatingAnimView.ratingStrokeWidth}private val fillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {style = Paint.Style.FILLcolor = ColorUtils.setAlphaComponent(ratingStrokeColor, (0.3 * 255).toInt())}private val circlePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {style = Paint.Style.STROKEcolor = ratingStrokeColorstrokeWidth = context.dpToPx(1.5f)}private val circleFillPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {style = Paint.Style.FILLcolor = Color.WHITE}init {context.withStyledAttributes(attrs, R.styleable.TriangleRatingAnimView) {maxRating = getInt(R.styleable.TriangleRatingAnimView_maxRating, 5)upRating = getInt(R.styleable.TriangleRatingAnimView_upRating, 0)leftRating = getInt(R.styleable.TriangleRatingAnimView_leftRating, 0)rightRating = getInt(R.styleable.TriangleRatingAnimView_rightRating, 0)strokeColor = getColor(R.styleable.TriangleRatingAnimView_strokeColor, Color.GRAY)strokeWidth = context.dpToPx(getDimension(R.styleable.TriangleRatingAnimView_strokeWidth, 2f))ratingStrokeColor = getColor(R.styleable.TriangleRatingAnimView_ratingStrokeColor, Color.RED)ratingStrokeWidth = context.dpToPx(getDimension(R.styleable.TriangleRatingAnimView_ratingStrokeWidth, 4f))}}private fun animateRating() {val animator = ValueAnimator.ofFloat(0f, 1f).apply {duration = 300interpolator = LinearOutSlowInInterpolator()addUpdateListener { animation ->val animatedValue = animation.animatedValue as FloatanimatedUpRating = (upRating * animatedValue).toInt()animatedLeftRating = (leftRating * animatedValue).toInt()animatedRightRating = (rightRating * animatedValue).toInt()invalidate()}}animator.start()}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val width = measuredWidth.toFloat()val height = measuredHeight.toFloat()val circleRadius = context.dpToPx(5f)val padding = circleRadius + context.dpToPx(2f)val p1 = width / 2 to paddingval p2 = padding to height - paddingval p3 = width - padding to height - padding// 绘制外部三角形val path = Path().apply {moveTo(p1.first, p1.second)lineTo(p2.first, p2.second)lineTo(p3.first, p3.second)close()}canvas.drawPath(path, paint)val centroidX = (p1.first + p2.first + p3.first) / 3val centroidY = (p1.second + p2.second + p3.second) / 3// 绘制顶点到重心的连线canvas.drawLine(p1.first, p1.second, centroidX, centroidY, paint)canvas.drawLine(p2.first, p2.second, centroidX, centroidY, paint)canvas.drawLine(p3.first, p3.second, centroidX, centroidY, paint)val dynamicP1 =centroidX + (p1.first - centroidX) * (animatedUpRating / maxRating.toFloat()) to centroidY + (p1.second - centroidY) * (animatedUpRating / maxRating.toFloat())val dynamicP2 =centroidX + (p2.first - centroidX) * (animatedLeftRating / maxRating.toFloat()) to centroidY + (p2.second - centroidY) * (animatedLeftRating / maxRating.toFloat())val dynamicP3 =centroidX + (p3.first - centroidX) * (animatedRightRating / maxRating.toFloat()) to centroidY + (p3.second - centroidY) * (animatedRightRating / maxRating.toFloat())// 绘制内部动态三角形val ratingPath = Path().apply {moveTo(dynamicP1.first, dynamicP1.second)lineTo(dynamicP2.first, dynamicP2.second)lineTo(dynamicP3.first, dynamicP3.second)close()}canvas.drawPath(ratingPath, outerPaint)canvas.drawPath(ratingPath, fillPaint)// 绘制动态点上的空心圆canvas.drawCircle(dynamicP1.first, dynamicP1.second, circleRadius, circlePaint)canvas.drawCircle(dynamicP1.first, dynamicP1.second, circleRadius - context.dpToPx(1.5f), circleFillPaint)canvas.drawCircle(dynamicP2.first, dynamicP2.second, circleRadius, circlePaint)canvas.drawCircle(dynamicP2.first, dynamicP2.second, circleRadius - context.dpToPx(1.5f), circleFillPaint)canvas.drawCircle(dynamicP3.first, dynamicP3.second, circleRadius, circlePaint)canvas.drawCircle(dynamicP3.first, dynamicP3.second, circleRadius - context.dpToPx(1.5f), circleFillPaint)}
}

定义Activity界面xml文件

在res/layout/activity_rating.xml中使用自定义View:

<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center_horizontal"android:gravity="center"android:orientation="vertical"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"><TextViewandroid:id="@+id/upText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="时间管理"android:textColor="@color/black"android:textSize="13sp"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.yxlh.androidxy.demo.ui.rating.TriangleRatingAnimViewandroid:id="@+id/triangleRatingAnimView"android:layout_width="300dp"android:layout_height="200dp"android:layout_centerInParent="true"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@+id/upText"app:leftRating="3"app:maxRating="10"app:ratingStrokeColor="@android:color/holo_red_dark"app:ratingStrokeWidth="4dp"app:rightRating="8"app:strokeColor="@android:color/darker_gray"app:strokeWidth="3dp"app:upRating="5" /><TextViewandroid:id="@+id/leftText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="成本控制"android:textColor="@color/black"android:textSize="13sp"app:layout_constraintTop_toBottomOf="@+id/triangleRatingAnimView"app:layout_constraintLeft_toLeftOf="@+id/triangleRatingAnimView"/><TextViewandroid:id="@+id/rightText"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="质量保证"android:textColor="@color/black"android:textSize="13sp"app:layout_constraintTop_toBottomOf="@+id/triangleRatingAnimView"app:layout_constraintRight_toRightOf="@+id/triangleRatingAnimView"/></androidx.constraintlayout.widget.ConstraintLayout><Buttonandroid:id="@+id/randomizeButton"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/triangleRatingAnimView"android:layout_centerHorizontal="true"android:layout_marginTop="20dp"android:text="更改数据" /></androidx.appcompat.widget.LinearLayoutCompat>
定义RatingActivity
package com.yxlh.androidxy.demo.ui.ratingimport android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.yxlh.androidxy.databinding.ActivityRatingBinding
import kotlin.random.Randomclass RatingActivity : AppCompatActivity() {private var binding: ActivityRatingBinding? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityRatingBinding.inflate(layoutInflater)setContentView(binding?.root)binding?.randomizeButton?.setOnClickListener {randomizeRatings()}}private fun randomizeRatings() {val random = Random(System.currentTimeMillis())val maxRating = 5 + random.nextInt(6)val upRating = 1 + random.nextInt(maxRating)val leftRating = 1 + random.nextInt(maxRating)val rightRating = 1 + random.nextInt(maxRating)binding?.triangleRatingAnimView?.apply {this.maxRating = maxRatingthis.upRating = upRatingthis.leftRating = leftRatingthis.rightRating = rightRatinginvalidate()}}
}

通过以上步骤和代码,我们可以创建一个带动画效果的三角形纬度评分控件,使评分展示更加生动和直观。

详情可见:github.com/yixiaolunhui/AndroidXY

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

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

相关文章

转行3年涨薪300%,我总结了一套产品经理快速入门指南!

想转行的产品小白&#xff0c;初期一定会遇到这个问题——我要如何 0 基础转行产品经理&#xff1f; 要想 0 基础快速转行产品经理&#xff0c;我通过个人实践总结了 5 个关键点&#xff0c;可以参考。 一、熟悉产品经理的工作全流程 转行的产品小白&#xff0c;首先要建立产…

ABtest假设检验知识|配对检验|比率检验|单向表-列联表检验

文章目录 1 假设检验基础2 一般假设检验2.1 假设检验包2.2 sample - 点击转化率2.2.1 问题描述2.2.2 实验设计2.2.3 数据处理2.2.4 方差齐性检验2.2.5 假设检验2.2.6 结果分析 3 检验两个均值的差&#xff1a;配对3.1 大样本检验3.1.1 单侧检验3.1.2 双侧检验 3.2 小样本检验3.…

【大模型】(记一面试题)使用Streamlit和Ollama构建PDF文件处理与聊天机器人应用

【大模型】(记一面试题)使用Streamlit和Ollama构建PDF文件处理与聊天机器人应用 我在找工作的过程中&#xff0c;遇到一个面试题&#xff1a;搭建一个简易的利用大型 LLM 和 RAG 来实现用户与PDF文件的自然语言交互。 参考链接&#xff1a;https://medium.com/the-ai-forum/ra…

算法题1:电路开关(HW)

题目描述 实验室对一个设备进行通断测试,实验员可以操控开关进行通断,有两种情况: ps,图没记下来,凭印象画了类似的 初始时,3个开关的状态均为断开;现给定实验员操控记录的数组 records ,records[i] = [time, switchId],表示在时刻 time 更改了开关 switchId 的状态…

【大模型】 基于AI和全球化进程的权衡:开源大模型与闭源大模型

【大模型】 基于AI和全球化进程的权衡&#xff1a;开源大模型与闭源大模型 前言 实际上关于开源or闭源&#xff0c;一直以来都是颇有争议的话题&#xff0c;人们争执于数据的隐私性和共享性&#xff0c;到底哪一方能获得的收益更大。而对于开源与闭源哪个更好实际上也就是说是…

深度学习500问——Chapter09:图像分割(5)

文章目录 9.12 DenseNet 9.13 图像分割的数据集 9.13.1 PASCAL VOC 9.13.2 MS COCO 9.13.3 Cityscapes 9.14 全景分割 9.12 DenseNet 这篇论文是CVPR2017年的最佳论文。 卷积神经网络结构的设计主要朝着两个方向发展&#xff0c;一个是更宽的网络&#xff08;代表&#xff1a…

【算法例题】n元钱买n只鸡

题目描述&#xff1a;公鸡5元1只&#xff0c;母鸡3元1只&#xff0c;小鸡1元3只&#xff0c;问&#xff1a;n元钱买n只鸡&#xff0c;怎么买&#xff1f; 解题思路&#xff1a;这题要用枚举算法&#xff0c;枚举鸡的数量&#xff0c;代码如下&#xff1a; ​#include <bit…

初步学习pygame,使用pygame搭建简单的窗口效果

在VSCode上使用pygame 第一步&#xff1a;创建 Python 虚拟环境 打开 VSCode 中的 Terminal&#xff08;在菜单栏中选择 View > Terminal&#xff09;使用 cd 命令切换到你的项目文件夹输入以下命令来创建一个新的虚拟环境&#xff1a; python3 -m venv env这将在你的项目…

每天五分钟深度学习框架PyTorch:创建具有特殊值的tensor张量

本文重点 tensor张量是一个多维数组,本节课程我们将学习一些pytorch中已经封装好的方法,使用这些方法我们可以快速创建出具有特殊意义的tensor张量。 创建一个值为空的张量 import torch import numpy as np a=torch.empty(1) print(a) print(a.dim()) print(s.shape) 如图…

三菱机械手维修控制器故障

在工业自动化领域&#xff0c;三菱工业机器人凭借其高性能、高可靠性和易用性&#xff0c;受到了广泛应用。然而&#xff0c;随着时间的推移&#xff0c;可能会出现MITSUBISH工业机械臂控制器故障&#xff0c;需要进行三菱机械手维修。 一、MITSUBISH机械手控制器故障诊断 在进…

冷干机的日常维护

冷干机的日常维护保养。 观察记录 (一)每班观察记录仪表值4次 1、压缩空气进出口压差不超过0.035Mpa; 2、蒸发压力表0.4Mpa-0.5Mpa; 高压压力表1.2Mpa-1.6Mpa。&#xff08;冷媒R22&#xff09; 3、压缩机的运行电流、电压。 (二)经常观察冷却水系统、压缩空气系统的进口温度…

【Numpy】深入解析numpy.mgrid()函数

numpy.mgrid()&#xff1a;多维网格生成与数值计算的利器 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f393…

Qt下使用QImage和OpenCV实现图像的拼接与融合

文章目录 前言一、使用QImage进行水平拼接二、使用OpenCV进行水平拼接三、使用OpenCV进行图像融合四、示例完整代码总结 前言 本文主要讲述了在Qt下使用QImage和OpenCV实现图像的拼接与融合&#xff0c;并结合相应的示例进行讲解&#xff0c;以便大家学习&#xff0c;如有错误…

首发!飞凌嵌入式FETMX6ULL-S核心板已适配OpenHarmony 4.1

近日&#xff0c;飞凌嵌入式在FETMX6ULL-S核心板上率先适配了OpenHarmony 4.1&#xff0c;这也是业内的首个应用案例&#xff0c;嵌入式核心板与OpenHarmony操作系统的结合与应用&#xff0c;将进一步推动千行百业的数智化进程。 飞凌嵌入式FETMX6ULL-S核心板基于NXP i.MX 6ULL…

码蹄集部分题目(2024OJ赛16期;单调栈集训+差分集训)

&#x1f9c0;&#x1f9c0;&#x1f9c0;单调栈集训 &#x1f96a;单调栈 单调递增栈伪代码&#xff1a; stack<int> st; for(遍历数组) {while(栈不为空&&栈顶元素大于当前元素)//单调递减栈就是把后方判断条件变为小于等于即可{栈顶元素出栈;//同时进行其他…

【Linux】LAMP集群分布式安全方案

LAMP集群分布式安全方案主要涉及确保Linux、Apache、MySQL和PHP&#xff08;LAMP&#xff09;组合构成的集群环境的安全性和稳定性。 本次实验通过网络层安全对防火墙配置&#xff1a;使用防火墙&#xff08;如iptables或firewalld&#xff09;来限制对集群的访问&#xff0c;只…

[集群聊天服务器]----(五)User类、UserModel类

接着上文[集群聊天服务器]----(四)MySQL数据库模块&#xff0c;接下来我们对User类、UserModel类进行剖析&#xff0c;User表和UserModel类是项目最基本也是最重要的部分&#xff0c;通过它我们对用户的id&#xff0c;用户名&#xff0c;密码&#xff0c;状态相关信息进行存储&…

uniapp+canvas实现逐字手写效果

在移动端使用 UniApp 进行逐字手写的功能。用户可以在一个 inputCanvas 上书写单个字&#xff0c;然后在特定时间后将这个字添加到 outputCanvas 上&#xff0c;形成一个逐字的手写效果。用户还可以保存整幅图像或者撤销上一个添加的字。 初始化 Canvas&#xff1a; 使用 uni.c…

Top3专业课150满分,怎么考的?

这个系列会邀请上岸学长学姐进行经验分享~ 今天经验分享的同学是小马哥上海交大819的全程班学员&#xff0c;专业课150分满分&#xff0c;这位同学也是819期末考试的第一名&#xff0c;非常厉害&#xff01;大家吸吸欧气&#xff01; 初试成绩单 前言 先介绍下自己&#xff0…

新火种AI|复旦团队在“冷冻人脑”领域获得重大进展!人工智能是否会对此形成助力?

​在低温医学领域&#xff0c;“冷冻人脑”技术的研究和突破既是重点&#xff0c;也是难点。因为这项技术关乎着人类是否可以取得一个令人瞩目的突破——人类的生命是否能够得到延续。 早几年&#xff0c;诸如“利用人体冷冻技术将身患绝症的病人保存十几年&#xff0c;几十年…