自定义Android滑块拼图验证控件

自定义Android滑块拼图验证控件

      • 拼图认证视图
      • 默认策略
      • 工具类
      • 参考

1、继承自AppCompatImageView,兼容ImageView的scaleType设置,可设置离线/在线图片。
2、通过设置滑块模型(透明背景的图形块)设置滑块(和缺省块)样式,可修改缺省块颜色。
效果图

拼图认证视图

class PictureVerifyView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : AppCompatImageView(context, attrs, defStyleAttr
) {private var mState = STATE_IDEL //当前状态// right bottom 禁用private var piercedPositionInfo: RectF? = null //拼图缺块阴影的位置// right bottom 禁用private var thumbPositionInfo: RectF? = null //拼图缺块的位置private var thumbBlock: Bitmap? = null //拼图缺块Bitmapprivate var piercedBlock: Bitmap? = nullprivate var thumbPaint: Paint? = null//绘制拼图滑块的画笔private var piercedPaint: Paint? = null//绘制拼图缺块的画笔private var startTouchTime: Long = 0 //滑动/触动开始时间private var looseTime: Long = 0 //滑动/触动松开时间private var blockSize = DEF_BLOCK_SIZEprivate var mTouchEnable = true //是否可触动private var callback: Callback? = nullprivate var mStrategy: CaptchaStrategy? = nullprivate var mMode = Captcha.MODE_BAR //Captcha验证模式private val xModeDstIn = PorterDuffXfermode(PorterDuff.Mode.DST_IN)private var isReversal = falseprivate var middlewarePaint: Paint? = Paint()private val srcRect = Rect()private val dstRect = RectF()override fun onDetachedFromWindow() {mStrategy?.onDetachedFromWindow()thumbBlock?.recycle()piercedBlock?.recycle()thumbBlock = nullthumbPaint = nullpiercedPositionInfo = nullthumbPositionInfo = nullcallback = nullpiercedPaint = nullmiddlewarePaint = nullsuper.onDetachedFromWindow()}interface Callback {fun onSuccess(time: Long)fun onFailed()}private var tempX = 0fprivate var tempY = 0fprivate var downX = 0fprivate var downY = 0finit {setCaptchaStrategy(DefaultCaptchaStrategy(context))}private fun initDrawElements() {// 创建缺省镂空位置piercedPositionInfo ?: mStrategy?.getPiercedPosition(width, height, blockSize)?.also {piercedPositionInfo = itthumbPositionInfo =mStrategy?.getThumbStartPosition(width, height, blockSize, mMode, it)}// 创建滑块thumbBlock ?: createBlockBitmap().apply {thumbBlock = this}}private fun getBlockWidth() = if (isReversal) blockSize.height else blockSize.widthprivate fun getBlockHeight() = if (isReversal) blockSize.width else blockSize.heightprivate fun getRealBlockWidth() =getBlockWidth() + (mStrategy?.getThumbShadowInfo()?.size?.toFloat() ?: 0f)private fun getRealBlockHeight() =getBlockHeight() + (mStrategy?.getThumbShadowInfo()?.size?.toFloat() ?: 0f)/*** 生成拼图滑块和阴影图片*/private fun createBlockBitmap(): Bitmap {// 获取背景图val origBitmap = getOrigBitmap()// 获取滑块模板val templateBitmap = getTempBitmap()if (blockSize.width != blockSize.height) {isReversal = templateBitmap.width == blockSize.height.toInt()}val resultBmp = Bitmap.createBitmap(getBlockWidth().toInt(),getBlockHeight().toInt(),Bitmap.Config.ARGB_8888)// 创建滑块画板middlewarePaint?.run {reset()isAntiAlias = trueval canvas = Canvas(resultBmp)// 裁剪镂空位置val cropLeft = ((piercedPositionInfo?.left)?.toInt() ?: 0)val cropTop = ((piercedPositionInfo?.top)?.toInt() ?: 0)srcRect.set(cropLeft,cropTop,cropLeft + getBlockWidth().toInt(),cropTop + getBlockHeight().toInt())dstRect.set(0f, 0f, getBlockWidth(), getBlockHeight())// 从原图上rect区间裁剪与画板上rectR区域重叠canvas.drawBitmap(origBitmap,srcRect,dstRect,this)srcRect.set(0, 0, getBlockWidth().toInt(), getBlockHeight().toInt())// 选择交集取上层图片xfermode = xModeDstIn// 绘制底层模板dstcanvas.drawBitmap(templateBitmap,srcRect,dstRect,this)}return getRealThumbBitmap(resultBmp).apply {createPiercedBitmap(templateBitmap)origBitmap.recycle()}}// 获取缺省模板模型private fun getTempBitmap() = mStrategy?.getThumbBitmap(blockSize)?: Utils.getBitmap(R.drawable.capt_def_puzzle, blockSize)private fun getOrigBitmap() =Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply {val canvasOrig = Canvas(this)// 复原ImageView中显示操作 防止缺省位置错位canvasOrig.concat(imageMatrix)drawable.draw(canvasOrig)}/*** 设置带阴影的滑块*/private fun getRealThumbBitmap(resultBmp: Bitmap) =mStrategy?.getThumbShadowInfo()?.run {Utils.addShadow(resultBmp, this)} ?: resultBmp/*** 获取滑块图片*/private fun createPiercedBitmap(templateBitmap: Bitmap) {piercedBlock = (mStrategy?.piercedColor() ?: Color.TRANSPARENT).let {if (it == Color.TRANSPARENT) {templateBitmap} else {createColorBitmap(templateBitmap, it)}}}/*** 获取滑块模型形状的纯色图片*/private fun createColorBitmap(templateBitmap: Bitmap, color: Int, isRecycle: Boolean = true) =Bitmap.createBitmap(getBlockWidth().toInt(),getBlockHeight().toInt(),Bitmap.Config.ARGB_8888).apply {val c = Canvas(this)c.drawColor(color)middlewarePaint?.run {reset()xfermode = xModeDstIn// 从原图上rect区间裁剪与画板上rectR区域重叠c.drawBitmap(templateBitmap,srcRect,dstRect,this)if (isRecycle) {templateBitmap.recycle()}}}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)initDrawElements()if (mState != STATE_ACCESS) {// 绘制缺块位置piercedPaint?.runWith(piercedPositionInfo, piercedBlock) { p, i, b ->if (mStrategy?.drawPiercedBitmap(canvas, p, i, b) != true) {canvas.drawBitmap(b, i.left, i.top, p)}}}if (mState == STATE_MOVE || mState == STATE_IDEL || mState == STATE_DOWN || mState == STATE_UNACCESS) {// 绘制滑块thumbPaint?.runWith(thumbPositionInfo, thumbBlock) { p, i, b ->if (mStrategy?.drawThumbBitmap(canvas, p, i, b) != true) {val offset = (mStrategy?.getThumbShadowInfo()?.size?.toFloat()) ?: 0fcanvas.drawBitmap(b,(i.left - offset).coerceAtLeast(0f),(i.top - offset).coerceAtLeast(0f),p)}}}}private fun Paint.runWith(t: RectF?,bm: Bitmap?,block: (Paint, RectF, Bitmap) -> Unit): Paint {return this.also { p ->t?.let { rect ->bm?.let { b ->if (!b.isRecycled) {block(p, rect, b)}}}}}/*** 按下滑动条(滑动条模式)*/fun down(progress: Int) {if (isEnabled) {startTouchTime = System.currentTimeMillis()mState = STATE_DOWNthumbPositionInfo?.left = progress / 100f * (width - getRealBlockWidth())invalidate()}}/*** 触动拼图块(触动模式)*/private fun downByTouch(x: Float, y: Float) {if (isEnabled) {mState = STATE_DOWNthumbPositionInfo?.run {left = x - getRealBlockWidth() / 2ftop = y - getRealBlockHeight() / 2f}startTouchTime = System.currentTimeMillis()invalidate()}}/*** 移动拼图缺块(滑动条模式)*/fun move(progress: Int) {if (isEnabled) {mState = STATE_MOVEthumbPositionInfo?.left = progress / 100f * (width - getRealBlockWidth())invalidate()}}/*** 触动拼图缺块(触动模式)*/private fun moveByTouch(offsetX: Float, offsetY: Float) {if (isEnabled) {mState = STATE_MOVEthumbPositionInfo?.run {left = (left + offsetX.toInt()).coerceAtMost(width - getRealBlockWidth())top = (top + offsetY.toInt()).coerceAtMost(height - getRealBlockHeight())}invalidate()}}/*** 松开*/fun loose() {if (isEnabled) {mState = STATE_LOOSENlooseTime = System.currentTimeMillis()checkAccess()invalidate()}}/*** 复位*/fun reset() {mState = STATE_IDELthumbPositionInfo = nullthumbBlock?.recycle()thumbBlock = nullpiercedBlock?.recycle()piercedBlock = nullisReversal = falsepiercedPositionInfo = nullinvalidate()}fun unAccess() {mState = STATE_UNACCESSinvalidate()}fun access() {mState = STATE_ACCESSinvalidate()}fun callback(callback: Callback?) {this.callback = callback}fun setCaptchaStrategy(strategy: CaptchaStrategy) {mStrategy = strategythumbPaint = strategy.thumbPaintpiercedPaint = strategy.piercedPaintsetLayerType(LAYER_TYPE_SOFTWARE, thumbPaint)if (!isInLayout) {invalidate()}}fun setBlockSize(size: SizeF) {blockSize = sizereset()}fun setBitmap(bitmap: Bitmap?) {setImageBitmap(bitmap)}override fun setImageBitmap(bm: Bitmap?) {super.setImageBitmap(bm)reset()}override fun setImageDrawable(drawable: Drawable?) {super.setImageDrawable(drawable)reset()}override fun setImageURI(uri: Uri?) {super.setImageURI(uri)reset()}override fun setImageResource(resId: Int) {super.setImageResource(resId)reset()}fun setMode(@Captcha.Mode mode: Int) {mMode = modeisEnabled = truereset()}fun setTouchEnable(enable: Boolean) {mTouchEnable = enable}private fun getFaultTolerant() = (mStrategy?.getFaultTolerant()) ?: DEF_TOLERANCE/*** 检测是否通过*/private fun checkAccess() {thumbPositionInfo?.let { info ->piercedPositionInfo?.run {val faultTolerant = getFaultTolerant()if (abs(info.left - left) < faultTolerant && abs(info.top - top) < faultTolerant) {access()callback?.onSuccess(looseTime - startTouchTime)} else {unAccess()callback?.onFailed()}}}}override fun dispatchTouchEvent(event: MotionEvent): Boolean {//触动模式下,点击超出拼图缺块的区域不进行处理thumbPositionInfo?.let {if (event.action == MotionEvent.ACTION_DOWN&& mMode == Captcha.MODE_NONBAR&& (event.x < it.left || event.x > it.left + getRealBlockWidth() || event.y < it.top || event.y > it.top + getRealBlockHeight())) {return false}}return super.dispatchTouchEvent(event)}@SuppressLint("ClickableViewAccessibility")override fun onTouchEvent(event: MotionEvent): Boolean {if (mMode == Captcha.MODE_NONBAR && mTouchEnable && isEnabled) {thumbBlock?.run {val x = event.xval y = event.ywhen (event.action) {MotionEvent.ACTION_DOWN -> {downX = xdownY = ydownByTouch(x, y)}MotionEvent.ACTION_UP -> loose()MotionEvent.ACTION_MOVE -> {val offsetX = x - tempXval offsetY = y - tempYmoveByTouch(offsetX, offsetY)}}tempX = x:tempY = y}}return true}companion object {//状态码private const val STATE_DOWN = 1private const val STATE_MOVE = 2private const val STATE_LOOSEN = 3private const val STATE_IDEL = 4private const val STATE_ACCESS = 5private const val STATE_UNACCESS = 6internal const val DEF_TOLERANCE = 10 //验证的最大容差internal val DEF_BLOCK_SIZE = SizeF(50f, 50f) //验证的最大容差}
}

