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">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <com.yang.app.MyRootView
            android: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.MyCropView
        android: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? = null
    var mRootView: MyRootView? = null
    var 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? MyCropView
        mRootView = 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 = -1
    var imageHeight = -1
    val preOption = BitmapFactory.Options().apply {
        // 只获取图片的宽高
        inJustDecodeBounds = true
        BitmapFactory.decodeResource(resources, imageId, this)
    }
    imageWidth = preOption.outWidth
    imageHeight = preOption.outHeight
    // 计算缩放比例
    val scaleMatrix = Matrix()
    // 确定未缩放Bitmap的RectF
    var srcRect = RectF(0f, 0f, imageWidth.toFloat(), imageHeight.toFloat())
    // 通过目标RectF, 确定缩放数值,存储在scaleMatrix中
    scaleMatrix.setRectToRect(srcRect, destRect, Matrix.ScaleToFit.CENTER)
    // 缩放数值再映射到原始Bitmap上,得到缩放后的RectF
    scaleMatrix.mapRect(srcRect)

    val finalOption = BitmapFactory.Options().apply {
        if (imageHeight > 0 && imageWidth > 0) {
            inPreferredConfig = Bitmap.Config.RGB_565
            inSampleSize = 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 = fromWidth
    var bitmapHeight = fromHeight
    if (fromWidth > toWidth|| fromHeight > toHeight) {
        var inSampleSize = 2
        // 计算最大的inSampleSize值,该值是2的幂,并保持原始宽高大于目标宽高
        while (bitmapWidth >= toWidth && bitmapHeight >= toHeight) {
            bitmapWidth /= 2
            bitmapHeight /= 2
            inSampleSize *= 2
        }
        return inSampleSize
    }
    return 1
}

fun setRectChangeListener(listener: RectChangedListener) {
	mRectChangeListener = listener
}

fun dpToPx(context: Context, dp: Float): Float {
    val metrics = context.resources.displayMetrics
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics)
}

自定义View代码

  • 显示图片的View
class MyRootView constructor(context: Context, attrs: AttributeSet? ) : View(context, attrs) {
    private var lastX = 0f
    private var lastY = 0f
    private val scroller = OverScroller(context)
    private var tracker: VelocityTracker? = null
    private var initialLeft = 0
    private var initialTop = 0
    private var mDestRect: RectF? = null
    private val mScaleMatrix = Matrix()
    private var mRectChangeListener: RectChangedListener? = null
    private var mPaint = Paint().apply {
        isAntiAlias = true
        isFilterBitmap = true
    }

