自定義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.pswp.cn/news/41986.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/41986.shtml
英文地址,請注明出處:http://en.pswp.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;后面跟著一個 整數若干空…

xcode把包打到高版本的iPhone里

打開xcode CTRLb build工程&#xff0c;build成功 把手機連到mac&#xff0c;在xcode選項卡里面的window里面選中device and simulator 打開對應的手機的頁面 然后在工程目錄下build成功過后有一個product的文件夾里面&#xff0c;直接把app拖到對應的手機的窗口就可以不用…

指針和數組簡單填空題合集(純刷題:60道)

前言 本篇文章適合初學指針和數組的朋友&#xff0c;如果您看了前幾組題覺得很簡單&#xff0c;可以看一看我的另一篇文章。 通過本篇文章&#xff0c;你可以清晰的區分出strlen和sizeof的區別&#xff0c;&#xff08;題目類型包括一維數組、二維數組&#xff09;并提高自己…

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 通過…

C++ Primer Plus: 第10章(2)

第10章編程題&#xff1a; &#xff08;1&#xff09; Account.h: #ifndef ACCOUNT_H_ #define ACCOUNT_H_#include <string>class Account { private:std::string name ;std::string code ;double money ; public:Account() ;Account(std::string Name, std::string Co…

Vue history和hash模式

目錄 一、簡介 一、簡介 ~~~~~~~~ 在Vue.js中&#xff0c;路由模式是用來管理應用程序中不同頁面之間的導航的機制。Vue Router支持兩種常見的路由模式&#xff1a;history模式和hash模式。 History 模式&#xff1a; ~~~~~~~~ History模式使用瀏覽器的history.pushState API …

紅帆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…

springboot 自定義注解

1、引入maven依賴&#xff08;版本太低也會導致不生效&#xff09; <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId><version>2.7.10</version></dependency…

深度分析納斯達克上市公司慧擇的競爭優勢和投資價值

來源&#xff1a;猛獸財經 作者&#xff1a;猛獸財經 一、保險行業的現狀、競爭與機遇 在疫情期間&#xff0c;很多行業的經營理念與經營方式&#xff0c;甚至客戶行為、客戶需求都發生了變化&#xff0c;進而催生出新的機遇。保險行業亦是如此&#xff0c;受疫情影響&#xf…

用Python編程實現百度自然語言處理接口的對接,助力你開發智能化處理程序

用Python編程實現百度自然語言處理接口的對接&#xff0c;助力你開發智能化處理程序 隨著人工智能的不斷進步&#xff0c;自然語言處理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;成為了解決文本處理問題的重要工具。百度自然語言處理接口提供了一系…

騰訊開啟2024校招,主要招聘5大類崗位

近日&#xff0c;騰訊的大動作一個接一個&#xff0c;前腳剛公布2023上半年財報&#xff0c;后腳就開啟了2024校招&#xff0c;不得不讓人感嘆騰訊真速度&#xff01; 此次招聘對象為畢業時間在2023年9月至2024年8月期間的2024屆應屆畢業生&#xff0c;覆蓋北上廣深等多個城市…

異步編程框架Seastar介紹

使用Seastar進行異步(Asynchronout)編程 介紹 我們在本文中介紹的Seastar&#xff0c;是一個用于在現代多核機器上&#xff0c;編寫高效復雜的服務器應用程序的C庫。 傳統上&#xff0c;用于編寫服務器應用程序的編程語言庫和框架已經分為兩個不同的陣營&#xff1a;那些注重…

環境與能源創新專題:地級市綠色創新、碳排放與環境規制數據

數據簡介&#xff1a;推動綠色發展&#xff0c;促進人與自然和諧共生是重大戰略舉措。綠色發展強調“綠水青山就是金山銀山”&#xff0c;人與自然和諧共生重在正確處理生態環境保護與經濟發展的關系。在著力于實現綠色發展的過程中&#xff0c;綠色創新是綠色發展的重要驅動因…

關于API數據接口獲取商品的數據的說明

獲取商品數據已經成為許多應用程序的重要組成部分。為了實現這一目標&#xff0c;許多公司和技術開發者使用API數據接口來獲取相關數據。本文將詳細介紹如何使用API數據接口獲取商品數據&#xff0c;并使用Python作為編程語言示例來展示相關代碼。 API數據接口是一種通信協議&…