老熟女激烈的高潮_日韩一级黄色录像_亚洲1区2区3区视频_精品少妇一区二区三区在线播放_国产欧美日产久久_午夜福利精品导航凹凸

重慶分公司,新征程啟航

為企業提供網站建設、域名注冊、服務器等服務

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

0.前言

成都創新互聯公司專注為客戶提供全方位的互聯網綜合服務,包含不限于做網站、成都網站建設、高青網絡推廣、成都微信小程序、高青網絡營銷、高青企業策劃、高青品牌公關、搜索引擎seo、人物專訪、企業宣傳片、企業代運營等,從售前售中售后,我們都將竭誠為您服務,您的肯定,是我們最大的嘉獎;成都創新互聯公司為所有大學生創業者提供高青建站搭建服務,24小時服務熱線:18980820575,官方網址:www.cdcxhl.com

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

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()
  {

    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()
  {

    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

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持創新互聯。


名稱欄目:AndroidKotlin仿微信頭像裁剪圖片的方法示例
文章轉載:http://www.xueling.net.cn/article/igphce.html

其他資訊

在線咨詢
服務熱線
服務熱線:028-86922220
TOP
主站蜘蛛池模板: 在野外被三个男人躁爽白浆视频 | 国产在线播放一区二区 | 久久精品一区中文字幕 | 97超碰国产精品无码分类 | 二级特黄绝大片免费视频大片 | 综合人妻久久一区二区精品 | 亚洲片在线观看 | 久久免费看少妇高潮特黄WWW | 久久中文字幕无码A片不卡男同 | 欧美四虎影院 | 98精品久久久久久久 | 亚洲一区二区中文字幕在线观看 | 精品国产乱码久久久久久丨区2区 | 久久综合99 | 夜夜爽久久精品91 | 91看片在线?看视频 国产日韩欧美色图 | 尤物AV无码国产在线观看 | 亚洲欧洲美洲综合色网 | 国产精品一区二区免费看 | av片一区二区 | 免费看含羞草AV片成人 | 国产高潮白浆喷水 | 日韩欧美永久中文字幕视频 | 国产精品内射久久久久欢欢 | 国产亚洲一区二区三区 | 欧美人牲口杂交在线播放免费 | 欧美第6页 | 亚洲理论在线a中文字幕 | 欧美一级日韩一级 | 国产精品短视频 | 麻豆aⅴ精品无码一区二区 亚洲大尺度专区无码浪潮AV | 成人久久综合 | 69久久夜色精品国产69 | 97精品依人久久久大香线蕉97 | 国产一区二区三区不卡在线 | 久草高清在线 | 国产成社区在线视频观看 | 人与嘼交av免费 | 日本特一级片 | 日本欧美一级 | 人妖天堂狠狠ts人妖天堂狠狠 |