RE:从零开始的车载Android HMI(四) - 收音机刻度尺
最近比较忙,研究复杂的东西需要大量集中的时间,但是又抽不出来,就写点简单的东西吧。车载应用开发中有一个几乎避不开的自定义View,就是收音机的刻度条。本篇文章我们来研究如何绘制一个收音机的刻度尺。
本系列文章的目的是在讲解自定义View是如何实现的,阅读时,注意一些通用效果的实现方式,而不要仅仅局限于如何实现本文中提到的刻度尺。
本文涉及的的知识点如下:
- 自定义View时的一些常识,例如:如何处理
layout_height
、layout_width
; - 刻度的绘制;
- OverScroller介绍,以及如何实现惯性滑动;
- 滑动位置修正,实现刻度吸附效果。
实现思路
写一个 Android 收音机 UI 上的刻度尺 View 的基本思路如下:
- 第一步,创建一个自定义 View 类,继承自 View 并根据需要重写构造方法和onMeasure、onLayout、onDraw 方法。
- 第二步,在 onDraw 方法中使用 Canvas 和 Paint 对象来绘制刻度尺的各个部分,包括刻度线,刻度值,指示器等。
- 第三步,使用 scrollBy 或者 scrollTo 方法来实现刻度尺的滑动效果,并使用 Scroller 或者 OverScroller 对象来实现惯性滑动效果。
- 第四步,在自定义 View 类中定义一些接口或者回调方法,用于与外部进行通信和交互。
下面我们来一一实现。
实现过程
定义View的宽、高
定义View的宽、高就是重写onMeasure方法。还记得View测量模式的含义吗?没关系,我们简单回忆一下即可。
- MeasureSpec.EXACTLY(精确模式)
当我们在xml中将为layout_height
或layout_width
设定为match_parent或者具体的值时,在onMeasure
时对应的测量模式就会是EXACTLY
,表示当前View的高度或宽度值是已经确定好的。
所以,在EXACTLY
时我们一般不会修改系统测量出的值,直接将其用作当前View的高度或宽度。
- MeasureSpec.AT_MOST(至多模式)
当我们在xml中将为layout_height
或layout_width
设定为wrap_content时,在onMeasure
时对应的测量模式就会是AT_MOST
,表示系统并不知道当前View的高度和宽度,但是有一个确定范围值,只要不超过系统给出值,都可以。
所以,在EXACTLY
时我们需要计算出当前View需要的高度和宽度(也就是View默认宽高)。大于、等于系统的测量值时,使用系统的测量值;小于系统的测量值时,使用我们自己的。
- MeasureSpec.UNSPECIFIED(不限制模式)
当自定义View的父布局时ScrollView一类,可以跟随子View大小改变自身大小的View时,在onMeasure
时对应的测量模式就会是UNSPECIFIED
。处理方式不定,一般也可以直接采用系统的测量值。
在本例中,我们只计算View的高度,宽度使用系统测量好的即可。
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),measureHeight(heightMeasureSpec))
}private fun measureHeight(heightMeasureSpec: Int): Int {val heightMode = MeasureSpec.getMode(heightMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)var height = 0when (heightMode) {// MeasureSpec.EXACTLY 为match_parent或者具体的值MeasureSpec.EXACTLY -> {height = heightSize}// MeasureSpec.AT_MOST 为wrap_contentMeasureSpec.AT_MOST -> {// 高度 = 刻度尺长刻度的高度 + 上边距 + 下边距height = longScaleHeight.coerceAtLeast(pointHeight) + paddingTop + paddingBottom// 如果高度大于父容器给的高度,则取父容器给的高度height = height.coerceAtMost(heightSize)}// MeasureSpec.UNSPECIFIED 父容器对于子容器没有任何限制,子容器想要多大就多大,多出现于ScrollViewMeasureSpec.UNSPECIFIED -> {height = heightSize}}return height
}
为了支持xml中的padding属性,在计量高度时还需要加上paddingTop、paddingBottom。然后,在onSizeChanged
方法中我们会得到View的最终宽、高,并据此计算出刻度条长指针、短指针的高度。
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)viewWidth = wviewHeight = hinitParams()
}private fun initParams() {// 长刻度的高度 = 控件高度 - 上边距 - 下边距longScaleHeight = height - paddingTop - paddingBottom// 短刻度的高度 = 长刻度的高度 - 15dpshortScaleHeight = longScaleHeight - 15.dp
}
到此,我们定义的View已经可以正确处理xml中的layout_height
和layout_width
属性了。接下来我们开始绘制刻度条。
绘制刻度
绘制刻度原理并不复杂,思路如下:
- 使用刻度尺的最大值 - 最小值,得到总的刻度数
- 循环总刻度数,使用drawLine绘制出线条
- 绘制出收音机刻度尺中间的游标
override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)for (i in 0..scaleCount) {// 点从下往上绘制// 刻度起始的x坐标 = 刻度间隔 * i + 左边距val x1 = scaleSpace * i// 刻度起始的y坐标 = 上边距val y1 = height - paddingBottom// 刻度终点的x坐标 = 刻度起始的x坐标val x2 = x1// 刻度终点的y坐标 = 控件高度 - 下边距 - 刻度高度val y2 = height - paddingBottom - (if (i % 10 == 0) longScaleHeight else shortScaleHeight)// 绘制表尺刻度canvas?.drawLine(x1.toFloat(), y1.toFloat(),x2.toFloat(), y2.toFloat(),linePaint)drawCenterLine(canvas)}
}private fun drawCenterLine(canvas: Canvas?) {// 表尺中心点的x坐标 +滚动的距离是为了让中心点始终在屏幕中间val centerPointX = viewWidth / 2 + scrollX// 中间刻度的起始y坐标 = 上边距 - 5dp(加长5dp)val centerStartPointY = paddingTop - 5.dp// 中间刻度的终点y坐标 = 控件高度 - 下边距 + 5dp(加长5dp)val centerEndPointY = viewHeight - paddingBottom + 5.dpcanvas?.drawLine(centerPointX.toFloat(), centerStartPointY.toFloat(),centerPointX.toFloat(), centerEndPointY.toFloat(),pointPaint)
}
完成上述步骤我们就可以看到下面的效果。
这一步,我们绘制出了刻度尺的刻度和游标,已经可以看到刻度尺基本的雏形了。接下来,我们继续实现刻度尺的滑动。
触摸滑动
常规实现触摸滑动的方式之一是重写OnTouchEvent
方法,根据触摸移动时的坐标判断是否处于滑动状态。本例中,我们使用手势识别类 - GestureDetector
。
GestureDetector
是一个用于检测用户在屏幕上的手势操作的类,它可以识别一些基本的手势,如按下、抬起、滑动、长按、轻击、快速滑动等。
它的使用方法是创建一个GestureDetector
实例,并传入一个GestureDetector.OnGestureListener
接口,该接口定义了一些方法,用于处理不同的手势事件。在需要检测手势的View或Activity中,将触摸事件传递给GestureDetector
的onTouchEvent
方法,从而让GestureDetector
响应触摸事件并调用相应的监听器方法。
private val gestureDetector by lazy { GestureDetector(context, touchGestureListener) }private val touchGestureListener = object : GestureDetector.SimpleOnGestureListener() {override fun onDown(e: MotionEvent): Boolean {return true}override fun onScroll(e1: MotionEvent,e2: MotionEvent,distanceX: Float,distanceY: Float): Boolean {// 当监听到滑动事件时,滚动到指定位置scrollBy(distanceX.toInt(), 0)return true}}override fun onTouchEvent(event: MotionEvent?): Boolean {// return gestureDetector.onTouchEvent(event!!)}
上面的代码中,当GestureDetector
监听到触摸手势为滑动onScroll
时,调用View.scrollBy()方法,将当前View的内容移动对应的滑动距离。onScroll
的回调值 distanceX 就是手指在x轴(水平)上滑动的距离。
这里注意scrollBy
和scrollTo
的区别和特点:
scrollBy
是在当前的位置基础上,相对滑动一定的距离,而scrollTo
是直接滑动到指定的绝对位置。scrollBy
实际上是调用了scrollTo
方法,它的参数是滑动的增量,而scrollTo
的参数是滑动的目标位置。scrollBy
和scrollTo
都是移动View的内容,而不是View本身。对于一个ViewGroup,它的内容是它的所有子View;对于一个TextView,它的内容是它的文本。
这一步中,我们实现了触摸滑动。但是当停止滑动时,刻度尺会立即停下,用户体验比较差,所以还需进一步实现惯性滑动。
惯性滑动
惯性滑动是一种在用户在屏幕上滑动页面后,页面不会马上停下,而是继续保持一定时间的滚动效果的手势操作。它可以提高用户的交互体验,让页面的滚动更加平滑和自然。
Android中的OverScroller
是一个用于实现View平滑滚动的辅助类,它可以根据用户的手势操作或者指定的参数来计算出每一时刻View的位置和速度,并提供了一些方法来控制滚动的开始、结束、中断等状态。
OverScroller.fling()
方法可以实现,从指定位置滑动一段位置然后停下。滑动效果只与离手速度以及滑动边界有关,不能设置惯性滑动距离、时间和插值器。
private val touchGestureListener = object : GestureDetector.SimpleOnGestureListener() {// ...override fun onFling(e1: MotionEvent,e2: MotionEvent,velocityX: Float,velocityY: Float): Boolean {// 启动滚动器,设置滚动的起始位置,速度,范围和回弹距离scroller.fling(scrollX, 0,-velocityX.toInt() / 2, 0,-viewWidth / 2, (scaleCount - 1) * scaleSpace - viewWidth / 2,0, 0,viewWidth / 4, 0)invalidate()return true}
}
fling方法参数的含义如下:
- startX, startY:表示滑动的起始位置的x和y坐标。
- velocityX, velocityY:表示滑动的初始速度的x和y分量,单位是像素/秒。
- minX, maxX, minY, maxY:表示滑动的边界范围,如果滑动超过这个范围,就会触发OverScroll效果。
- overX, overY:表示OverScroll的最大距离,即滑动超过边界后,还能继续滑动的距离
computeScroll()
是一个用于控制View的滑动效果的方法,它会在View的draw()方法中被调用,用于计算View在每一时刻的位置和状态。
override fun computeScroll() {super.computeScroll()// 如果滚动器正在滚动,更新滚动的位置,并根据需要修正位置if (scroller.computeScrollOffset()) {scrollTo(scroller.currX, scroller.currY)invalidate()}}
OverScroller
不仅可以实现惯性滑动,还可以实现以下几种滚动效果:
startScroll
:从指定位置滚动一段指定的距离然后停下,滚动效果与设置的滚动距离、滚动时间、插值器有关,跟离手速度没有关系。一般用于控制View滚动到指定的位置。springBack
:从指定位置回弹到指定位置,一般用于实现拖拽后的回弹效果,不能指定回弹时间和插值器。
好了,惯性滑动实现了,但是当前的刻度尺还有很多瑕疵,例如:滑动时会越界,滑动停止后可能停在两个刻度之间,接下来我们还需要进一步修正这些瑕疵。
限定滑动范围
首先,我们需要限定滑动的范围,保证滑动时不能越界。
重写View的scrollTo
方法,在这里我们可以监听View每次的滑动坐标,当滑动坐标越界时,及时修正滑动坐标,就可以防止越界。
// 滚动方法
override fun scrollTo(x: Int, y: Int) {Log.e("TAG", "scrollTo: ")// 限制滚动的范围,避免越界var x = x// 当x坐标小于可视区域的一半时,设置x坐标为可视区域的一半if (x < -viewWidth / 2) {x = -viewWidth / 2}// 当x坐标大于最大滚动距离时,设置x坐标为最大滚动距离if (x > (scaleCount) * scaleSpace - viewWidth / 2) {x = (scaleCount) * scaleSpace - viewWidth / 2}// 调用父类的滚动方法super.scrollTo(x, y)// 保存当前选中的值 和x坐标currentValue = (x + viewWidth / 2) / scaleSpace + scaleMinValuecurrentX = x// 触发重绘,更新视图invalidate()
}
在scrollTo
中我们计算出了当前的实际刻度值,这里保存好备用。
位置修正
在computeScroll()
中判断滚动事件是否已经结束,如果结束,就开始修正滑动坐标。
override fun computeScroll() {// 如果滚动器正在滚动,更新滚动的位置,并根据需要修正位置if (scroller.computeScrollOffset()) {scrollTo(scroller.currX, scroller.currY)if (scroller.isFinished) {correctPosition()}invalidate()}
}
修正坐标基本思路是,首先计算出当前刻度数值对应的x坐标,因为刻度值在计算时已经取整了,所以一定是不等于当前的实际x坐标,两者的差值就是偏移量。当偏移量大于刻度间距的一半时,向前滑动,否则向后滑动。
// 修正位置,计算当前选中值距离最近的刻度的偏移量,并根据偏移量进行平滑滚动到正确的位置
private fun correctPosition() {// 刻度值对应的x坐标val scaleX: Int = (currentValue - scaleMinValue) * scaleSpace - viewWidth / 2// 偏移值 = 刻度值对应的x坐标-当前x坐标 的绝对值val offset = (scaleX - currentX).absoluteValueif (offset == 0) {return}// 大于间距if (offset > scaleSpace / 2) {smoothScrollBy(scaleSpace - offset)} else {smoothScrollBy(-offset)}
}// 平滑滚动方法
private fun smoothScrollBy(dx: Int) {// 启动滚动器,设置滚动的起始位置,距离,时间和插值器scroller.startScroll(scrollX, 0, dx, 0, 200)invalidate()
}
由于computeScroll()
只有在滑动时才会有回调,所以还需要在手指抬起时修正一次位置,防止遗漏拖动事件。
override fun onTouchEvent(event: MotionEvent?): Boolean {// 当手指抬起时,校准位置if (event?.action == MotionEvent.ACTION_UP || event?.action == MotionEvent.ACTION_CANCEL) {correctPosition()// 将刻度值通过回调传出去.}return gestureDetector.onTouchEvent(event!!)
}
通过,以上这几步我们就完成了一个收音机刻度,最后我们进行收尾。
收尾
刻度尺需要支持外部传入数据,并移动相应的位置,所以暴露一个setCurrentValue
方法,然后重写onLayout方法,在其中计算出刻度值的滑动坐标,使用scrollTo
滑动到对应的x坐标即可。
// 注意,由于会主动调用requestLayout(),所以不能复写kotlin的set方法。
fun setCurrentValue(value: Int) {// 限制值的范围,避免越界var value = valueif (value < scaleMinValue) {value = scaleMinValue}if (value > scaleMaxValue) {value = scaleMaxValue}currentValue = value// 更新当前选中值,并重新布局requestLayout()
}override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)// 根据当前选中值计算滚动的距离 = (当前选中值 - 最小值) * 刻度间隔 - 控件宽度的一半val scrollX: Int = (currentValue - scaleMinValue) * scaleSpace - viewWidth / 2scrollTo(scrollX, 0)
}
使用一段测试代码,就可以实现收音机搜台效果了。
val handler = Handler(Looper.getMainLooper())for (i in 0..155) {handler.postDelayed({findViewById<ScaleView>(R.id.scaleView).setCurrentValue(i)}, 150 * i.toLong())
}
总结
本文介绍了如何编写一个刻度尺View,不过本文的例子不要直接使用在你的项目中,以我个人经验而言,收音机刻度尺变化较多,互联网上很少有View能不做修改直接运用在项目中的,所以更应该关注实现的原理,方便我们在需要时进行修改和定义。
源码地址:https://github.com/linxu-link/FuckView
好,以上就是本文的所有内容,感谢你的阅读,希望对你有所帮助。
相关文章:

RE:从零开始的车载Android HMI(四) - 收音机刻度尺
最近比较忙,研究复杂的东西需要大量集中的时间,但是又抽不出来,就写点简单的东西吧。车载应用开发中有一个几乎避不开的自定义View,就是收音机的刻度条。本篇文章我们来研究如何绘制一个收音机的刻度尺。 本系列文章的目的是在讲…...

评估安全 Wi-Fi 接入:Cisco ISE、Aruba、Portnox 和 Foxpass
在当今不断变化的数字环境中,对 Wi-Fi 网络进行强大访问控制的需求从未像现在这样重要。各组织一直在寻找能够为其用户提供无缝而安全的体验的解决方案。 在本博客中,我们将深入探讨保护 Wi-Fi(和有线)网络的四种领先解决方案——…...
java 泛型作为方法的返回值的封装
问题背景 业务需要,经常需要http方式调用某服务,然后某服务返回特定类型的返回内容。 类似 String resStr xxxHttpClient.post() ,然后它返回一个字符串,你还需要反序列化成某种格式的。 返回值可以反序列化成的形式如下&#…...
ASP.NET Core 中基于 Minimal APIs 的Web API
基于 Minimal APIs 的Web API Minimal APIs 是ASP.NET Core中快速构建 REST API 的方式,可以用最少的代码构建全功能的REST API。比如下面三行代码: var app WebApplication.Create(args); app.MapGet("/", () > "Hello World!&quo…...

Unity ProBuilder SetUVs 不起作用
ProBuilder SetUVs 不起作用 🐟 需要设置face.manulUV true public static void Set01UV(this ProBuilderMesh mesh){foreach (var face in mesh.faces){face.manualUV true;//设置为手动uv}var vertices mesh.GetVertices().Select(v > v.position).ToArray(…...
c#接口(interface)
概述: 在C#中,接口是一种定义了一组相关方法、属性和事件的规范。接口可以被类或结构体实现,以提供一种方式来定义类之间的契约或协议。 接口定义了一组成员,这些成员没有具体的实现。实现接口的类必须提供这些成员的具体实现。…...

SSH远程连接macOS服务器:通过cpolar内网穿透技术实现远程访问的设置方法
文章目录 前言1. macOS打开远程登录2. 局域网内测试ssh远程3. 公网ssh远程连接macOS3.1 macOS安装配置cpolar3.2 获取ssh隧道公网地址3.3 测试公网ssh远程连接macOS 4. 配置公网固定TCP地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址 5. 使用固定TCP端口地址ssh远程 …...

【C++】Visual Studio EditorConfig 格式设置
【C】Visual Studio EditorConfig 格式设置 文章目录 【C】Visual Studio EditorConfig 格式设置I - EditorConfig1.1 - 通用设置indent_styleindent_sizetab_widthend_of_linecharsettrim_trailing_whitespaceinsert_final_newline II - Visual Studio 特定键值缩进设置cpp_in…...
服务器单机大规模数据存储方案
大规模数据存储都需要解决三个核心问题: 1.数据存储容量的问题,既然大数据要解决的是数据 PB 计的数据计算问题,而一般的服务器磁盘容量通常 1~2TB,那么如何存储这么大规模的数据呢? 2.数据读写速度的问题&…...

ElasticSearch-集成ik分词器
本文已收录于专栏 《中间件合集》 目录 背景介绍版本选择优势说明集成过程1.下载安装包2.解压安装包3.重启ElasticSearch服务3.1通过ps -ef | grep elastic查看正在启动的es进程号3.2使用kill -9 xxx 杀死进程3.3使用 ./elasticsearch 启动es服务 分词测试细粒度分词方式分词请…...

c++版opencv求二值图的质心
代码 #include <iostream> #include <opencv2/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/imgproc.hpp>int main(int argc, char* argv[]) {cv::Mat input_image cv::imread("Untitled.png", cv::IMREAD_GRAYSCALE);cv:…...

6、深入解析Kotlin类与对象:构造、伴生、单例全面剖析
前言 本篇文章将带您了解Kotlin编程中的重要概念:类及构造函数、访问修饰符、伴生对象和单例模式。就像搭积木一样,我们会逐步揭开这些概念的面纱,让您轻松理解它们的作用和用法。无论您是编程新手还是有经验的开发者,本文都将为…...
【开源ESP32谷歌恐龙小游戏】【游戏演示和介绍】LVGL ST7789 适用于Arduino
【源码及教程地址-持续更新】 ESP32 C3 LVGL 迷你小电视 Google谷歌恐龙小游戏 1.9寸LCD显示屏开发板 ST7789 适用于Arduino开发板,教程,资料,程序,代码,PDF手册 【开源 & ESP32谷歌恐龙小游戏】【游戏演示和介绍】LVGL ST7789 适用于Arduin...

openCV实战-系列教程7:轮廓检测2与模板匹配(轮廓检测/轮廓特征/轮廓近似/轮廓边界矩阵/轮廓边界圆/模版匹配)、原理解析、源码解读
🧡💛💚💙💜OpenCV实战系列总目录 打印一个图片可以做出一个函数: def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey()cv2.destroyAllWindows() 1、轮廓特征与近似 1.1 轮廓特征 前面我们计算了…...

cs231n_1_IntroToConv
参考的视频来自如下链接https://www.bilibili.com/video/BV1Ed4y1b7bm/ 参考笔记如下https://blog.csdn.net/TeFuirnever/article/details/89059673 x.1 CV历史 生物快速发展于5.4亿年前,那时的化石显示生物进化出了视觉,视觉使得生物多样性大爆炸。 …...
OPENCV实现SURF特征检测
1、SURF优点:SIFT速度慢,一次出现了SURF;2、使用SURF步骤:surf = cv2.xfeatures2d.SURF_create()kp,des = surf.detectAndComputer(img,mask)# -*- coding:utf-8 -*- """ 作者:794919561 日期:2023/8/31 """# -*-...

Android Gradle 同步优化
作者:究极逮虾户 很多人听到方法论三个字,就觉得我要开始pua,说我阿里味,但是我觉得这个查问题的方式可能会对大家有点帮助。 很多人都会有这样的困扰,给你的一个工作内容是一个你完全陌生的东西,第一选择…...
BeautifulSoup:学习使用BeautifulSoup库进行HTML解析和数据提取。
BeautifulSoup是一个用于解析HTML和XML文档的Python库。它可以帮助我们从网页中提取数据,并以易于操作的方式进行分析。 以下是使用BeautifulSoup进行HTML解析和数据提取的基本语法: 安装BeautifulSoup库:首先,你需要在你的Python…...

基于沙猫群算法优化的BP神经网络(预测应用) - 附代码
基于沙猫群算法优化的BP神经网络(预测应用) - 附代码 文章目录 基于沙猫群算法优化的BP神经网络(预测应用) - 附代码1.数据介绍2.沙猫群优化BP神经网络2.1 BP神经网络参数设置2.2 沙猫群算法应用 4.测试结果:5.Matlab代…...
PCL 判断三点共线(三维空间)
文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 继续之前的思路PCL 判断两条线段的平行性(三维空间),我们可以把判断三点共线看做是判断两条线段是否具有平行性,且这两条线段共用其中一个端点,基于此当这两条线段平行时,则证明这三点共线。 二、实现代码 /…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...

Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...