最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android?實(shí)現(xiàn)單指滑動(dòng)雙指縮放照片demo及過(guò)程解析

 更新時(shí)間:2023年04月25日 08:55:03   作者:風(fēng)羅伊曼  
這篇文章主要為大家介紹了Android?實(shí)現(xiàn)單指滑動(dòng)雙指縮放照片demo及過(guò)程解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

一、前景提示

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

二、實(shí)現(xiàn)功能

根據(jù)查詢到的結(jié)果分為兩種,一個(gè)是使用手勢(shì)監(jiān)聽來(lái)實(shí)現(xiàn),第二種監(jiān)聽觸摸事件來(lái)實(shí)現(xiàn)

  • 手勢(shì)監(jiān)聽-- ScaleGestureDetector Google提供的手勢(shì)監(jiān)聽類
  • 觸摸事件--OnTouchListener 自己監(jiān)聽觸摸事件自己實(shí)現(xiàn)放大縮小的邏輯

2.1 手勢(shì)監(jiān)聽

先寫布局文件

<?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>

再去實(shí)現(xiàn)手勢(shì)監(jiān)聽方法

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
        }
    }
}

代碼很簡(jiǎn)單直接使用ScaleGestureDetector去監(jiān)聽觸摸事件,手勢(shì)本質(zhì)也是Google內(nèi)部監(jiān)聽事件判斷再回調(diào)給我們使用。當(dāng)然我們這里不去查看源碼,只看實(shí)現(xiàn)過(guò)程。 在使用過(guò)程中發(fā)現(xiàn)這種縮放并不平滑,而且響應(yīng)有點(diǎn)慢,有延遲。猜想內(nèi)部是由很多其他的判斷吧。那我們只想簡(jiǎn)單一點(diǎn)怎么搞呢,那就是自己去判斷縮放,還有實(shí)現(xiàn)單指滑動(dòng)用手勢(shì)也不太好實(shí)現(xiàn)的樣子。所以我們?cè)囋嚨诙N方式實(shí)現(xiàn)也就是觸摸事件。

2.2 觸摸事件

首先我們實(shí)現(xiàn)一下縮放,我們還是沿用上次使用onTouchListener來(lái)處理我們的觸摸事件,布局文件中需要把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
}

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

計(jì)算如下。

 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)
    }

通過(guò)計(jì)算能得到直角邊和鄰邊,對(duì)他們使用勾股定理就能得到斜邊的值,也就是兩個(gè)手指之間的距離。 有做過(guò)觸摸事件監(jiān)聽的同學(xué)就應(yīng)該知道,我們下一步要監(jiān)聽移動(dòng)事件了也就是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事件中我們也需要對(duì)手指之間的距離進(jìn)行計(jì)算,如果距離超過(guò)10,就開始計(jì)算縮放倍數(shù),通過(guò)postScale進(jìn)行矩陣變換。 在MotionEvent.ACTION_POINTER_UP事件中對(duì)mode值進(jìn)行復(fù)位操作,畢竟還有個(gè)單指拖動(dòng)操作。 如果大家把上面的代碼運(yùn)行過(guò)就會(huì)發(fā)現(xiàn)怎么圖片沒(méi)有居中顯示,這是因?yàn)槲覀兊目s放屬性被改為矩陣也就是android:scaleType="matrix",那么想要圖片居中顯示怎么操作呢,只需要在觸摸時(shí)去改變縮放屬性,其他的時(shí)候不變即可。 我們把imageView恢復(fù)成android:scaleType="fitCenter",在onTouchListener中加入(放在when前即可)

mImageView.scaleType = ImageView.ScaleType.MATRIX

這樣一開始就可以保持圖片在中央了。 這樣縮放功能實(shí)現(xiàn)了,下面實(shí)現(xiàn)單指拖動(dòng)功能,思路很簡(jiǎn)單記錄第一次按下的位置,在移動(dòng)過(guò)程中計(jì)算應(yīng)該需要偏移的距離,再記錄下當(dāng)前的位置,以便于下次計(jì)算。

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) {
                // 單指拖動(dòng)
                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
    }
}

代碼實(shí)現(xiàn)和思路一樣,我們還需要在MotionEvent.ACTION_UP中復(fù)位模式,調(diào)用postTranslate進(jìn)行偏移。 這樣基本上功能我們都簡(jiǎn)單實(shí)現(xiàn)了。下面我們就需要優(yōu)化了代碼,如果各位跟著實(shí)現(xiàn)了,就會(huì)發(fā)現(xiàn)縮放倍數(shù)太大了導(dǎo)致輕輕動(dòng)一下就會(huì)放很大,還有別的都是需要我們優(yōu)化的。

三、功能優(yōu)化

3.1 優(yōu)化縮放倍數(shù)太大問(wèn)題

其實(shí)這個(gè)問(wèn)題和我們處理move事件有關(guān)系,熟悉Android事件機(jī)制都知道一個(gè)完整的事件流程就是down->move.....move->up。知道了這個(gè)之后,再仔細(xì)看我們的代碼

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

在move事件中我們這樣處理的,計(jì)算縮放倍數(shù)然后縮放,大體一看是沒(méi)有什么問(wèn)題的。但是,我們的move事件不止執(zhí)行一次,這就導(dǎo)致我們的縮放不止執(zhí)行一次,每次都是在原來(lái)的基礎(chǔ)上放大或者縮小。所以輕輕移動(dòng)倍數(shù)就會(huì)很多。 最簡(jiǎn)單的辦法就是我們記錄一下move過(guò)程中累計(jì)的倍數(shù),如果到達(dá)最大值或者最小值就不讓放大或者縮小了。代碼如下。

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

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

3.2 保持原圖不縮小

實(shí)現(xiàn)起來(lái)也很簡(jiǎn)單,需要先定義一個(gè)變量記錄當(dāng)前縮放之后的倍數(shù)。大家測(cè)試就會(huì)發(fā)現(xiàn),如果是放大操作那么倍數(shù)就會(huì)大于1如果是縮小倍數(shù)就會(huì)比1 小。我們就可以利用這點(diǎn)來(lái)處理我們的邏輯。

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
}

demo在這里點(diǎn)我點(diǎn)我

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

以上就是Android 實(shí)現(xiàn)單指滑動(dòng)雙指縮放照片demo及過(guò)程解析的詳細(xì)內(nèi)容,更多關(guān)于Android單指滑動(dòng)雙指縮放的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論

大城县| 阿拉尔市| 连山| 大邑县| 河北区| 宝山区| 台中县| 乐安县| 峨山| 张北县| 大埔县| 上饶市| 长顺县| 方山县| 南平市| 绵阳市| 图们市| 阳春市| 乐昌市| 大冶市| 平谷区| 阿巴嘎旗| 武汉市| 永新县| 汽车| 北流市| 襄樊市| 盖州市| 灵丘县| 翁源县| 东乌珠穆沁旗| 翁源县| 清苑县| 云龙县| 周宁县| 平南县| 柏乡县| 临湘市| 无锡市| 济宁市| 抚远县|