国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android Kotlin仿微信頭像裁剪圖片的方法示例

2019-10-21 21:40:54
字體:
來源:轉載
供稿:網友

0.前言

最近突發了很多事情,又跟康仔跳票了,無可奈何,不好意思了。最近生活上有很多感悟,一個男人的牛逼就在于平衡工作,學習和家庭,這個點很難把握,既要保證家庭和睦,又要保證自己價值的實現從而避免墮入平庸,每個人的狀況都是不一樣的,沒有什么經驗是可以照搬的,怎么說呢,不斷摸索吧。

1.分析

整個效果是仿照微信來做的,效果如圖所示:

Android,Kotlin,微信頭像,裁剪圖片

整個效果就是從圖庫選取一張圖片,并進行裁剪,從圖庫選取沒什么好說的,就說說怎么做的裁剪控件吧,這個裁剪控件就是ClipImageView,可以看到它有一個陰影遮罩,一個透明的框,還有圖片的顯示,以及可以移動圖片。

2.代碼

class ClipImageView(context: Context, attributeSet: AttributeSet?) : ImageView(context, attributeSet){  private val paint: Paint = Paint(Paint.ANTI_ALIAS_FLAG)  var clipWidth = 300    set(value)    {      field = value      if (isAttachedToWindow)      {        postInvalidate()      }    }  var clipHeight = 300    set(value)    {      field = value      if (isAttachedToWindow)      {        postInvalidate()      }    }  var minScale = 1.0f  var maxScale = 1.0f  private var rectColor = Color.BLACK  private var lastTouchX = 0F  private var lastTouchY = 0F  private val transMatrix = Matrix()  private var isTouching = false  private var scale = 1.0f  var onsaveClipImageListener: OnSaveClipImageListsner? = null  private val scaleGestureDetectorListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener()  {    override fun onScale(detector: ScaleGestureDetector?): Boolean    {      val curScaleFactor = detector?.scaleFactor ?: 1.0f      var curScale = scale * curScaleFactor      curScale = if (curScale >= 1.0f) Math.min(maxScale, curScale) else Math.max(minScale, curScale)      val scaleFactor = if (curScale > scale) 1 + (curScale - scale) / scale else 1.0f - (scale - curScale) / scale      transMatrix.postScale(scaleFactor, scaleFactor, detector?.focusX          ?: 0f, detector?.focusY ?: 0f)      postInvalidate()      scale = curScale      return true    }    override fun onScaleEnd(detector: ScaleGestureDetector?)    {      super.onScaleEnd(detector)    }  }  private var scaleGestureDetector: ScaleGestureDetector  constructor(context: Context) : this(context, null)  init  {    paint.strokeJoin = Paint.Join.ROUND    scaleGestureDetector = ScaleGestureDetector(context, scaleGestureDetectorListener)    if (attributeSet != null)    {      pareseAttributeSet(attributeSet)    }    setBackgroundColor(Color.WHITE)  }  private fun pareseAttributeSet(attributeSet: AttributeSet)  {    val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ClipImageView)    clipWidth = typedArray.getDimensionPixelOffset(R.styleable.ClipImageView_clip_width, clipWidth)    clipHeight = typedArray.getDimensionPixelOffset(R.styleable.ClipImageView_clip_width, clipHeight)    rectColor = typedArray.getColor(R.styleable.ClipImageView_rect_color, rectColor)    minScale = typedArray.getFloat(R.styleable.ClipImageView_min_scale, minScale)    maxScale = typedArray.getFloat(R.styleable.ClipImageView_max_scale, maxScale)    typedArray.recycle()  }  override fun layout(l: Int, t: Int, r: Int, b: Int)  {    super.layout(l, t, r, b)    if (clipWidth > measuredWidth)    {      clipWidth = measuredWidth    }    if (clipHeight > measuredHeight)    {      clipHeight = measuredHeight    }  }  override fun onTouchEvent(event: MotionEvent?): Boolean  {    if (event?.pointerCount ?: 1 >= 2)    {      isTouching = false      return scaleGestureDetector.onTouchEvent(event)    }    else    {      when (event?.action)      {        MotionEvent.ACTION_DOWN ->        {          isTouching = true          lastTouchX = event.x          lastTouchY = event.y        }        MotionEvent.ACTION_MOVE ->        {          if (isTouching && event.pointerCount == 1)          {            val offsetX = event.x - lastTouchX            val offsetY = event.y - lastTouchY            transMatrix.postTranslate(offsetX, offsetY)            lastTouchX = event.x            lastTouchY = event.y            postInvalidate()          }        }        MotionEvent.ACTION_UP ->        {          isTouching = false        }      }      return true    }  }  override fun onDraw(canvas: Canvas?)  {    canvas?.let {      val saveState = it.saveCount      it.save()      it.concat(transMatrix)      super.onDraw(canvas)      it.restoreToCount(saveState)      drawMask(it)      drawRect(it)    }  }  private fun drawMask(canvas: Canvas)  {    paint.style = Paint.Style.FILL    paint.color = Color.parseColor("#A0000000")    canvas.drawRect(0.0f, 0.0f, width.toFloat(), (height / 2 - clipHeight / 2).toFloat(), paint)    canvas.drawRect((width / 2 + clipWidth / 2).toFloat(), (height / 2 - clipHeight / 2).toFloat(), width.toFloat(), (height / 2 + clipHeight / 2).toFloat(), paint)    canvas.drawRect(0.0f, (height / 2 + clipHeight / 2).toFloat(), width.toFloat(), height.toFloat(), paint)    canvas.drawRect(0.0f, (height / 2 - clipHeight / 2).toFloat(), (width / 2 - clipWidth / 2).toFloat(), (height / 2 + clipHeight / 2).toFloat(), paint)  }  private fun drawRect(canvas: Canvas)  {    paint.style = Paint.Style.FILL_AND_STROKE    paint.color = rectColor    paint.strokeWidth = 4.0f    val offset = paint.strokeWidth / 2    val left: Float = (width / 2 - clipWidth / 2).toFloat() - offset    val top: Float = (height / 2 - clipHeight / 2).toFloat() - offset    val right: Float = (width / 2 + clipWidth / 2).toFloat() + offset    val bottom: Float = (height / 2 + clipHeight / 2).toFloat() + offset    canvas.drawLine(left, top, right, top, paint)    canvas.drawLine(right, top, right, bottom, paint)    canvas.drawLine(left, bottom, right, bottom, paint)    canvas.drawLine(left, top, left, bottom, paint)  }  interface OnSaveClipImageListsner  {    fun onImageFinishedSav()  }  inner class SaveTask(private val filePath: String) : AsyncTask<Unit, Unit, Unit>()  {    override fun doInBackground(vararg params: Unit?): Unit    {      saveClipImage(filePath)    }    override fun onPostExecute(result: Unit?)    {      super.onPostExecute(result)      onsaveClipImageListener?.onImageFinishedSav()    }  }  fun clipAndSaveImage(filePath: String)  {    SaveTask(filePath).execute()  }  private fun saveClipImage(filePath: String)  {    val clipBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)    val clipCanvas = Canvas(clipBitmap)    draw(clipCanvas)    try    {      val outputStream = FileOutputStream(filePath)      val bitmap = Bitmap.createBitmap(clipBitmap, width / 2 - clipWidth / 2, height / 2 - clipHeight / 2, clipWidth, clipHeight, transMatrix, true)      bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)      outputStream.close()    }    catch (e: IOException)    {      e.printStackTrace()    }  }}

