您好,登錄后才能下訂單哦!
今天小編給大家分享一下Android怎么實現單指滑動雙指縮放照片的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
最近接到一個查看大圖的需求,現在圖片展示還不夠大,要求還要能縮小能放大還能保存照片。直接開始Google實現方式。
根據查詢到的結果分為兩種,一個是使用手勢監聽來實現,第二種監聽觸摸事件來實現
手勢監聽-- ScaleGestureDetector Google提供的手勢監聽類
觸摸事件--OnTouchListener 自己監聽觸摸事件自己實現放大縮小的邏輯
先寫布局文件
<?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內部監聽事件判斷再回調給我們使用。當然我們這里不去查看源碼,只看實現過程。 在使用過程中發現這種縮放并不平滑,而且響應有點慢,有延遲。猜想內部是由很多其他的判斷吧。那我們只想簡單一點怎么搞呢,那就是自己去判斷縮放,還有實現單指滑動用手勢也不太好實現的樣子。所以我們試試第二種方式實現也就是觸摸事件。
首先我們實現一下縮放,我們還是沿用上次使用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的矩陣保存,后續對照片移動,縮放都是通過變換矩陣來實現的。 至于計算兩個手指之間的距離用的勾股定理,來個示意圖,大家就明白了。
計算如下。
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進行偏移。 這樣基本上功能我們都簡單實現了。下面我們就需要優化了代碼,如果各位跟著實現了,就會發現縮放倍數太大了導致輕輕動一下就會放很大,還有別的都是需要我們優化的。
其實這個問題和我們處理move事件有關系,熟悉Android事件機制都知道一個完整的事件流程就是down->move.....move->up。知道了這個之后,再仔細看我們的代碼
val currentDistance = getDistance(event) if (currentDistance > 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,可以自己賦值。
實現起來也很簡單,需要先定義一個變量記錄當前縮放之后的倍數。大家測試就會發現,如果是放大操作那么倍數就會大于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怎么實現單指滑動雙指縮放照片”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。