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