Android打造圆形相机并实现人脸识别(一)

  先来看看效果:

  

    那接下来就会分别分享一下我做这个东东的时候,遇到的坑以及最终实现的方案。

    圆形相机预览View

    做这个View的时候,先是想着自己直接定义一个自定义的TexureView,然后重写onDraw方法,draw一个圆形border就好了。但是发现继承自TexureView以后,却没有了onDraw之类的方法,看来还是得再去研究下这块。在万分着急之时,看到了这篇博客:https://blog.csdn.net/weixin_43901866/article/details/99452491 

    可以发现,其实这里是将TexureView和Border分开了,TextureView主要用View轮廓的裁剪来实现圆形;而Border则通过draw的方法去添加。

    这不是完美解决么,于是仿着这篇博客,写了一个RoundTextureView和一个CircleTexureBorderView。

    先看看我是这么写的:

class RoundTextureView: TextureView {

    private var mRadius = 0F

    constructor(context: Context): this(context, null)

    constructor(context: Context, attrs: AttributeSet?): super(context, attrs) {
        val a = context.obtainStyledAttributes(attrs, R.styleable.RoundTextureView)
        a.apply {
            mRadius = if (hasValue(R.styleable.RoundTextureView_textureRadius)) {
                // 默认为圆形
                a.getFloat(R.styleable.RoundTextureView_textureRadius, measuredWidth.toFloat() / 2)
            } else {
                min(measuredWidth, measuredHeight).toFloat() / 2
            }
        }
        a.recycle()

        outlineProvider = object: ViewOutlineProvider() {
            override fun getOutline(view: View?, outline: Outline?) {
                val rect = if (measuredWidth <= measuredHeight) {
                    Rect(0, (measuredHeight - measuredWidth) / 2, measuredWidth, (measuredHeight + measuredWidth) / 2)
                } else {
                    Rect((measuredWidth - measuredHeight) / 2, 0, measuredHeight, (measuredHeight + measuredWidth) / 2)
                }
                outline?.setOval(rect)

            }
        }
        clipToOutline = true
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mRadius = min(measuredWidth, measuredHeight).toFloat() / 2
    }

    fun turnRound() {
        invalidateOutline()
    }

}

    预览一下:发现 没问题 很ok。

    接下来就到这个Border了 Border的结构也比较简单 主要是拿到TextureView的宽/高 用来定义radius就可以了 而这个值是可以从外部传入的。

class CircleTextureBorderView : View {

    private val mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mAnimatePaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var mTextureViewWidth: Int = measuredWidth

    private var mColor = Color.CYAN

    private var mTipsText: String = "请放入人脸"

    private var mTextHeight = 0F

    constructor(context: Context): this(context, null)

    constructor(context: Context, attributeSet: AttributeSet?): super(context, attributeSet) {
        mPaint.strokeWidth = ScreenUtils.dip2px(context, 3F).toFloat()
        mPaint.style = Paint.Style.STROKE
        mAnimatePaint.style = Paint.Style.FILL
        mAnimatePaint.color = Color.parseColor("#7F000000")
        mTextPaint.color = Color.WHITE
        mTextPaint.style = Paint.Style.FILL
        mTextPaint.textSize = ScreenUtils.dip2px(context, 12F).toFloat()
        mTextPaint.strokeWidth = 1F
        mTextHeight = mTextPaint.fontMetrics.descent - mTextPaint.fontMetrics.ascent
        attributeSet?.apply {
            val a = context.obtainStyledAttributes(this, R.styleable.CircleTextureBorderView)
//            mTextureViewWidth = a.getDimensionPixelSize(R.styleable.CircleTextureBorderView_circleTextureWidth,
//                measuredWidth)
            mColor = a.getColor(R.styleable.CircleTextureBorderView_circleTextureBorderColor,
                Color.CYAN)
            a.recycle()
        }
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        mPaint.color = mColor
        val radius = mTextureViewWidth.toFloat() / 2
        canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredWidth.toFloat() / 2, radius, mPaint)
        // 外边框比内部大10-12dp 边框的厚度为1
        mPaint.strokeWidth = 1F
        canvas?.drawCircle(measuredWidth.toFloat() / 2, measuredWidth.toFloat() / 2,
            radius + ScreenUtils.dip2px_20(context), mPaint)
        mPaint.strokeWidth = ScreenUtils.dip2px(context, 3F).toFloat()

        val left = (measuredWidth - mTextureViewWidth.toFloat()) / 2
        val right = (measuredWidth + mTextureViewWidth.toFloat()) / 2
        val top = (measuredHeight - mTextureViewWidth.toFloat()) / 2
        val bottom = (measuredHeight + mTextureViewWidth.toFloat()) / 2

        // x: (measuredWidth.toFloat() - mTextPaint.measureText(mTipsText)) / 2
        // y: (measuredHeight.toFloat() + mTextureViewWidth / 2) +
        //                    (mTextPaint.fontMetrics.descent - mTextPaint.fontMetrics.ascent) / 2
        canvas?.drawArc(left, top, right, bottom, 150F, -120F, false, mAnimatePaint)
        canvas?.drawText(mTipsText, (measuredWidth.toFloat() - mTextPaint.measureText(mTipsText)) / 2,
            (measuredHeight.toFloat() + mTextureViewWidth / 2) / 2 + mTextHeight * 1.5F, mTextPaint)
    }

    fun setTipsText(str: String) {
        this.mTipsText = str
        postInvalidate()
    }

    fun setCircleTextureWidth(width: Int) {
        this.mTextureViewWidth = width
        postInvalidate()
    }

}

    这里就遇到过一个坑:就是当时想直接在xml中把两个view的宽高写死,但是后来发现效果并不好(太大),于是又对TextureView重写了onGlobalLayoutListener的接口方法,然后将TextureView的实际宽高进行调整,但是却忘了调整BorderView了,虽然错误很低级,但是还是想记录提示自己一下。

    而这里的那个扇形提示,就没有再对外做扩展,就固定了120度,当然如果有其他需求的可以在这里去写一个对外方法定义这个角度。(其实主要还是这个角度比较好算~)

    那么到这里,就完成了第一阶段,对这个预览View的实现。接下来会去讲讲使用Camera2来实现相机预览功能。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值