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.mzph.cn/pingmian/42091.shtml

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

相关文章

texStudio使用(小白)

原先使用overleaf在线编译&#xff0c;可能eps格式的图片太大导致需要充钱&#xff0c;所以考虑本地安装 安装教程参考B站视频&#xff1a;B站Latex本地编译器安装&#xff1a;TexLive TextStudio 踩到坑&#xff1a; 1. 编译器位置要选择对 因为BibTex选成了Biber导致出现无…

vb.netcad二开自学笔记1:万里长征第一步Hello CAD!

已入门的朋友请绕行&#xff01; 今天开启自学vb.net 开发autocad&#xff0c;网上相关资料太少了、太老了。花钱买课吧&#xff0c;穷&#xff01;又舍不得&#xff0c;咬牙从小白开始摸索自学吧&#xff0c;虽然注定是踏上了一条艰苦之路&#xff0c;顺便作个自学笔记备忘!积…

海外短剧CPS系统:源码部署与推广分佣全攻略

海外短剧CPS系统&#xff1a;源码部署与推广分佣全攻略 在数字化浪潮下&#xff0c;海外短剧市场日益繁荣&#xff0c;成为众多创业者和投资者的新宠。倾力打造了一款创新的海外短剧CPS分销系统&#xff0c;为广大用户提供了一个全新的赚钱模式。今天&#xff0c;我们就来深入…

Django学习第六天

启动项目命令 python manage.py runserver 取消模态框功能 js实现列表数据删除 第二种实现思路 使用jquery修改模态框标题 编辑页面拿到数据库数据显示默认数据功能实现 想要去数据库中获取数据时&#xff1a;对象/字典 三种不同的数据类型 使用Ajax传入数据实现表单编辑&…

HTML5使用<details>标签:展开/收缩信息

details 标签提供了一种替代 JavaScript 的方法&#xff0c;它主要是提供了一个展开/收缩区域。details 标签中可以使用 summary 标签从属于 details 标签&#xff0c;单击 summary 标签中的内容文字时&#xff0c;details 标签中的其他所有从属元素将会展开或收缩。语法如下&a…

S32DS S32 Design Studio for S32 Platform 3.5 软件安装离线激活

问题描述 重新下载安装 NXP s32系列芯片的集成开发环境&#xff08;IDE&#xff09; S32DS S32 Design Studio&#xff0c;当前版本 S32 Design Studio for S32 Platform 3.5&#xff0c;安装时遇到激活问题 在线激活&#xff0c;激活码哪里来&#xff1f; s32ds 不是免费的&a…

快排的非递归实现

前提 快排的递归实现&#xff0c;在深度过深时会存在栈溢出的风险&#xff0c;所以我们需要掌握快排的非递归写法 快排的实现 单趟实现 上次我们使用了hoare的快排单趟写法&#xff0c;所以这次我们使用前后指针法. 前后指针法 初始状态下&#xff0c;初始化prev为left,cu…

文件上传(本地、OSS)

什么是文件上传&#xff1a;将文件上传到服务器。 文件上传-本地存储 前端 <template> <div><!-- 上传文件需要设置表单的提交方式为post&#xff0c;并设置enctype属性、表单项的type属性设置为file --><form action"http://localhost:8080/wedu/…

Java+ Idea+ Vue产科信息管理系统源码 什么是产科信息管理系统的门诊管理?

Java Idea Vue产科信息管理系统源码 什么是产科信息管理系统的门诊管理&#xff1f; 产科信息管理系统 门诊管理是现代医疗服务的重要组成部分&#xff0c;它借助信息技术手段&#xff0c;对产科门诊的各个环节进行优化和重构&#xff0c;以提高医疗服务效率、提升患者体验、加…

SpringBoot 生产实践:没有父 starter 的打包问题

文章目录 前言一、搜索引擎二、Chat GPT三、官方文档四、小结推荐阅读 前言 今天刚准备写点文章&#xff0c;需要 SpringBoot 项目来演示效果。一时心血来潮&#xff0c;没有采用传统的方式&#xff08;即通过引入 spring-boot-starter-parent 父工程的方式&#xff09;。 &l…

