View->裁剪框View的绘制,手势处理

XML文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="vertical"><com.yang.app.MyRootViewandroid:id="@+id/my_root"android:layout_width="match_parent"android:layout_height="0dp"android:layout_weight="1"android:orientation="vertical"android:layout_marginLeft="60dp"android:layout_marginTop="60dp"android:layout_marginRight="60dp"android:layout_marginBottom="60dp"></com.yang.app.MyRootView></LinearLayout><com.yang.app.MyCropViewandroid:id="@+id/my_crop"android:layout_width="match_parent"android:layout_height="match_parent"/>
</RelativeLayout>

Activity代码

const val TAG = "Yang"
class MainActivity : AppCompatActivity() {var tempBitmap: Bitmap? = nullvar mRootView: MyRootView? = nullvar mCropView: MyCropView? = null@SuppressLint("MissingInflatedId")override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)val tempRect = RectF(0f, 0f, resources.displayMetrics.widthPixels.toFloat(), resources.displayMetrics.heightPixels.toFloat())mCropView = findViewById(R.id.my_crop) as? MyCropViewmRootView = findViewById<MyRootView?>(R.id.my_root).apply {mCropView?.let {setRectChangeListener(it)}}CoroutineScope(Dispatchers.IO).launch {tempBitmap = getBitmap(resources, tempRect, R.drawable.real)withContext(Dispatchers.Main) {tempBitmap?.let {// 设置裁剪框的初始位置mCropView?.setOriginBitmapRect(RectF(0f, 0f, it.width.toFloat(), it.height.toFloat()))mRootView?.setOriginBitmap(it)}}}}
}fun getBitmap(resources : Resources, destRect : RectF, imageId: Int): Bitmap? {var imageWidth = -1var imageHeight = -1val preOption = BitmapFactory.Options().apply {// 只获取图片的宽高inJustDecodeBounds = trueBitmapFactory.decodeResource(resources, imageId, this)}imageWidth = preOption.outWidthimageHeight = preOption.outHeight// 计算缩放比例val scaleMatrix = Matrix()// 确定未缩放Bitmap的RectFvar srcRect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())// 通过目标RectF, 确定缩放数值,存储在scaleMatrix中scaleMatrix.setRectToRect(srcRect, destRect, Matrix.ScaleToFit.CENTER)// 缩放数值再映射到原始Bitmap上,得到缩放后的RectFscaleMatrix.mapRect(srcRect)val finalOption = BitmapFactory.Options().apply {if (imageHeight > 0 && imageWidth > 0) {inPreferredConfig = Bitmap.Config.RGB_565inSampleSize = calculateInSampleSize(imageWidth,imageHeight,srcRect.width().toInt(),srcRect.height().toInt())}}return BitmapFactory.decodeResource(resources, imageId, finalOption)
}fun calculateInSampleSize(fromWidth: Int, fromHeight: Int, toWidth: Int, toHeight: Int): Int {var bitmapWidth = fromWidthvar bitmapHeight = fromHeightif (fromWidth > toWidth|| fromHeight > toHeight) {var inSampleSize = 2// 计算最大的inSampleSize值,该值是2的幂,并保持原始宽高大于目标宽高while (bitmapWidth >= toWidth && bitmapHeight >= toHeight) {bitmapWidth /= 2bitmapHeight /= 2inSampleSize *= 2}return inSampleSize}return 1
}fun setRectChangeListener(listener: RectChangedListener) {mRectChangeListener = listener
}fun dpToPx(context: Context, dp: Float): Float {val metrics = context.resources.displayMetricsreturn TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics)
}

自定义View代码

  • 显示图片的View
class MyRootView constructor(context: Context, attrs: AttributeSet? ) : View(context, attrs) {private var lastX = 0fprivate var lastY = 0fprivate val scroller = OverScroller(context)private var tracker: VelocityTracker? = nullprivate var initialLeft = 0private var initialTop = 0private var mDestRect: RectF? = nullprivate val mScaleMatrix = Matrix()private var mRectChangeListener: RectChangedListener? = nullprivate var mPaint = Paint().apply {isAntiAlias = trueisFilterBitmap = true}private var mOriginBitmap: Bitmap? = nulloverride fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {super.onLayout(changed, left, top, right, bottom)if (initialLeft == 0) initialLeft = leftif (initialTop == 0) initialTop = topmDestRect = RectF(0f, 0f, measuredWidth.toFloat(), measuredHeight.toFloat())}override fun onTouchEvent(event: MotionEvent): Boolean {when (event.action) {MotionEvent.ACTION_DOWN -> {tracker = VelocityTracker.obtain().apply {addMovement(event)}lastX = event.rawXlastY = event.rawY}MotionEvent.ACTION_MOVE -> {if (tracker == null) {tracker = VelocityTracker.obtain()tracker?.addMovement(event)}val dx = event.rawX - lastXval dy = event.rawY - lastYval left = left + dx.toInt()val top = top + dy.toInt()val right = right + dx.toInt()val bottom = bottom + dy.toInt()layout(left, top, right, bottom)lastX = event.rawXlastY = event.rawY}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {// 手指抬起时,根据速度进行惯性滑动// (int startX, int startY, int velocityX, int velocityY, int minX, int maxX, int minY, int maxY)// startX, startY:开始滑动的位置// velocityX, velocityY:滑动的速度// minX, maxX, minY, maxY:滑动的范围val parentView = (parent as? View)tracker?.computeCurrentVelocity(1000)scroller.fling(initialLeft, initialTop,-tracker?.xVelocity?.toInt()!!, -tracker?.yVelocity?.toInt()!!,0, parentView?.width!! - width,0, parentView?.height!! - height,width, height)tracker?.recycle()tracker = nullinvalidate() // 请求重绘View,这会导致computeScroll()被调用}}return true}override fun computeScroll() {if (scroller.computeScrollOffset()) {// 更新View的位置val left = scroller.currXval top = scroller.currYval right = left + widthval bottom = top + heightlayout(left, top, right, bottom)if (!scroller.isFinished) {invalidate()  // 继续请求重绘View,直到滑动结束}}}fun setOriginBitmap(bitmap: Bitmap) {mOriginBitmap = bitmapinvalidate()}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)mOriginBitmap?.let {setScaleMatrix(it)canvas?.drawBitmap(it, mScaleMatrix, mPaint)mScaleMatrix.postTranslate(left.toFloat(), top.toFloat())mRectChangeListener?.onRectChanged(mScaleMatrix)}}fun setScaleMatrix(bitmap: Bitmap) {val scaleX = mDestRect?.width()!! / bitmap.widthval scaleY = mDestRect?.height()!! / bitmap.heightval scale = Math.min(scaleX, scaleY)val dx = (mDestRect?.width()!! - bitmap.width!! * scale) / 2val dy = (mDestRect?.height()!! - bitmap.height!! * scale) / 2mScaleMatrix.reset()mScaleMatrix.postScale(scale, scale)mScaleMatrix.postTranslate(dx, dy)}fun setRectChangeListener(listener: RectChangedListener) {mRectChangeListener = listener}
}
  • 裁剪框View
class MyCropView(context: Context, attrs: AttributeSet) : View(context, attrs), RectChangedListener {private val mRectLinePaint = Paint().apply {isAntiAlias = truecolor = Color.WHITEstrokeWidth = dpToPx(context, 1.5f)style = Paint.Style.STROKE}private val mCornerAndCenterLinePaint = Paint().apply {isAntiAlias = truecolor = Color.REDstrokeWidth = dpToPx(context, 3f)style = Paint.Style.STROKE}private val mDividerLinePaint = Paint().apply {isAntiAlias = truecolor = Color.WHITEstrokeWidth = dpToPx(context, 3f) / 2falpha = (0.5 * 255).toInt()style = Paint.Style.STROKE}private val mLineOffset = dpToPx(context, 3f) / 2fprivate val mLineWidth = dpToPx(context, 15f)private val mCenterLineWidth = dpToPx(context, 18f)private val mCoverColor = context.getColor(com.tran.edit.R.color.crop_cover_color)// 处理手势private var downX = 0fprivate var downY = 0fenum class MoveType {LEFT_TOP, RIGHT_TOP, LEFT_BOTTOM, RIGHT_BOTTOM, LEFT, TOP, RIGHT, BOTTOM}private var mMoveType : MoveType?= nullprivate var mOriginBitmapRect = RectF()private var mOriginViewRect = RectF()private var mInitCropMatrix = Matrix()private var mCropMatrix = Matrix()private var mMinCropRect = RectF(0f, 0f, 200f , 200f)private var mActivePointerId = -1override fun onTouchEvent(event: MotionEvent?): Boolean {when (event?.actionMasked) {MotionEvent.ACTION_DOWN -> {// 记录第一根手指的位置和idval pointerIndex = event.actionIndexmActivePointerId = event.getPointerId(pointerIndex)downX = event.getX(pointerIndex)downY = event.getY(pointerIndex)val cropRect = getCropRect()// 计算初始拖动裁剪框的大致方向val leftTopRect = getStartCropCornerRect(cropRect.left, cropRect.top)if (leftTopRect.contains(event.x , event.y)) {mMoveType = MoveType.LEFT_TOPreturn true}val leftBottomRect = getStartCropCornerRect(cropRect.left, cropRect.bottom)if (leftBottomRect.contains(event.x , event.y)) {mMoveType = MoveType.LEFT_BOTTOMreturn true}val rightTopRect = getStartCropCornerRect(cropRect.right, cropRect.top)if (rightTopRect.contains(event.x , event.y)) {mMoveType = MoveType.RIGHT_TOPreturn true}val rightBottomRect = getStartCropCornerRect(cropRect.right, cropRect.bottom)if (rightBottomRect.contains(event.x , event.y)) {mMoveType = MoveType.RIGHT_BOTTOMreturn true}val leftCenterRect = getStartCropCenterRect(cropRect.left, cropRect.left, cropRect.top, cropRect.bottom)if (leftCenterRect.contains(event.x , event.y)) {mMoveType = MoveType.LEFTreturn true}val rightCenterRect = getStartCropCenterRect(cropRect.right, cropRect.right, cropRect.top, cropRect.bottom)if (rightCenterRect.contains(event.x , event.y)) {mMoveType = MoveType.RIGHTreturn true}val topCenterRect = getStartCropCenterRect(cropRect.left, cropRect.right, cropRect.top, cropRect.top)if (topCenterRect.contains(event.x , event.y)) {mMoveType = MoveType.TOPreturn true}val bottomCenterRect = getStartCropCenterRect(cropRect.left, cropRect.right, cropRect.bottom, cropRect.bottom)if (bottomCenterRect.contains(event.x , event.y)) {mMoveType = MoveType.BOTTOMreturn true}return true}MotionEvent.ACTION_POINTER_DOWN->{// 记录第二根手指的位置和idval pointerIndex = event.actionIndexmActivePointerId = event.getPointerId(pointerIndex)downX = event.getX(pointerIndex)downY = event.getY(pointerIndex)}MotionEvent.ACTION_MOVE -> {mMoveType ?: return false// 如果此时屏幕上有两根手指,这个时候mActivePointerId就是第二根手指的id,不支持多指更新位置val pointerIndex = event.findPointerIndex(mActivePointerId)if (pointerIndex < 0 || pointerIndex != 0) {return false}var deltaX = event.getX(pointerIndex) - downXvar deltaY = event.getY(pointerIndex) - downYdownX = event.getX(pointerIndex)downY = event.getY(pointerIndex)val originalRect = getInitCropRect()val startCropRect = getCropRect()val endCropRect = RectF(startCropRect)when (mMoveType) {MoveType.LEFT_TOP -> {endCropRect.left += deltaXendCropRect.top += deltaY}MoveType.LEFT_BOTTOM -> {endCropRect.left += deltaXendCropRect.bottom += deltaY}MoveType.RIGHT_TOP -> {endCropRect.right += deltaXendCropRect.top += deltaY}MoveType.RIGHT_BOTTOM -> {endCropRect.right += deltaXendCropRect.bottom += deltaY}MoveType.LEFT -> {endCropRect.left += deltaX}MoveType.RIGHT -> {endCropRect.right += deltaX}MoveType.TOP -> {endCropRect.top += deltaY}MoveType.BOTTOM -> {endCropRect.bottom += deltaY}else -> {//}}// 限制不超过初始裁剪框的大小endCropRect.left = max(endCropRect.left, originalRect.left)endCropRect.top = max(endCropRect.top, originalRect.top)endCropRect.right = min(endCropRect.right, originalRect.right)endCropRect.bottom = min(endCropRect.bottom, originalRect.bottom)if (endCropRect.width() < mMinCropRect.width() || endCropRect.height() < mMinCropRect.height()) {// 将裁剪框的大小调整到最小范围adjustCropRect(endCropRect, mMinCropRect, originalRect)return true}mCropMatrix.setRectToRect(startCropRect, endCropRect, Matrix.ScaleToFit.FILL)invalidate()mOriginViewRect.set(getCropRect())}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {// 所有手指抬起,重置状态downX = -1fdownY = -1fmMoveType = nullmActivePointerId = -1}MotionEvent.ACTION_POINTER_UP -> {// 假如屏幕上有两根手指// 按下第二根,抬起第二根,mActivePointerId == pointerId, 活动手指更新为第一根// 按下第二根,抬起第一根,mActivePointerId != pointerId, 活动手指为第二根,不变val pointerIndex = event.actionIndexval pointerId = event.getPointerId(pointerIndex)if (mActivePointerId == pointerId) {// 选择一个新的活动手指val newPointerIndex = if (pointerIndex == 0) 1 else 0mActivePointerId = event.getPointerId(newPointerIndex)downX = event.getX(newPointerIndex)downY = event.getY(newPointerIndex)}}}return true}fun adjustCropRect(rect: RectF, minRect: RectF, maxRect: RectF) {if (rect.width() <= minRect.width()) {// 当前裁剪框的左右边界加上距离最小裁剪框的距离val xOffset = (minRect.width() - rect.width()) / 2rect.left -= xOffsetrect.right += xOffset// 如果左边界小于最小裁剪框的左边界,那么左边界就等于最小裁剪框的左边界if (rect.left < maxRect.left) {rect.offset(maxRect.left - rect.left, 0f)}if (rect.right > maxRect.right) {rect.offset(maxRect.right - rect.right, 0f)}}if (rect.height() <= minRect.height()) {// 当前裁剪框的上下边界加上距离最小裁剪框的距离val yOffset = (minRect.height() - rect.height()) / 2rect.top -= yOffsetrect.bottom += yOffset// 如果上边界小于最小裁剪框的上边界,那么上边界就等于最小裁剪框的上边界if (rect.top < maxRect.top) {rect.offset(0f, maxRect.top - rect.top)}if (rect.bottom > maxRect.bottom) {rect.offset(0f, maxRect.bottom - rect.bottom)}}}fun getStartCropCornerRect(startX : Float, startY : Float): RectF {return RectF(startX - mLineWidth, startY - mLineWidth, startX + mLineWidth, startY + mLineWidth)}fun getStartCropCenterRect(startX : Float, endX : Float, startY : Float, endY : Float): RectF {if (startX == endX){return RectF(startX - mLineWidth, startY, startX + mLineWidth, endY)}else{return RectF(startX, startY - mLineWidth, endX, startY + mLineWidth)}}override fun onRectChanged(changedMatrix: Matrix) {mInitCropMatrix.set(changedMatrix)val initCropRect = RectF(mOriginBitmapRect)mInitCropMatrix.mapRect(initCropRect)mOriginViewRect.set(initCropRect)invalidate()}fun getOriginViewRect(): RectF {return RectF(mOriginViewRect)}fun getInitCropRect(): RectF {val initCropRect = RectF(mOriginBitmapRect)mInitCropMatrix.mapRect(initCropRect)return initCropRect}fun getCropRect(): RectF {val cropRect = getOriginViewRect()mCropMatrix.mapRect(cropRect)mCropMatrix.reset()return cropRect}fun setOriginBitmapRect(rectF: RectF){mOriginBitmapRect = rectF}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)val drawRect = getCropRect()drawRect?.let { rect->// 1. 绘制遮罩canvas.save()canvas.clipOutRect(rect)canvas.drawColor(Color.argb(mCoverColor.alpha, mCoverColor.red, mCoverColor.green, mCoverColor.blue))canvas.restore()// 2. 绘制边框canvas?.drawRect(rect, mRectLinePaint)// 3. 绘制分割线val x1 = rect.left + rect.width() / 3val x2 = rect.left + rect.width() * 2 / 3val y1 = rect.top + rect.height() / 3val y2 = rect.top + rect.height() * 2 / 3canvas.drawLine(x1, rect.top, x1, rect.bottom, mDividerLinePaint)canvas.drawLine(x2, rect.top, x2, rect.bottom, mDividerLinePaint)canvas.drawLine(rect.left, y1, rect.right, y1, mDividerLinePaint)canvas.drawLine(rect.left, y2, rect.right, y2, mDividerLinePaint)// 4. 绘制四个角的折线canvas.drawLine(rect.left - mLineOffset, rect.top - mLineOffset * 2, rect.left - mLineOffset, rect.top + mLineWidth, mCornerAndCenterLinePaint)canvas.drawLine(rect.left - mLineOffset * 2, rect.top - mLineOffset, rect.left + mLineWidth, rect.top - mLineOffset, mCornerAndCenterLinePaint)canvas.drawLine(rect.right + mLineOffset, rect.top - mLineOffset * 2, rect.right + mLineOffset, rect.top + mLineWidth, mCornerAndCenterLinePaint)canvas.drawLine(rect.right + mLineOffset * 2, rect.top - mLineOffset, rect.right - mLineWidth, rect.top - mLineOffset, mCornerAndCenterLinePaint)canvas.drawLine(rect.right + mLineOffset, rect.bottom + mLineOffset * 2, rect.right + mLineOffset, rect.bottom - mLineWidth, mCornerAndCenterLinePaint)canvas.drawLine(rect.right + mLineOffset * 2, rect.bottom + mLineOffset, rect.right - mLineWidth, rect.bottom + mLineOffset, mCornerAndCenterLinePaint)canvas.drawLine(rect.left - mLineOffset, rect.bottom + mLineOffset * 2, rect.left - mLineOffset, rect.bottom - mLineWidth, mCornerAndCenterLinePaint)canvas.drawLine(rect.left - mLineOffset * 2, rect.bottom + mLineOffset, rect.left + mLineWidth, rect.bottom + mLineOffset, mCornerAndCenterLinePaint)// 5. 绘制四条边的中间线canvas.drawLine(rect.left - mLineOffset, rect.centerY() - mCenterLineWidth / 2, rect.left - mLineOffset, rect.centerY() + mCenterLineWidth / 2, mCornerAndCenterLinePaint)canvas.drawLine(rect.right + mLineOffset, rect.centerY() - mCenterLineWidth / 2, rect.right + mLineOffset, rect.centerY() + mCenterLineWidth / 2, mCornerAndCenterLinePaint)canvas.drawLine(rect.centerX() - mCenterLineWidth / 2, rect.top - mLineOffset, rect.centerX() + mCenterLineWidth / 2, rect.top - mLineOffset, mCornerAndCenterLinePaint)canvas.drawLine(rect.centerX() - mCenterLineWidth / 2, rect.bottom + mLineOffset, rect.centerX() + mCenterLineWidth / 2, rect.bottom + mLineOffset, mCornerAndCenterLinePaint)}}
}

效果图

在这里插入图片描述

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

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

相关文章

Linux系统的基础知识和常用命令

1、什么是Linux&#xff1f; 是一种免费使用和自由传播的类UNIX操作系统&#xff0c;其内核由林纳斯本纳第克特托瓦兹于1991年10月5日首次发布&#xff0c;它主要受到Minix和Unix思想的启发&#xff0c;是一个基于POSIX的多用户、多任务、支持多线程和多CPU的操作系统。它能运行…

Flink实现准确和高效流处理的关键问题

时间相关: Watermark 水位线 水位线是插入到数据流中的一个标记,可以认为是一个特殊的数据。水位线主要的内容是一个时间戳,用来表示当前事件时间的进展。水位线是基于数据的时间戳生成的。水位线的时间戳必须单调递增,以确保任务的事件时间时钟一直向前推进,进展。水位线…

Git 运用小知识

1.Git添加未完善代码的解决方法 1.1 Git只是提交未推送 把未完善的代码提交到本地仓库 只需点击撤销提交&#xff0c;提交的未完善代码会被撤回 代码显示未提交状态 1.2 Git提交并推送 把未完善的代码提交并推送到远程仓库 点击【未完善提交并推送】的结点选择还原提交&#x…

【后端面试题】【中间件】【NoSQL】MongoDB查询优化2(优化排序、mongos优化)

优化排序 在MongoDB里面&#xff0c;如果能够利用索引来排序的话&#xff0c;直接按照索引顺序加载数据就可以了。如果不能利用索引来排序的话&#xff0c;就必须在加载了数据之后&#xff0c;再次进行排序&#xff0c;也就是进行内存排序。 可想而知&#xff0c;如果内存排序…

【Oracle】Oracle常用函数

目录 聚合函数数字函数1. ABS函数&#xff1a;返回一个数的绝对值。2. CEIL函数&#xff1a;返回大于等于给定数的最小整数。3. FLOOR函数&#xff1a;返回小于等于给定数的最大整数。4. ROUND函数&#xff1a;将一个数四舍五入到指定的小数位。5. MOD函数&#xff1a;返回两个…

Vue 数据大屏适配

1、准备俩个盒子 .dataScreen-content 盒子内容根据设计稿给的px单位进行正常的布局就行 2、盒子的CSS样式 .dataScreen-container {width: 100%;height: 100%;// 有背景图需要的样式background: url("./images/bg.png") no-repeat;background-repeat: no-repeat;b…

推荐算法学习笔记2.1:基于深度学习的推荐算法-基于共线矩阵的深度推荐算法-AutoRec模型

AutoRec模型 前置知识&#xff1a;推荐算法学习笔记1.1:传统推荐算法-协同过滤算法 AutoRec模型通过引入自编码器结构&#xff0c;将共线矩阵中的用户向量&#xff08;基于用户的U-AutoRec&#xff09;或物品向量&#xff08;基于物品的I-AutoRec&#xff09;嵌入到低维空间后还…

在 PostgreSQL 中,如何处理大规模的文本数据以提高查询性能?

文章目录 一、引言二、理解 PostgreSQL 中的文本数据类型三、数据建模策略四、索引选择与优化五、查询优化技巧六、示例场景与性能对比七、分区表八、数据压缩九、定期维护十、总结 在 PostgreSQL 中处理大规模文本数据以提高查询性能 一、引言 在当今的数据驱动的世界中&…

HashMap中的put()方法

一. HashMap底层结构 HashMap底层是由哈希表(数组),链表,红黑树构成,哈希表存储的类型是一个节点类型,哈希表默认长度为16,它不会每个位置都用,当哈希表中的元素个数大于等于负载因子(0.75)*哈希表长度就会扩容到原来的2倍 二. 底层的一些常量 三. HashMap的put()方法 当插入一…

Linux 系统管理4——账号管理

一、用户账号管理 1、用户账号概述 &#xff08;1&#xff09;用户账号的常见分类&#xff1a; 1>超级用户&#xff1a;root uid0 gid0 权限最大。 2>普通用户&#xff1a;uid>500 做一般权限的系统管理&#xff0c;权限有限。 3>程序用户&#xff1a;1<uid&l…

3.python

闯关 3作业 本节关卡&#xff1a; 学习 python 虚拟环境的安装 Python 的基本语法 学会 vscode 远程连接 internstudio 打断点调试 python 程序

生物化学笔记:电阻抗基础+电化学阻抗谱EIS+电化学系统频率响应分析

视频教程地址 引言 方法介绍 稳定&#xff1a;撤去扰动会到原始状态&#xff0c;反之不稳定&#xff0c;还有近似稳定的 阻抗谱图形&#xff08;Nyquist和Bode图&#xff09; 阻抗谱图形是用于分析电化学系统和材料的工具&#xff0c;主要有两种类型&#xff1a;Nyquist图和B…

Drools开源业务规则引擎(三)- 事件模型(Event Model)

文章目录 Drools开源业务规则引擎&#xff08;三&#xff09;- 事件模型&#xff08;Event Model&#xff09;1.org.kie.api.event2.RuleRuntimeEventManager3.RuleRuntimeEventListener接口说明示例规则文件规则执行日志输出 4.AgentaEventListener接口说明示例监听器实现类My…

Java 7新特性深度解析:提升效率与功能

文章目录 Java 7新特性深度解析&#xff1a;提升效率与功能一、Switch中添加对String类型的支持二、数字字面量的改进三、异常处理&#xff08;捕获多个异常&#xff09;四、增强泛型推断五、NIO2.0&#xff08;AIO&#xff09;新IO的支持六、SR292与InvokeDynamic七、Path接口…

WordPress网站添加插件和主题时潜在危险分析

WordPress 最初只是一个简单的博客软件&#xff0c;现在据估计为全球前 1000 万个网站中的 30% 提供支持。WordPress受欢迎的因素之一是可以轻松创建插件和主题来扩展它并提供比默认设置更多的功能。 目前&#xff0c;WordPress 网站列出了 56,000 多个插件以及数千个主题。插件…

DatawhaleAI夏令营2024 Task2

#AI夏令营 #Datawhale #夏令营 赛题解析一、Baseline详解1.1 环境配置1.2 数据处理任务理解2.3 prompt设计2.4 数据抽取 二、完整代码总结 赛题解析 赛事背景 在数字化时代&#xff0c;企业积累了大量对话数据&#xff0c;这些数据不仅是交流记录&#xff0c;还隐藏着宝贵的信…

python读取csv出错怎么解决

Python用pandas的read_csv函数读取csv文件。 首先&#xff0c;导入pandas包后&#xff0c;直接用read_csv函数读取报错OSError&#xff0c;如下&#xff1a; 解决方案是加上参数&#xff1a;enginepython。 运行之后没有报错&#xff0c;正在我欣喜之余&#xff0c;输出一下d…

linux 服务器数据备份 和 mysql 数据迁移

查看域名ip 查看程序所处文件位置 list open files 1、 lsof -i :port 查看端口获取进程 pid 2、lsof -i pid 1、scp 下载服务器文件到本地 security copy protocol 2、导出服务器 mysql 数据库&#xff08;表&#xff09;到本地 mysqldump是MySQL自带的一个实用程序&…

CentOS 7.9 停止维护(2024-6-30)后可用在线yum源 —— 筑梦之路

众所周知&#xff0c;centos 7 在2024年6月30日&#xff0c;生命周期结束&#xff0c;官方不再进行支持维护&#xff0c;而很多环境一时之间无法完全更新替换操作系统&#xff0c;因此对于yum源还是需要的&#xff0c;特别是对于互联网环境来说&#xff0c;在线yum源使用方便很…

207 课程表

题目 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出&#xff0c;其中 prerequisites[i] [ai, bi] &#xff0c;表示如果要学习课程 ai 则 必须 先学习课程 bi 。 …