默认策略

class DefaultCaptchaStrategy(ctx: Context) : CaptchaStrategy(ctx) {private val degreesList = arrayListOf(0, 90, 180, 270)private val defBound: ShadowInfo =ShadowInfo(SizeUtils.dp2px(3.0f), Color.BLACK,SizeUtils.dp2px(2.0f).toFloat())// 滑块模型override fun getThumbBitmap(blockSize: SizeF): Bitmap {return Utils.getBitmap(R.drawable.capt_def_puzzle,blockSize,getDegrees())}override fun getThumbShadowInfo() = defBound // 滑块阴影信息// 缺省位置override fun getPiercedPosition(width: Int, height: Int, blockSize: SizeF): RectF {val random = Random()val size =blockSize.width.coerceAtLeast(blockSize.height).toInt() + getThumbShadowInfo().sizeval left = (random.nextInt(width - size).coerceAtLeast(size)).toFloat()val top = (random.nextInt(height - size).coerceAtLeast(getThumbShadowInfo().size)).toFloat()return RectF(left, top, 0f, 0f)}private fun getDegrees(): Int {val random = Random()return degreesList[random.nextInt(degreesList.size)]}// 滑块初始位置override fun getThumbStartPosition(width: Int,height: Int,blockSize: SizeF,mode: Int,thumbPosition: RectF): RectF {var left = 0fval top: Floatval maxSize = blockSize.width.coerceAtLeast(blockSize.height).toInt()if (mode == Captcha.MODE_BAR) {top = thumbPosition.top} else {val random = Random()val size = maxSize + getThumbShadowInfo().sizeleft = (random.nextInt(width - size).coerceAtLeast(getThumbShadowInfo().size)).toFloat()top = (random.nextInt(height - size).coerceAtLeast(getThumbShadowInfo().size)).toFloat()}return RectF(left, top, 0f, 0f)}override val thumbPaint: Paintget() = Paint().apply {isAntiAlias = true}override val piercedPaint: Paintget() = Paint().apply {isAntiAlias = true}override fun drawThumbBitmap(canvas: Canvas, paint: Paint, info: RectF, src: Bitmap): Boolean {return false}override fun drawPiercedBitmap(canvas: Canvas,paint: Paint,info: RectF,src: Bitmap): Boolean {return false}// 缺省块颜色override fun piercedColor(): Int {return ResourcesUtils.getColor(R.color.black_a6)}// 验证可冗余空间override fun getFaultTolerant(): Int {return SizeUtils.dp2px(10.0f)}
}

