Kotlin 背景圆头像图

1.需求:大部分的头像选择更换时都采取这样的功能,半透明的蒙版,中间一个正方形的选取框,底图随意移动缩放,确定后截取为背景头像。网易云,微信都是如此
2.实现:
1:代码:

package sss

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.ScaleGestureDetector
import android.view.View
import com.example.sosrcpro.R


class CyclerView:View{
    //图片来源
    private var bitmap:Bitmap? = null
    private var origin:Bitmap? = null    //放大缩小的源位图,缩放后赋予bitmap变量,防止频繁缩放导致失真。
    private var paint:Paint? = null
    private var bitmapShader:BitmapShader? = null
    private var bitmapPaint:Paint? = null
    private var bitmatLeft = 0
    private var bitmapTop = 0
    private  var roundPaint:Paint? =null
    private var viewWidthHalf = 0F
    private var viewHeihtHalf = 0F
    var starX = 0F
    var starY = 0F
    var roundRadiu = 0F
    var detector:ScaleGestureDetector? = null
    var detectorListener:ScaleGestureDetector.OnScaleGestureListener? = null
    var fixedRadiu = 0F
    private var curScale = 1F

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

    }
    constructor(context: Context,attributeSet: AttributeSet?):this(context,attributeSet,0){

    }
    constructor(context: Context,attributeSet: AttributeSet?,style:Int):super(context,attributeSet,style){
        paint = Paint()
        bitmapPaint = Paint()
        roundPaint = Paint()
        paint?.apply {
            setAntiAlias(true)
        }
        bitmap = BitmapFactory.decodeResource(resources, R.mipmap.timg)
        origin = BitmapFactory.decodeResource(resources, R.mipmap.timg)
        detectorListener = object:ScaleGestureDetector.OnScaleGestureListener{

            //返回true才能进入onScale
            override fun onScaleBegin(detector: ScaleGestureDetector?): Boolean {

                return  true
            }

            override fun onScaleEnd(detector: ScaleGestureDetector?) {

            }

            override fun onScale(detector: ScaleGestureDetector): Boolean {
                var currentS = detector.currentSpan
                var currentX = detector.currentSpanX
                var currentY = detector.currentSpanY
                var currentScale = detector.scaleFactor
                Log.d("save","currentS:"+currentS.toString())
                Log.d("save","currentScale:"+currentScale.toString())
                if(currentScale>1)
                scaleBitmapByTouch(0.1F)
                else if(currentScale<1){
                    scaleBitmapByTouch(-0.1F)
                }
                return true
            }
        }
        detector = ScaleGestureDetector(getContext(),detectorListener)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        viewWidthHalf = measuredWidth/2F
        viewHeihtHalf = measuredHeight/2F
        //图片一开始要居中
        bitmatLeft = viewWidthHalf.toInt()-bitmap!!.width/2
        bitmapTop = viewHeihtHalf.toInt()-bitmap!!.height/2
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        var srcrect =  Rect(0, 0, bitmap!!.getWidth(), bitmap!!.getHeight())
        //单指移动图片
        var dscrect =  Rect(bitmatLeft, bitmapTop, bitmap!!.getWidth()+bitmatLeft, bitmap!!.getHeight()+bitmapTop)
        //绘制底图
        canvas?.drawBitmap(bitmap!!, srcrect, dscrect, bitmapPaint)
        paint?.setColor(Color.parseColor("#80666666"))
        var radiu = canvas?.width!! /3F
        fixedRadiu = radiu/2F
       var layerId =  canvas.saveLayer(0F,0F,canvas.width*1F,canvas.height*1F,null,Canvas.ALL_SAVE_FLAG)
        //绘制灰色半透明的蒙版
        canvas?.drawRect(0F,0F,canvas.width*1F,canvas.height*1F,paint!!)
        //DST_OUT:取下层绘制非交集部分。不显示上层(由于圆圈部分存在交集,不显示这下层部分,就镂空了,
        // 进而显示底图,而底图其他部分显示其他非交集下层部分)
        paint?.setXfermode(PorterDuffXfermode(PorterDuff.Mode.DST_OUT))
        paint?.setColor(Color.BLUE)
        //画圆,通过透底得到镂空的底图
        canvas?.drawCircle(canvas.width/2F,canvas.height/2F,fixedRadiu,paint!!)
        paint?.setXfermode(null)
        canvas?.restoreToCount(layerId)
    }
    fun setBitmap(bitmap:Bitmap){
        this.bitmap = bitmap
        invalidate()
    }
    fun getBitmapFormView():Bitmap{

        return bitmap!!
    }
    fun scaleBitmapByTouch(scale:Float){
        var matrix = Matrix()
        curScale = curScale+scale
        if (curScale<=0){
            curScale = 1F
            return
        }
        matrix.postScale(curScale,curScale)
        bitmap = Bitmap.createBitmap(origin!!,0,0,origin!!.width,origin!!.height,matrix,true)

    }

    fun  getCircleBitmap( bitmap:Bitmap) {
        var circleBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        var canvas =  Canvas(circleBitmap)
        var paint =  Paint()
        var rect =  Rect(0, 0, bitmap.getWidth(), bitmap.getHeight())
        var rectF =  RectF(rect);
        var roundRa = 0.0f
        if (bitmap.getWidth() > bitmap.getHeight()) {
            roundRa = bitmap.getHeight() / 2.0f
        } else {
            roundRa = bitmap.getWidth() / 2.0f
        }
    }
   //是频繁调用的,starX,starY的初始化要放在类中,不是在函数中,否则不管是任何触摸类型都设置为0了。
    //加上放大缩小,范围控制,截图,保存。
    override fun onTouchEvent(event: MotionEvent?): Boolean {
       detector!!.onTouchEvent(event)
        var touchType = event!!.actionMasked
        when(touchType){
            //单指,多指
            MotionEvent.ACTION_DOWN->{
                starX =event.x
                starY = event.y
                Log.e("native","ACTION_DOWN")
            }
            //单指,多指
            MotionEvent.ACTION_MOVE->{
                var x =event.x
                var y = event.y
                var xxx = x-starX
                var yyy =y-starY
                starX =x
                starY = y

                setBitmapPosition(bitmatLeft+xxx,bitmapTop+yyy)
                Log.e("native","ACTION_MOVE")
                var pointCount = event.pointerCount

            }
            //单指,多指
            MotionEvent.ACTION_UP->{
                Log.e("native","ACTION_UP")
            }
            //单指多指都是ACTION_MOVE
            //多指
            MotionEvent.ACTION_POINTER_DOWN->{
                Log.e("native","ACTION_POINTER_DOWN")
            }
            //多指
            MotionEvent.ACTION_POINTER_UP->{
                Log.e("native","ACTION_POINTER_UP")
            }
        }
        return true
    }
    fun setBitmapPosition(left:Float,top:Float){
        bitmatLeft = left.toInt()
        bitmapTop = top.toInt()
        //移动控制边界,
        if(bitmatLeft<=(viewWidthHalf.toInt()+fixedRadiu.toInt()-bitmap!!.width)){
            bitmatLeft = viewWidthHalf.toInt()+fixedRadiu.toInt()-bitmap!!.width
        }
        if(bitmatLeft>=(viewWidthHalf-fixedRadiu)){
            bitmatLeft=viewWidthHalf.toInt()-fixedRadiu.toInt()
        }
        if(bitmapTop<=(viewHeihtHalf.toInt()+fixedRadiu.toInt()-bitmap!!.height)){
            bitmapTop = viewHeihtHalf.toInt()+fixedRadiu.toInt()-bitmap!!.height
        }
        if(bitmapTop>=(viewHeihtHalf.toInt()-fixedRadiu.toInt())){
            bitmapTop = viewHeihtHalf.toInt()-fixedRadiu.toInt()
        }
        invalidate()
    }
    fun getRoundBitmap(radiu:Float):Bitmap{
        //画布设置为边长为圆框的半径
        var output = Bitmap.createBitmap(fixedRadiu.toInt()*2,fixedRadiu.toInt()*2,Bitmap.Config.ARGB_8888)
         var canvas = Canvas(output)
        //原图
        var srcRect = Rect(0,0,bitmap!!.width,bitmap!!.height)
        //移动,放置的位置又这个确定,看到的圆框与新的左上角的圆要效果一样。相当于多移动一部分(即两个圆的距离)
        var dscRect = Rect(bitmatLeft-(viewWidthHalf.toInt()-fixedRadiu.toInt()),bitmapTop-(viewHeihtHalf.toInt()-fixedRadiu.toInt()),bitmap!!.width+bitmatLeft-(viewWidthHalf.toInt()-fixedRadiu.toInt()),bitmap!!.height+bitmapTop-(viewHeihtHalf.toInt()-fixedRadiu.toInt()))
        //PorterDuff.Mode.SRC_IN 后不管背景颜色如何,我这都是灰色的。
        canvas.drawARGB(0,255,0,0)
        roundPaint!!.color = Color.parseColor("#ff424242")
        //圆的中心坐标不变,移动图片进行绘制即可。
        canvas.drawCircle(fixedRadiu,fixedRadiu,fixedRadiu,roundPaint!!)
        roundPaint!!.setXfermode(PorterDuffXfermode(PorterDuff.Mode.SRC_IN))
        canvas.drawBitmap(bitmap!!,srcRect,dscRect,roundPaint)
        roundPaint!!.setXfermode(null)     //每次绘制完毕后都应重置为null

       return  output
    }
    fun saveBitmap():Bitmap{
         return getRoundBitmap(fixedRadiu)
    }

}

2效果:
在这里插入图片描述
左上就是得到的圆形位图,每次截取后展示在上面。
中间是要的效果,可移动(有范围控制),可缩放,缩放的范围自己添加吧,这里没有添加。
下面是上一个demo的效果图,无视即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值