很多年前,在學(xué)校我學(xué)習(xí)了矩陣。 我記不太清楚了,但我記得的是在想,“但是......你對這些知識做了什么呢?”
快進(jìn)幾年,我開始作為Android開發(fā)人員工作,不得不使用ImageView的scaleType - 如果你看過所有可能的類型,你已經(jīng)注意到其中一個是matrix 。 多年來,我一直避開它,使用其他規(guī)模類型或解決問題。 然而幾周前我正在開發(fā)一種設(shè)計,其中組件的背景圖像應(yīng)該與視圖的左上角對齊,而不執(zhí)行任何縮放,如下所示:
所以我繼續(xù)添加了一個ImageView并再次瀏覽了所有scaleType - 希望有一個我錯過的類型,但是在我嘗試使用scaleType="matrix"它們都沒有完全正確,它完全符合我的要求。 但為什么這有效呢? 它實際上做了什么?
所以我看了一下Matrix文檔:
Matrix類包含一個3x3矩陣,用于轉(zhuǎn)換坐標(biāo)
嗯......不是很有幫助。 幸運的是,我并不是唯一一個不懂的人, Arnaud Bos寫了一篇很棒的文章 ,詳細(xì)解釋了背后的數(shù)學(xué)(警告:如果你打算閱讀它 - 可能需要一杯咖啡(或者兩杯)的時間)。 如果你在那篇文章的中途迷路了,我不能責(zé)怪你 - 這很復(fù)雜,但好消息是你不必理解數(shù)學(xué)就能用matrix(雖然它很有幫助。
我們怎樣才能使用Matrix?
正如我之前提到的,我們必須在ImageView上設(shè)置scaleType="matrix" 。 但要真正能夠使用它,我們必須在代碼中設(shè)置imageMatrix :
imageView.imageMatrix = Matrix().apply {
// perform transformations
}
現(xiàn)在我們有了這個 - 我們可以用它做什么? 矩陣支持一系列不同的變換,如translate , scale , rotate和skew 。 如果這些聽起來很熟悉,因為它們(大多數(shù))與View,動畫或畫布上的相同。
您會發(fā)現(xiàn),對于每個操作,都有一set , pre版本。 稍后我會談到這一點,但是現(xiàn)在我們只使用set版本。
所以,我們能做些什么?
Translating (移動)
設(shè)置translation意味著將圖像移動到其他位置。 您所要做的就是使用Matrix上所需的x和y坐標(biāo)調(diào)用setTranslate :
val dWidth = imageView.drawable.intrinsicWidth
val dHeight = imageView.drawable.intrinsicHeight
val vWidth = imageView.measuredWidth
val vHeight = imageView.measuredHeight
setTranslate(
round((vWidth - dWidth) * 0.5f),
round((vHeight - dHeight) * 0.5f)
)
在這個例子中,我們只是將drawable置于View中心,這導(dǎo)致與在ImageView上設(shè)置scaleType="center"相同的行為。 那么讓我們來看看ImageView如何做到這一點:
mDrawMatrix.setTranslate(Math.round((vwidth - dwidth) * 0.5f),
Math.round((vheight - dheight) * 0.5f));
它完全一樣! 所以我們不知道它已經(jīng)使用了矩陣變換。
Scaling (縮放)
Scaling(正如您可能已經(jīng)猜到的那樣)定義了圖像的大小。 您可以定義兩個值 - 一個用于x軸,另一個用于y軸。 但是,通過縮放,您還可以設(shè)置軸心點。
樞軸點定義轉(zhuǎn)換將保持不變的點。 默認(rèn)情況下它是0, 0 - 左上角 - 意味著圖像將向右和向下延伸,左上角保持不變 - 就像左邊的上面的gif一樣。
如果要從中心縮放圖像(如右側(cè)的gif),可以將軸設(shè)置為圖像的中心,如下所示:
setScale(0.5f, 0.5f, dWidth / 2f, dHeight / 2f)
這將使圖像縮放到其大小的一半,其中心的樞軸點。 如果你只想從左上角縮放它,你可以省略最后兩個參數(shù),如下所示:
setScale(0.5f, 0.5f)
但是你可以用縮放做更多的事情。 如果提供負(fù)比例值,則基本上可以圍繞軸(或兩個)鏡像圖像。 相當(dāng)漂亮!
Rotation (旋轉(zhuǎn))
你猜對了! 通過旋轉(zhuǎn),您可以旋轉(zhuǎn)圖像。 在這里,我們提供了我們想要旋轉(zhuǎn)的角度,以及一個類似于比例的可選樞軸點。
setRotate(45f,dWidth / 2f,dHeight / 2f)
將圖像圍繞圖像中心旋轉(zhuǎn)45度。 如果將它旋轉(zhuǎn)-45度,它將向左旋轉(zhuǎn)。
Skewing (傾斜)
傾斜可能是你以前沒有聽說過的轉(zhuǎn)變。 傾斜將沿軸(或兩個)拉伸您的圖像,就像上面的GIF一樣。 我們來看一個例子:
setSkew(1f,0f,dWidth / 2f,dHeight / 2f)
這會使圖像在x軸(以及中心點周圍)偏斜1,這是圖像的寬度,導(dǎo)致圖像傾斜45度,就像上面的gif一樣。
應(yīng)用多個轉(zhuǎn)換
我們現(xiàn)在可以translate,scale,rotate和skew圖像,但是如果我們想要將它們組合起來呢? 顯而易見的事情可能是連續(xù)調(diào)用多個set方法。 但是,這只會應(yīng)用最后一次轉(zhuǎn)換 - 所有以前的轉(zhuǎn)換都將被覆蓋。 這是因為set方法基本上重置了Matrix。
但正如我之前提到的,還有每個轉(zhuǎn)換的post版本。 通過使用這些,我們可以應(yīng)用多個變換,并真正利用matrix的魔力。
但是pre的區(qū)別是什么? 對于第一次轉(zhuǎn)換,使用三種版本中的哪一種沒有區(qū)別,但對于任何未來的轉(zhuǎn)換,它可以產(chǎn)生很大的不同。
假設(shè)我們想要將圖像轉(zhuǎn)換為視圖的中心并將其縮放到一半大小。 這兩個版本將產(chǎn)生預(yù)期的效果:
val drawableLeft = round((vWidth - dWidth) * 0.5f)
val drawableTop = round((vHeight - dHeight) * 0.5f)
// Version 1
setTranslate(drawableLeft, drawableTop)
val (viewCenterX, viewCenterY) = vWidth / 2f to vHeight / 2f
postScale(0.5f, 0.5f, viewCenterX, viewCenterY)
// Version 2
setTranslate(drawableLeft, drawableTop)
val (drawableCenterX, drawableCenterY) = dWidth / 2f to dHeight / 2f
preScale(0.5f, 0.5f, drawableCenterX, drawableCenterY)
請注意,在第一個版本中,我們使用postScale和視圖的中心,而在第二個版本中,我們使用preScale和drawable的中心。
使用postScale ,將在translate后應(yīng)用比例轉(zhuǎn)換。 由于圖像已經(jīng)在視圖中居中,我們必須使用視圖的中心點作為樞軸。
val (viewCenterX, viewCenterY) = vWidth / 2f to vHeight / 2f
postScale(0.5f, 0.5f, viewCenterX, viewCenterY)
所以從頭開始回顧這個例子 - 為什么應(yīng)用scaleType="matrix"只是起作用? 使用默認(rèn)martix,比例將為1,平移,旋轉(zhuǎn)和傾斜將為0,因此圖像將在左上角繪制。 所以它完全符合我的需要!
下次您必須以默認(rèn)scaleType不起作用的方式布局圖像時 - 嘗試使用Matrix!