可以發現這段代碼是繼承自ImageView。

先看代碼段

private fun pareseAttributeSet(attributeSet: AttributeSet)  {    val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.ClipImageView)    clipWidth = typedArray.getDimensionPixelOffset(R.styleable.ClipImageView_clip_width, clipWidth)    clipHeight = typedArray.getDimensionPixelOffset(R.styleable.ClipImageView_clip_width, clipHeight)    rectColor = typedArray.getColor(R.styleable.ClipImageView_rect_color, rectColor)    minScale = typedArray.getFloat(R.styleable.ClipImageView_min_scale, minScale)    maxScale = typedArray.getFloat(R.styleable.ClipImageView_max_scale, maxScale)    typedArray.recycle()  }

這里解析布局文件的里的屬性,其中clipwidth和clipheight分別代表裁剪框的寬度和高度,minScale和maxScale是最小和最大的縮放程度。

override fun layout(l: Int, t: Int, r: Int, b: Int)  {    super.layout(l, t, r, b)    if (clipWidth > measuredWidth)    {      clipWidth = measuredWidth    }    if (clipHeight > measuredHeight)    {      clipHeight = measuredHeight    }  }

在layout方法里設置clipWidth和clipHeight,防止設置值大于控件大小。

drawMask方法和drawRect方法是用來繪制遮罩層和裁剪框的,其中遮罩層就是四個方形,而裁剪框就是一個矩形的外框。

private fun drawMask(canvas: Canvas)  {    paint.style = Paint.Style.FILL    paint.color = Color.parseColor("#A0000000")    canvas.drawRect(0.0f, 0.0f, width.toFloat(), (height / 2 - clipHeight / 2).toFloat(), paint)    canvas.drawRect((width / 2 + clipWidth / 2).toFloat(), (height / 2 - clipHeight / 2).toFloat(), width.toFloat(), (height / 2 + clipHeight / 2).toFloat(), paint)    canvas.drawRect(0.0f, (height / 2 + clipHeight / 2).toFloat(), width.toFloat(), height.toFloat(), paint)    canvas.drawRect(0.0f, (height / 2 - clipHeight / 2).toFloat(), (width / 2 - clipWidth / 2).toFloat(), (height / 2 + clipHeight / 2).toFloat(), paint)  }  private fun drawRect(canvas: Canvas)  {    paint.style = Paint.Style.FILL_AND_STROKE    paint.color = rectColor    paint.strokeWidth = 4.0f    val offset = paint.strokeWidth / 2    val left: Float = (width / 2 - clipWidth / 2).toFloat() - offset    val top: Float = (height / 2 - clipHeight / 2).toFloat() - offset    val right: Float = (width / 2 + clipWidth / 2).toFloat() + offset    val bottom: Float = (height / 2 + clipHeight / 2).toFloat() + offset    canvas.drawLine(left, top, right, top, paint)    canvas.drawLine(right, top, right, bottom, paint)    canvas.drawLine(left, bottom, right, bottom, paint)    canvas.drawLine(left, top, left, bottom, paint)  }