工具类

object Utils {/*** 获取指定大小、指定旋转角度的图片*/@JvmStaticfun getBitmap(@DrawableRes resId: Int, size: SizeF, degrees: Int = 0): Bitmap {val options = BitmapFactory.Options()options.inMutable = trueval newWidth = size.width.toInt()val newHeight = size.height.toInt()return ImageUtils.scale(BitmapFactory.decodeResource(ResourcesUtils.getResources(),resId,options), newWidth, newHeight, true).let {if (degrees > 0) ImageUtils.rotate(it,degrees, newWidth / 2f, newHeight / 2f, true) else it}}/*** 给图片添加阴影*/@JvmStaticfun addShadow(srcBitmap: Bitmap,info: ShadowInfo): Bitmap? {val w = 2 * info.size + info.dx.toInt()val h = 2 * info.size + info.dy.toInt()val dstWidth = srcBitmap.width + wval dstHeight = srcBitmap.height + hval mask = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ALPHA_8)val scaleToFit = Matrix()val src = RectF(0f, 0f, srcBitmap.width.toFloat(), srcBitmap.height.toFloat())val dst = RectF(info.size.toFloat(),info.size.toFloat(),dstWidth - info.size - info.dx,dstHeight - info.size - info.dy)scaleToFit.setRectToRect(src, dst, Matrix.ScaleToFit.CENTER)val dropShadow = Matrix(scaleToFit)dropShadow.postTranslate(info.dx, info.dy)val maskCanvas = Canvas(mask)val paint = Paint(Paint.ANTI_ALIAS_FLAG)maskCanvas.drawBitmap(srcBitmap, scaleToFit, paint)paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)maskCanvas.drawBitmap(srcBitmap, dropShadow, paint)//设置阴影val filter = BlurMaskFilter(info.size.toFloat(), BlurMaskFilter.Blur.NORMAL)paint.reset()paint.isAntiAlias = truepaint.color = info.colorpaint.maskFilter = filterpaint.isFilterBitmap = trueval ret = Bitmap.createBitmap(dstWidth, dstHeight, Bitmap.Config.ARGB_8888)val retCanvas = Canvas(ret)//绘制阴影retCanvas.drawBitmap(mask, 0f, 0f, paint)retCanvas.drawBitmap(srcBitmap, scaleToFit, null)mask.recycle()return ret}
}

