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 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
    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)
        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(1.1F)
                else if(currentScale<1){
                    scaleBitmapByTouch(0.9F)
                }
                return true
            }
        }
        detector = ScaleGestureDetector(getContext(),detectorListener)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        viewWidthHalf = measuredWidth/2F
        viewHeihtHalf = measuredHeight/2F
    }

    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()
        matrix.setScale(scale,scale)
        bitmap = Bitmap.createBitmap(bitmap!!,0,0,bitmap!!.width,bitmap!!.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
                if(pointCount==2){
                }
            }
            //单指,多指
            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()
        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效果:
Kotlin 背景圆头像图_第1张图片
Kotlin 背景圆头像图_第2张图片
这里的缩放还没有限制,单指移动也还没限制,懒得弄了,这只是练练手,懂个思路就行,其实大概也能用了,哈哈哈哈哈哈哈哈哈。

代码改了,效果图没改,代码改后截图大小刚好和圆框大小一致。原来的是和原图大小一样,这样就没办法直接用了。

你可能感兴趣的:(canvas,bitmap)