接著看如何讓圖片隨手指移動和縮放,這里說一下transMatrix,這個是Matrix類,通過它應用到Canvas來實現縮放和移動。

override fun onTouchEvent(event: MotionEvent?): Boolean  {    if (event?.pointerCount ?: 1 >= 2)    {      isTouching = false      return scaleGestureDetector.onTouchEvent(event)    }    else    {      when (event?.action)      {        MotionEvent.ACTION_DOWN ->        {          isTouching = true          lastTouchX = event.x          lastTouchY = event.y        }        MotionEvent.ACTION_MOVE ->        {          if (isTouching && event.pointerCount == 1)          {            val offsetX = event.x - lastTouchX            val offsetY = event.y - lastTouchY            transMatrix.postTranslate(offsetX, offsetY)            lastTouchX = event.x            lastTouchY = event.y            postInvalidate()          }        }        MotionEvent.ACTION_UP ->        {          isTouching = false        }      }      return true    }  }

當兩個手指觸摸時,由移動事件有ScaleGestureDetector處理縮放,否則進行移動。

先看移動:

將移動的距離應用到transMatrix,并調用postInvalidate()重新繪制。

再看縮放處理

private val scaleGestureDetectorListener = object : ScaleGestureDetector.SimpleOnScaleGestureListener()  {    override fun onScale(detector: ScaleGestureDetector?): Boolean    {      val curScaleFactor = detector?.scaleFactor ?: 1.0f      var curScale = scale * curScaleFactor      curScale = if (curScale >= 1.0f) Math.min(maxScale, curScale) else Math.max(minScale, curScale)      val scaleFactor = if (curScale > scale) 1 + (curScale - scale) / scale else 1.0f - (scale - curScale) / scale      transMatrix.postScale(scaleFactor, scaleFactor, detector?.focusX          ?: 0f, detector?.focusY ?: 0f)      postInvalidate()      scale = curScale      return true    }    override fun onScaleEnd(detector: ScaleGestureDetector?)    {      super.onScaleEnd(detector)    }  }

在SimpleOnScaleGestureListener的onScale方法處理縮放,將縮放因子應用到transMatrix,并調用postInvalidate()重新繪制。

接下重點就是onDraw方法:

override fun onDraw(canvas: Canvas?)  {    canvas?.let {      val saveState = it.saveCount      it.save()      it.concat(transMatrix)      super.onDraw(canvas)      it.restoreToCount(saveState)      drawMask(it)      drawRect(it)    }  }

先調用save,保存當前畫布狀態,之后應用transMatrix,縮放和移動畫布,然后調用ImageView的onDraw()方法,也就是父類的方法,用來繪制圖片,因為繪制遮罩層和裁剪框不移動,所以恢復畫布狀態后進行繪制。

最后就是裁剪圖片

inner class SaveTask(private val filePath: String) : AsyncTask<Unit, Unit, Unit>()  {    override fun doInBackground(vararg params: Unit?): Unit    {      saveClipImage(filePath)    }    override fun onPostExecute(result: Unit?)    {      super.onPostExecute(result)      onsaveClipImageListener?.onImageFinishedSav()    }  }  fun clipAndSaveImage(filePath: String)  {    SaveTask(filePath).execute()  }  private fun saveClipImage(filePath: String)  {    val clipBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)    val clipCanvas = Canvas(clipBitmap)    draw(clipCanvas)    try    {      val outputStream = FileOutputStream(filePath)      val bitmap = Bitmap.createBitmap(clipBitmap, width / 2 - clipWidth / 2, height / 2 - clipHeight / 2, clipWidth, clipHeight, transMatrix, true)      bitmap.compress(Bitmap.CompressFormat.JPEG, 80, outputStream)      outputStream.close()    }    catch (e: IOException)    {      e.printStackTrace()    }  }

可以看到啟動了一個AsyncTask用來裁剪和保存Bitmap,其中saveClipImage就是重新構建了一個畫布,并傳入bitmap,重新調用draw方法,將數據信息保存到bitmap,然后裁剪bitmap并存入文件。

3.源碼地址 GitHub

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持VEVB武林網。


注:相關教程知識閱讀請移步到Android開發頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 含山县| 微山县| 马关县| 三原县| 高碑店市| 香河县| 崇礼县| 工布江达县| 济源市| 杭锦后旗| 沁水县| 玉树县| 肥西县| 伊春市| 宁化县| 寿宁县| 苏尼特右旗| 中牟县| 庆元县| 东丽区| 墨江| 丹棱县| 闵行区| 内丘县| 郁南县| 肇庆市| 隆回县| 稻城县| 工布江达县| 平湖市| 文昌市| 米脂县| 湟中县| 兴海县| 开江县| 太保市| 社旗县| 新平| 丰城市| 丰顺县| 柯坪县|