    private var mOriginBitmap: Bitmap? = null

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
        if (initialLeft == 0) initialLeft = left
        if (initialTop == 0) initialTop = top
        mDestRect = 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.rawX
                lastY = event.rawY
            }

            MotionEvent.ACTION_MOVE -> {
                if (tracker == null) {
                    tracker = VelocityTracker.obtain()
                    tracker?.addMovement(event)
                }
                val dx = event.rawX - lastX
                val dy = event.rawY - lastY
                val 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.rawX
                lastY = 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 = null
                invalidate() // 请求重绘View,这会导致computeScroll()被调用
            }
        }
        return true
    }

    override fun computeScroll() {
        if (scroller.computeScrollOffset()) {
            // 更新View的位置
            val left = scroller.currX
            val top = scroller.currY
            val right = left + width
            val bottom = top + height
            layout(left, top, right, bottom)
            if (!scroller.isFinished) {
                invalidate()  // 继续请求重绘View,直到滑动结束
            }
        }
    }

    fun setOriginBitmap(bitmap: Bitmap) {
        mOriginBitmap = bitmap
        invalidate()
    }

    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.width
        val scaleY = mDestRect?.height()!! / bitmap.height
        val scale = Math.min(scaleX, scaleY)
        val dx = (mDestRect?.width()!! - bitmap.width!! * scale) / 2
        val dy = (mDestRect?.height()!! - bitmap.height!! * scale) / 2
        mScaleMatrix.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 = true
        color = Color.WHITE
        strokeWidth = dpToPx(context, 1.5f)
        style = Paint.Style.STROKE
    }
    private val mCornerAndCenterLinePaint = Paint().apply {
        isAntiAlias = true
        color = Color.RED
        strokeWidth = dpToPx(context, 3f)
        style = Paint.Style.STROKE
    }
    private val mDividerLinePaint = Paint().apply {
        isAntiAlias = true
        color = Color.WHITE
        strokeWidth = dpToPx(context, 3f) / 2f
        alpha = (0.5 * 255).toInt()
        style = Paint.Style.STROKE
    }

    private val mLineOffset = dpToPx(context, 3f) / 2f
    private 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 = 0f
    private var downY = 0f

    enum class MoveType {
        LEFT_TOP, RIGHT_TOP, LEFT_BOTTOM, RIGHT_BOTTOM, LEFT, TOP, RIGHT, BOTTOM
    }
    
    private var mMoveType : MoveType?= null
    private 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 = -1
    
    override fun onTouchEvent(event: MotionEvent?): Boolean {
        when (event?.actionMasked) {
            MotionEvent.ACTION_DOWN -> {
                // 记录第一根手指的位置和id
                val pointerIndex = event.actionIndex
                mActivePointerId = 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_TOP
                    return true
                }

                val leftBottomRect = getStartCropCornerRect(cropRect.left, cropRect.bottom)
                if (leftBottomRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.LEFT_BOTTOM
                    return true
                }

                val rightTopRect = getStartCropCornerRect(cropRect.right, cropRect.top)
                if (rightTopRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.RIGHT_TOP
                    return true
                }

                val rightBottomRect = getStartCropCornerRect(cropRect.right, cropRect.bottom)
                if (rightBottomRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.RIGHT_BOTTOM
                    return true
                }

                val leftCenterRect = getStartCropCenterRect(cropRect.left, cropRect.left, cropRect.top, cropRect.bottom)
                if (leftCenterRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.LEFT
                    return true
                }

                val rightCenterRect = getStartCropCenterRect(cropRect.right, cropRect.right, cropRect.top, cropRect.bottom)
                if (rightCenterRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.RIGHT
                    return true
                }

                val topCenterRect = getStartCropCenterRect(cropRect.left, cropRect.right, cropRect.top, cropRect.top)
                if (topCenterRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.TOP
                    return true
                }

                val bottomCenterRect = getStartCropCenterRect(cropRect.left, cropRect.right, cropRect.bottom, cropRect.bottom)
                if (bottomCenterRect.contains(event.x , event.y)) {
                    mMoveType = MoveType.BOTTOM
                    return true
                }
                return true
            }
            
            MotionEvent.ACTION_POINTER_DOWN->{
                // 记录第二根手指的位置和id
                val pointerIndex = event.actionIndex
                mActivePointerId = 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) - downX
                var deltaY = event.getY(pointerIndex) - downY

                downX = event.getX(pointerIndex)
                downY = event.getY(pointerIndex)
                val originalRect = getInitCropRect()
                val startCropRect = getCropRect()
                val endCropRect = RectF(startCropRect)

                when (mMoveType) {
                    MoveType.LEFT_TOP -> {
                        endCropRect.left += deltaX
                        endCropRect.top += deltaY
                    }

                    MoveType.LEFT_BOTTOM -> {
                        endCropRect.left += deltaX
                        endCropRect.bottom += deltaY
                    }

                    MoveType.RIGHT_TOP -> {
                        endCropRect.right += deltaX
                        endCropRect.top += deltaY
                    }

                    MoveType.RIGHT_BOTTOM -> {
                        endCropRect.right += deltaX
                        endCropRect.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 = -1f
                downY = -1f
                mMoveType = null
                mActivePointerId = -1
            }

            MotionEvent.ACTION_POINTER_UP -> {
                // 假如屏幕上有两根手指
                // 按下第二根,抬起第二根,mActivePointerId == pointerId, 活动手指更新为第一根
                // 按下第二根,抬起第一根,mActivePointerId != pointerId, 活动手指为第二根,不变
                val pointerIndex = event.actionIndex
                val pointerId = event.getPointerId(pointerIndex)
                if (mActivePointerId == pointerId) {
                    // 选择一个新的活动手指
                    val newPointerIndex = if (pointerIndex == 0) 1 else 0
                    mActivePointerId = 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()) / 2
            rect.left -= xOffset
            rect.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()) / 2
            rect.top -= yOffset
            rect.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() / 3
            val x2 = rect.left + rect.width() * 2 / 3
            val y1 = rect.top + rect.height() / 3
            val y2 = rect.top + rect.height() * 2 / 3
            canvas.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.mfbz.cn/a/776010.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无人机对地面运动目标定位---获取目标的移动方向和速度

目录 一、引子 我们利用单目无人机通过等时间间隔拍照的形式对地面某移动目标进行定位&#xff0c;当前&#xff0c;我们已经获得了每张相片上该目标的三维坐标&#xff0c;并且知道该无人机在飞行过程中拍照的时间间隔&#xff0c;那么我们就可以通过一定的计算&#xff0c;得…

溶酶体靶向嵌合体制备方法和技术

网盘 https://pan.baidu.com/s/1dhCCryatp71j7yXTDdrrTw?pwdynr4 具有聚集诱导发光性质的比率型溶酶体pH探针及应用.pdf 内体-溶酶体转运靶向嵌合体降解剂及其制备方法与应用.pdf 可降解PDGFR-β的蛋白降解靶向嵌合体及其制备方法和应用.pdf 溶酶体膜包覆纳米颗粒的制备方法.…

华夏女中师生深入同仁堂,感悟中医药文化之精髓

华夏女中师生深入同仁堂&#xff0c;感悟中医药文化之精髓 2024年7月4日下午&#xff0c;北京师范大学实验华夏女子中学15名学生在薛艳老师的带领下来到北京同仁堂中医医院&#xff0c;开展职业影随活动。何泽扬院长对她们的到来表示欢迎。随后&#xff0c;在“冯建春全国名老中…

初识布隆过滤|工作场景

作用 检查一个元素是否在一个集合中 优缺点 优点&#xff1a;空间效率和查询时间比一般算法好&#xff0c;时间复杂度低&#xff0c;O(k) k是函数的个数&#xff0c;节省空间 缺点&#xff1a;有一定的错误几率&#xff0c;没有的也可能判定为存在&#xff0c;删除困难&…

一份适合新手的软件测试练习项目

最近&#xff0c;不少读者托我找一个能实际练手的测试项目。开始&#xff0c;我觉得这是很简单的一件事&#xff0c;但当我付诸行动时&#xff0c;却发现&#xff0c;要找到一个对新手友好的练手项目&#xff0c;着实困难。 我翻了不下一百个web网页&#xff0c;包括之前推荐练…

基于深度学习的图像背景剔除

在过去几年的机器学习领域&#xff0c;我一直想打造真正的机器学习产品。 几个月前&#xff0c;在参加了精彩的 Fast.AI 深度学习课程后&#xff0c;似乎一切皆有可能&#xff0c;我有机会&#xff1a;深度学习技术的进步使许多以前不可能实现的事情成为可能&#xff0c;而且开…

【SpringCloud】Hystrix源码解析

hystrix是一个微服务容错组件&#xff0c;提供了资源隔离、服务降级、服务熔断的功能。这一章重点分析hystrix的实现原理 1、服务降级 当服务实例所在服务器承受的压力过大或者受到网络因素影响没法及时响应请求时&#xff0c;请求会阻塞堆积&#xff0c;情况严重的话整个系统…

【算法笔记自学】入门篇(2)——算法初步

4.1排序 自己写的题解 #include <stdio.h> #include <stdlib.h>void selectSort(int A[], int n) {for(int i 0; i < n - 1; i) { // 修正索引范围int k i;for(int j i 1; j < n; j) { // 修正索引范围if(A[j] < A[k]) {k j;}}if (k ! i) { // 仅在…

取证与数据恢复:冷系统分析,实时系统分析与镜像分析之间的过渡办法

天津鸿萌科贸发展有限公司是 ElcomSoft 系列取证软件的授权代理商。 ElcomSoft 系列取证软件 ElcomSoft 系列取证软件支持从计算机和移动设备进行数据提取、解锁文档、解密压缩文件、破解加密容器、查看和分析证据。 计算机和手机取证的完整集合硬件加速解密最多支持10,000计…

面向对象案例:电影院

TOC 思路 代码 结构 具体代码 Movie.java public class Movie {//一共七个private int id;private String name;private double price;private double score;private String director;private String actors;private String info;//get和setpublic int getId() {return id;…

2024年【湖北省安全员-C证】考试资料及湖北省安全员-C证考试试卷

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 湖北省安全员-C证考试资料是安全生产模拟考试一点通生成的&#xff0c;湖北省安全员-C证证模拟考试题库是根据湖北省安全员-C证最新版教材汇编出湖北省安全员-C证仿真模拟考试。2024年【湖北省安全员-C证】考试资料及…

解决@Autowired 注入service 到 static接口方法的问题

1 对类进行 Component 定义 2 定义service及 static service Component public class OperationalJudgment {private static MemberService memberService;Resourceprivate MemberService service;PostConstructpublic void init() {memberServicethis.service;}3 static方法中…

PTrade常见问题系列3

量化允许同时运行回测和交易的策略个数配置。 量化允许同时运行回测和交易的策略个数在哪里查看&#xff1f; 在量化服务器/home/fly/config/custom_config_conf文件中&#xff0c;其中运行回测的策略个数由backtest_switch&#xff08;是否限制普通回测个数&#xff09;及ba…

AutoCAD 2022 for Mac/Win版 安装包下载

AutoCAD 2022 是由 Autodesk 开发的一款计算机辅助设计&#xff08;CAD&#xff09;软件。它广泛应用于工程、建筑、制造、动画和媒体娱乐等多个领域。 系统要求&#xff1a; 操作系统&#xff1a;Windows 10 或更高版本。 处理器&#xff1a;Intel 或 AMD 处理器&#xff0c…

算法库应用--寻找最长麦穗

学习贺利坚老师算法库 数据结构例程——串的顺序存储应用_使用顺序串存储身份证号-CSDN博客 本人详细解析博客 串的顺序存储的应用实例二_串的顺序存储应用-CSDN博客 版本更新日志 V1.0: 在原有的基础上, 进行优化名字, 并且有了相应的算法库作为支撑, 我使用了for循环来代替老…

第N7周:seq2seq翻译实战-pytorch复现-小白版

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 理论基础 seq2seq&#xff08;Sequence-to-Sequence&#xff09;模型是一种用于机器翻译、文本摘要等序列转换任务的框架。它由两个主要的递归神经网络&#…

HTML【详解】超链接 a 标签的四大功能(页面跳转、页内滚动【锚点】、页面刷新、文件下载)

超链接 a 标签主要有以下功能&#xff1a; 跳转到其他页面 <a href"https://www.baidu.com/" target"_blank" >百度</a>href&#xff1a;目标页面的 url 地址或同网站的其他页面地址&#xff0c;如 detail.htmltarget&#xff1a;打开目标页面…

全面助力巴西slot游戏包推广本土网盟dsp流量广告优势

全面助力巴西slot游戏包推广本土网盟dsp流量广告优势 在巴西这片充满活力的土地上&#xff0c;电子游戏市场蓬勃发展&#xff0c;成为娱乐产业的重要组成部分。随着网络技术的不断进步和移动互联网的普及&#xff0c;巴西玩家对于电子游戏的热情愈发高涨&#xff0c;游戏市场呈…

Streaming local LLM with FastAPI, Llama.cpp and Langchain

题意&#xff1a; 使用FastAPI、Llama.cpp和Langchain流式传输本地大型语言模型 问题背景&#xff1a; I have setup FastAPI with Llama.cpp and Langchain. Now I want to enable streaming in the FastAPI responses. Streaming works with Llama.cpp in my terminal, but…

google 邮件信息收集

主要介绍通过google和fofax对目标进行邮件信息收集 chrome插件 email-whatsapp-extractor link-klipper-extract-all bulk-url-opener-extension email-whatsapp-extractor 使用正则表达式&#xff0c;获取访问页面内所有的email邮箱和whatsapp号码&#xff0c;以表格的形式导…