中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android怎么實現單指滑動雙指縮放照片

發布時間:2023-04-26 17:04:05 來源:億速云 閱讀:112 作者:iii 欄目:開發技術

今天小編給大家分享一下Android怎么實現單指滑動雙指縮放照片的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

一、前景提示

最近接到一個查看大圖的需求,現在圖片展示還不夠大,要求還要能縮小能放大還能保存照片。直接開始Google實現方式。

二、實現功能

根據查詢到的結果分為兩種,一個是使用手勢監聽來實現,第二種監聽觸摸事件來實現

  • 手勢監聽-- ScaleGestureDetector Google提供的手勢監聽類

  • 觸摸事件--OnTouchListener 自己監聽觸摸事件自己實現放大縮小的邏輯

2.1 手勢監聽

先寫布局文件

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">
  <androidx.appcompat.widget.AppCompatImageView
    android:id="@+id/iv_example"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:text="Hello World!"
    android:scaleType="fitCenter"
    android:src="@drawable/muffin_7870491_1920"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

再去實現手勢監聽方法

class MainActivity : AppCompatActivity() {
    private lateinit var mScaleGestureDetector: ScaleGestureDetector
    private var mScaleFactor: Float = 1.0f
    private lateinit var mImageView: AppCompatImageView
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mImageView = findViewById(R.id.iv_example)
        mScaleGestureDetector = ScaleGestureDetector(this, ScaleGestureListener())
        mImageView.setOnTouchListener { _, event ->
            mScaleGestureDetector.onTouchEvent(event)
            true
        }
    }
    private inner class ScaleGestureListener : ScaleGestureDetector.SimpleOnScaleGestureListener() {
        override fun onScale(detector: ScaleGestureDetector): Boolean {
            mScaleFactor *= detector.scaleFactor
            // 限制縮放因子在0.1到10.0
            mScaleFactor = mScaleFactor.coerceIn(0.1f, 10.0f)
            mImageView.scaleX = mScaleFactor
            mImageView.scaleY = mScaleFactor
            return true
        }
    }
}

代碼很簡單直接使用ScaleGestureDetector去監聽觸摸事件,手勢本質也是Google內部監聽事件判斷再回調給我們使用。當然我們這里不去查看源碼,只看實現過程。 在使用過程中發現這種縮放并不平滑,而且響應有點慢,有延遲。猜想內部是由很多其他的判斷吧。那我們只想簡單一點怎么搞呢,那就是自己去判斷縮放,還有實現單指滑動用手勢也不太好實現的樣子。所以我們試試第二種方式實現也就是觸摸事件。

2.2 觸摸事件

首先我們實現一下縮放,我們還是沿用上次使用onTouchListener來處理我們的觸摸事件,布局文件中需要把imageView的縮放屬性改為矩陣 android:scaleType="matrix"

private var startMatrix = Matrix()
mImageView.setOnTouchListener { _, event ->
    when(event.action and MotionEvent.ACTION_MASK) {
        MotionEvent.ACTION_POINTER_DOWN -> {
            // 記錄雙指按下的位置和距離
            startDistance = getDistance(event)
            if (startDistance > 10f) {
                startMatrix.set(mImageView.imageMatrix)
                mode = 2
            }
            return@setOnTouchListener true
        }
    }
    true
}

沒有自己處理過觸摸事件的小伙伴可能會好奇MotionEvent.ACTION_MASK是什么,其實這個是為了處理多點觸摸事件加的一個flag和action做and操作,我們就能處理ACTION_POINTER_DOWN和ACTION_POINTER_UP這兩個多點觸摸事件。 看下代碼邏輯,我們先計算兩個手指的距離,如果距離大于10就證明是縮放操作,設置成我們自己定義的模式,再把imageView的矩陣保存,后續對照片移動,縮放都是通過變換矩陣來實現的。 至于計算兩個手指之間的距離用的勾股定理,來個示意圖,大家就明白了。

Android怎么實現單指滑動雙指縮放照片

計算如下。

 private fun getDistance(event: MotionEvent): Float {
        val dx = event.getX(0) - event.getX(1)
        val dy = event.getY(0) - event.getY(1)
        return sqrt(dx * dx + dy * dy)
    }

通過計算能得到直角邊和鄰邊,對他們使用勾股定理就能得到斜邊的值,也就是兩個手指之間的距離。 有做過觸摸事件監聽的同學就應該知道,我們下一步要監聽移動事件了也就是MotionEvent.ACTION_MOVE。

mImageView.setOnTouchListener { _, event ->
    when (event.action and MotionEvent.ACTION_MASK) {
        MotionEvent.ACTION_POINTER_DOWN -> {
            // 記錄雙指按下的位置和距離
            startDistance = getDistance(event)
            if (startDistance > 10f) {
                startMatrix.set(mImageView.imageMatrix)
                mode = 2
            }
            return@setOnTouchListener true
        }
        MotionEvent.ACTION_MOVE -> {
            if (mode == 2) {
                // 雙指縮放
                val currentDistance = getDistance(event)
                if (currentDistance > 10f) {
                    val scale = currentDistance / startDistance
                    mImageView.imageMatrix = startMatrix.apply {
                        postScale(scale, scale, getMidX(event), getMidY(event))
                    }
                }
            }
            return@setOnTouchListener true
        }
        MotionEvent.ACTION_POINTER_UP -> {
            mode = 0
            return@setOnTouchListener true
        }
        else -> return@setOnTouchListener true
    }
}