代码随想录-Day48

188. 买卖股票的最佳时机 IV 给你一个整数数组 prices 和一个整数 k &#xff0c;其中 prices[i] 是某支给定的股票在第 i 天的价格。 设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。也就是说&#xff0c;你最多可以买 k 次&#xff0c;卖 k 次。 注意…

DHCP服务器

目录 网络传输原则&#xff1a; DHCP: DHCP作用&#xff1a; 优缺点&#xff1a; DHCP的原理&#xff1a; 用虚拟机模拟DHCP服务器​编辑​编辑 网络传输原则&#xff1a; 网络是双向的&#xff0c;网络是有方向的 解释&#xff1a;网络是双向的&#xff1a; …

大数据中的常见数据问题:独断脏

想象你刚刚入职一家声称正在进行"数字化转型"的大型企业,担任大数据开发工程师。在入职的第一周,你满怀热情,迫不及待地想要大展拳脚,用你的技能来推动公司的数据驱动决策。 目录 大数据中的常见数据问题1. 独 - 数据孤岛2. 断 - 数据价值链断层3. 缺 - 标准、治理…

如何搜索查找ICLR论文

记录有几个查找顶级会议文章的网址&#xff0c;不止ICLR ICLR 2024 还会有visualization模式&#xff1a; ICLR 2024 virtual 这个网站也很棒 Paper Copilot ICLR 2024 当然还有一个用图表示各论文相关关系的网站&#xff1a; connected papers

WACV2023论文速览Attention注意力机制相关

Paper1 ScoreNet: Learning Non-Uniform Attention and Augmentation for Transformer-Based Histopathological Image Classification 摘要原文: Progress in digital pathology is hindered by high-resolution images and the prohibitive cost of exhaustive localized an…

2、图形验证码

1、图形验证码设计 1.1思路 现今&#xff0c;市面上的图形验证码付费的&#xff0c;免费的多种多样&#xff0c;主要形式有滑动拼图、文字点选、语序点选、字体识别、空间推理、智能随机等。 而处理也分为web端和sever端两部分 此处以免费的kaptcha 为例&#xff0c;进行数字图…

电机控制杂谈——增量式的预测电流控制的优势在哪?

1.前言 前几天看到这么个问题。“模型预测控制如何消除静态误差” 评论说用增量式的预测控制。 这个回答让我想起来我大四下看的这篇论文。现在都一百多被引用了。 但是苦于当时能力有限&#xff0c;没办法复现这个文章。 所以现在想重新验证一下。 2.静态误差和电机磁链有…

BulingBuling - 作息安排 [Reset Your Routine] - 1

The Blinkist Team: [ Reset Your Routine ] 如果你发现自己很难按部就班&#xff0c;或者陷入工作效率低的困境&#xff0c;那么你可能需要重新调整一下作息时间&#xff01;从睡眠和营养&#xff0c;到待办事项和井井有条--本指南为你提供了各种技巧和策略&#xff0c;让你的…

自然之美无需雕琢

《自然之美&#xff0c;无需雕琢 ”》在这个颜值至上的时代&#xff0c;但在温馨氛围中&#xff0c;单依纯以一种意想不到的方式&#xff0c;为我们诠释了自然之美的真谛。而医生的回答&#xff0c;如同一股清流耳目一新。“我说医生你看我这张脸&#xff0c;有没有哪里要动的。…

【数据结构(邓俊辉)学习笔记】高级搜索树01——伸展树

文章目录 1. 逐层伸展1. 1 宽松平衡1. 2 局部性1. 3 自适应调整1. 4 逐层伸展1. 5 实例1. 6 一步一步往上爬1. 7 最坏情况 2. 双层伸展2.1 双层伸展2.2 子孙异侧2.3 子孙同侧2.4 点睛之笔2.5 折叠效果2.6 分摊性能2.7 最后一步 3 算法实现3.1 功能接口3.2 伸展算法3.3 四种情况…