参考

Android拼图滑块验证码控件:http://blog.csdn.net/sdfsdfdfa/article/details/79120665
关于android:绘制图像时绘制外部阴影:https://www.codenong.com/17783467/
Paint API之—— Xfermode与PorterDuff详解:https://www.kancloud.cn/kancloud/android-tutorial/87249

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

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

相关文章

【HarmonyOS北向开发】-01 HarmonyOS概述

飞书原文链接-【HarmonyOS北向开发】-01 HarmonyOS概述https://fvcs2dhq8qs.feishu.cn/docx/TDf2d2KMaoPSUUxnvg2cASDdnCe?fromfrom_copylink

Leetcode-每日一题【剑指 Offer 20. 表示数值的字符串】

题目 请实现一个函数用来判断字符串是否表示数值&#xff08;包括整数和小数&#xff09;。 数值&#xff08;按顺序&#xff09;可以分成以下几个部分&#xff1a; 若干空格一个 小数 或者 整数&#xff08;可选&#xff09;一个 e 或 E &#xff0c;后面跟着一个 整数若干空…

Linux NTP原理及配置使用

一、NTP简介 1.NTP简介 NTP&#xff08;Network Time Protocol&#xff0c;网络时间协议&#xff09;是用来使网络中的各个计算机时间同步的一种协议。它的用途是把计算机的时钟同步到世界协调时UTC&#xff0c;其精度在局域网内可达0.1ms&#xff0c;在互联网上绝大多数的…