這里在move事件中我們也需要對手指之間的距離進行計算,如果距離超過10,就開始計算縮放倍數,通過postScale進行矩陣變換。 在MotionEvent.ACTION_POINTER_UP事件中對mode值進行復位操作,畢竟還有個單指拖動操作。 如果大家把上面的代碼運行過就會發現怎么圖片沒有居中顯示,這是因為我們的縮放屬性被改為矩陣也就是android:scaleType="matrix",那么想要圖片居中顯示怎么操作呢,只需要在觸摸時去改變縮放屬性,其他的時候不變即可。 我們把imageView恢復成android:scaleType="fitCenter",在onTouchListener中加入(放在when前即可)

mImageView.scaleType = ImageView.ScaleType.MATRIX

這樣一開始就可以保持圖片在中央了。 這樣縮放功能實現了,下面實現單指拖動功能,思路很簡單記錄第一次按下的位置,在移動過程中計算應該需要偏移的距離,再記錄下當前的位置,以便于下次計算。

private var lastX = 0f
private var lastY = 0f
mImageView.setOnTouchListener { _, event ->
    mImageView.scaleType = ImageView.ScaleType.MATRIX
    when (event.action and MotionEvent.ACTION_MASK) {
        MotionEvent.ACTION_DOWN -> {
            // 記錄單指按下的位置
            lastX = event.x
            lastY = event.y
            mode = 1
            startMatrix.set(mImageView.imageMatrix)
            return@setOnTouchListener true
        }
        MotionEvent.ACTION_POINTER_DOWN -> {
            // 記錄雙指按下的位置和距離
            startDistance = getDistance(event)
            if (startDistance > 10f) {
                startMatrix.set(mImageView.imageMatrix)
                mode = 2
            }
            return@setOnTouchListener true
        }
        MotionEvent.ACTION_MOVE -> {
            if (mode == 1) {
                // 單指拖動
                val dx = event.x - lastX
                val dy = event.y - lastY
                mImageView.imageMatrix = startMatrix.apply {
                    postTranslate(dx, dy)
                }
                lastX = event.x
                lastY = event.y
            } else if (mode == 2) {
                // 雙指縮放
                val currentDistance = getDistance(event)
                if (currentDistance > 10f) {
                    val scale = currentDistance / startDistance
                    mImageView.imageMatrix = startMatrix.apply {
                        postScale(scale, scale, getMidX(event), getMidY(event))
                    }
                }
            }
            return@setOnTouchListener true
        }
        MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> {
            mode = 0
            return@setOnTouchListener true
        }
        else -> return@setOnTouchListener true
    }
}

代碼實現和思路一樣,我們還需要在MotionEvent.ACTION_UP中復位模式,調用postTranslate進行偏移。 這樣基本上功能我們都簡單實現了。下面我們就需要優化了代碼,如果各位跟著實現了,就會發現縮放倍數太大了導致輕輕動一下就會放很大,還有別的都是需要我們優化的。

三、功能優化

3.1 優化縮放倍數太大問題

其實這個問題和我們處理move事件有關系,熟悉Android事件機制都知道一個完整的事件流程就是down->move.....move->up。知道了這個之后,再仔細看我們的代碼

val currentDistance = getDistance(event)
if (currentDistance &gt; 10f) {
	val scale = currentDistance / startDistance
	mImageView.imageMatrix = startMatrix.apply {
		postScale(scale, scale, getMidX(event), getMidY(event))
	}
}

在move事件中我們這樣處理的,計算縮放倍數然后縮放,大體一看是沒有什么問題的。但是,我們的move事件不止執行一次,這就導致我們的縮放不止執行一次,每次都是在原來的基礎上放大或者縮小。所以輕輕移動倍數就會很多。 最簡單的辦法就是我們記錄一下move過程中累計的倍數,如果到達最大值或者最小值就不讓放大或者縮小了。代碼如下。

if (scale > 1.0f) {
	sumScale += scale
} else {
	sumScale -= scale
}
if (sumScale >= maxScale || sumScale <= minScale) {
	return@setOnTouchListener true
}

簡單但是有效的方式。其中max和min,可以自己賦值。

3.2 保持原圖不縮小

實現起來也很簡單,需要先定義一個變量記錄當前縮放之后的倍數。大家測試就會發現,如果是放大操作那么倍數就會大于1如果是縮小倍數就會比1 小。我們就可以利用這點來處理我們的邏輯。

private var lastScaleFactor = 1f
if (scale * lastScaleFactor > 1.0f) {
	if (sumScale >= maxScale || sumScale <= minScale) {
		return@setOnTouchListener true
	}
	sumScale += scale
	mImageView.imageMatrix = startMatrix.apply {
		postScale(scale, scale, getMidX(event), getMidY(event))
		lastScaleFactor *= scale
	}
} else {
	sumScale -= scale
}

tips:demo好像不是放大不是很順暢,但是在項目里用Gilde加載后很流暢,猜測是照片大小問題。但是思路是一樣的問題不大。

以上就是“Android怎么實現單指滑動雙指縮放照片”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

宁强县| 尉犁县| 三原县| 天全县| 大连市| 陆丰市| 梁河县| 班玛县| 五指山市| 阿勒泰市| 个旧市| 城口县| 休宁县| 大同县| 潞西市| 新干县| 马公市| 阿巴嘎旗| 紫云| 宜章县| 陇西县| 茌平县| 镇远县| 花莲市| 敦化市| 达州市| 固原市| 巴林右旗| 东乡县| 石门县| 江达县| 山阴县| 高安市| 嘉定区| 五指山市| 布拖县| 弥渡县| 凤翔县| 双桥区| 湘潭县| 蛟河市|