CSS自学框架之动画

这一节&#xff0c;自学CSS动画。主要学习了淡入淡出、淡入缩放、缩放、移动、旋转动画效果。先看一下成果。 优雅的过渡动画&#xff0c;为你的页面添加另一份趣味&#xff01; 在你的选择器里插入 animation 属性&#xff0c;并添加框架内置的 keyframes 即可实现&#xff0…

《Kubernetes部署篇:Ubuntu20.04基于外部etcd+部署kubernetes1.24.16集群(多主多从)》

一、架构图 如下图所示: 二、环境信息 1、部署规划 主机名K8S版本系统版本内核版本IP地址备注k8s-master-631.24.16Ubuntu 20.04.5 LTS5.15.0-69-generic192.168.1.63master节点 + etcd节点k8s-master-641.24.16Ubuntu 20.04.5 LTS5.15.0-69-generic192.168.1.64master节点 + …

【抖音直播小玩法】介绍

一、是什么 直播小玩法是基于抖音直播场景的新型实时互动内容。直播小玩法由开发者自主开发&#xff0c;接入平台并开放给抖音主播挂载使用。开发者提供创意&#xff0c;依托平台生态&#xff0c;获取收益。 介入标准&#xff1a; 企业开发者&#xff0c;暂不支持个人开发者…

DAMO-YOLO:实时目标检测设计的报告

ReadPaperhttps://readpaper.com/pdf-annotate/note?pdfId4748421678288076801eId1920373270663763712 Abstract 在本报告中&#xff0c;我们提出了一种快速准确的目标检测方法&#xff0c;称为DAMO-YOLO&#xff0c;它比最先进的YOLO系列实现了更高的性能。DAMO-YOLO 通过…

红帆OA SQL注入漏洞复现

0x01 产品简介 红帆iOffice.net从最早满足医院行政办公需求&#xff08;传统OA&#xff09;&#xff0c;到目前融合了卫生主管部门的管理规范和众多行业特色应用&#xff0c;是目前唯一定位于解决医院综合业务管理的软件&#xff0c;是最符合医院行业特点的医院综合业务管理平…

Lnton羚通关于如何使用nanoPC-T4 安装OpenCV?

nanoPC-T4 安装 OpenCV Note: OpenCV has been pre-installed in FriendlyCore/FriendlyDesktop (Version after 201905) and does not require manual installation. Please download the latest FriendlyCore/FriendlyDesktop Image file from the following URL: http://do…

深度分析纳斯达克上市公司慧择的竞争优势和投资价值

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 一、保险行业的现状、竞争与机遇 在疫情期间&#xff0c;很多行业的经营理念与经营方式&#xff0c;甚至客户行为、客户需求都发生了变化&#xff0c;进而催生出新的机遇。保险行业亦是如此&#xff0c;受疫情影响&#xf…

腾讯开启2024校招,主要招聘5大类岗位

近日&#xff0c;腾讯的大动作一个接一个&#xff0c;前脚刚公布2023上半年财报&#xff0c;后脚就开启了2024校招&#xff0c;不得不让人感叹腾讯真速度&#xff01; 此次招聘对象为毕业时间在2023年9月至2024年8月期间的2024届应届毕业生&#xff0c;覆盖北上广深等多个城市…

环境与能源创新专题:地级市绿色创新、碳排放与环境规制数据

数据简介&#xff1a;推动绿色发展&#xff0c;促进人与自然和谐共生是重大战略举措。绿色发展强调“绿水青山就是金山银山”&#xff0c;人与自然和谐共生重在正确处理生态环境保护与经济发展的关系。在着力于实现绿色发展的过程中&#xff0c;绿色创新是绿色发展的重要驱动因…

WPF的CheckBox中的三个状态

WPF的CheckBox中的三个状态 CheckBox控件和RadioButton控件是继承自ToggleButton类&#xff0c;这意味着用户可切换他们的开关状态&#xff0c;其中IsChecked属性是可空的Boolean类型&#xff0c;这意味着该属性可以设置为true&#xff0c;false或null。 null值表示不确定状态…

GPU Microarch 学习笔记 [1]

WARP GPU的线程从thread grid 到thread block&#xff0c;一个thread block在CUDA Core上执行时&#xff0c;会分成warp执行&#xff0c;warp的颗粒度是32个线程。比如一个thread block可能有1024个线程&#xff0c;分成32个warp执行。 上图的CTA&#xff08;cooperative thre…

Codeforces Round 893 (Div. 2)B题题解

文章目录 [The Walkway](https://codeforces.com/contest/1858/problem/B)问题建模问题分析1.分析所求2.如何快速计算每个商贩被去除后的饼干数量代码 The Walkway 问题建模 给定n个椅子&#xff0c;其中有m个位置存在商贩&#xff0c;在商贩处必须购买饼干吃&#xff0c;每隔…

Python程序设计——字符串处理的特殊方法

学习目标&#xff1a; 学习如何创建字符串使用len、min和max函数获取一个字符串的长度、串中的最大和最小的字符使用下标运算符([])访问字符串中的元素使用截取运算符str[ start:end]从较长的字符串中得到一个子串使用运算符连接两个字符串&#xff0c;通过*运算符复制一个字符…

快速入门vue3新特性和新的状态管理库pinia

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 Vue3.3新特性 defineOptions defineModel pinia 介绍 与 Vuex 3.x/4.x 的比较 安装 核心概念 定义…

【腾讯云Cloud Studio实战训练营】使用Cloud Studio社区版快速构建React完成点餐H5页面还原

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&#x1f4bb;上一篇…

成集云 | 用友U8采购请购单同步钉钉 | 解决方案

源系统成集云目标系统 方案介绍 用友U8是中国用友集团开发和推出的一款企业级管理软件产品。具有丰富的功能模块&#xff0c;包括财务管理、采购管理、销售管理、库存管理、生产管理、人力资源管理、客户关系管理等&#xff0c;可根据企业的需求选择相应的模块进行集…

数据结构之队列详解(包含例题)

一、队列的概念 队列是一种特殊的线性表&#xff0c;特殊之处在于它只允许在表的前端&#xff08;front&#xff09;进行删除操作&#xff0c;而在表的后端&#xff08;rear&#xff09;进行插入操作&#xff0c;和栈一样&#xff0c;队列是一种操作受限制的线性表。进行插入操…