瞭解Android Matrix轉換

zhang523發表於2018-12-07

ImageView

原文 瞭解Android Matrix轉換

很多年前,在學校我學習了矩陣。 我記不太清楚了,但我記得的是在想,“但是......你對這些知識做了什麼呢?”

快進幾年,我開始作為Android開發人員工作,不得不使用ImageViewscaleType - 如果你看過所有可能的型別,你已經注意到其中一個是matrix 。 多年來,我一直避開它,使用其他規模型別或解決問題。 然而幾周前我正在開發一種設計,其中元件的背景影象應該與檢視的左上角對齊,而不執行任何縮放,如下所示:

ImageView

所以我繼續新增了一個ImageView並再次瀏覽了所有scaleType - 希望有一個我錯過的型別,但是在我嘗試使用scaleType="matrix"它們都沒有完全正確,它完全符合我的要求。 但為什麼這有效呢? 它實際上做了什麼?

所以我看了一下Matrix文件:

Matrix類包含一個3x3矩陣,用於轉換座標

嗯......不是很有幫助。 幸運的是,我並不是唯一一個不懂的人, Arnaud Bos寫了一篇很棒的文章 ,詳細解釋了背後的數學(警告:如果你打算閱讀它 - 可能需要一杯咖啡(或者兩杯)的時間)。 如果你在那篇文章的中途迷路了,我不能責怪你 - 這很複雜,但好訊息是你不必理解數學就能用matrix(雖然它很有幫助。

我們怎樣才能使用Matrix

正如我之前提到的,我們必須在ImageView上設定scaleType="matrix" 。 但要真正能夠使用它,我們必須在程式碼中設定imageMatrix

imageView.imageMatrix = Matrix().apply {
    // perform transformations
}
複製程式碼

現在我們有了這個 - 我們可以用它做什麼? 矩陣支援一系列不同的變換,如translatescalerotateskew 。 如果這些聽起來很熟悉,因為它們(大多數)與View,動畫或畫布上的相同。

您會發現,對於每個操作,都有一setpre版本。 稍後我會談到這一點,但是現在我們只使用set版本。

所以,我們能做些什麼?

Translating (移動)

ImageView

設定translation意味著將影象移動到其他位置。 您所要做的就是使用Matrix上所需的xy座標呼叫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 (縮放)

ImageView

Scaling(正如您可能已經猜到的那樣)定義了影象的大小。 您可以定義兩個值 - 一個用於x軸,另一個用於y軸。 但是,通過縮放,您還可以設定軸心點。

樞軸點定義轉換將保持不變的點。 預設情況下它是0, 0 - 左上角 - 意味著影象將向右和向下延伸,左上角保持不變 - 就像左邊的上面的gif一樣。

如果要從中心縮放影象(如右側的gif),可以將軸設定為影象的中心,如下所示:

setScale(0.5f, 0.5f, dWidth / 2f, dHeight / 2f)

複製程式碼

這將使影象縮放到其大小的一半,其中心的樞軸點。 如果你只想從左上角縮放它,你可以省略最後兩個引數,如下所示:

setScale(0.5f, 0.5f)
複製程式碼

但是你可以用縮放做更多的事情。 如果提供負比例值,則基本上可以圍繞軸(或兩個)映象影象。 相當漂亮!

ImageView

Rotation (旋轉)

ImageView

你猜對了! 通過旋轉,您可以旋轉影象。 在這裡,我們提供了我們想要旋轉的角度,以及一個類似於比例的可選樞軸點。

  setRotate(45f,dWidth / 2f,dHeight / 2f複製程式碼

將影象圍繞影象中心旋轉45度。 如果將它旋轉-45度,它將向左旋轉。

Skewing (傾斜)

imageView

傾斜可能是你以前沒有聽說過的轉變。 傾斜將沿軸(或兩個)拉伸您的影象,就像上面的GIF一樣。 我們來看一個例子:

  setSkew(1f0f,dWidth / 2f,dHeight / 2f複製程式碼

這會使影象在x軸(以及中心點周圍)偏斜1,這是影象的寬度,導致影象傾斜45度,就像上面的gif一樣。

應用多個轉換

我們現在可以translatescalerotateskew影象,但是如果我們想要將它們組合起來呢? 顯而易見的事情可能是連續呼叫多個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和檢視的中心,而在第二個版本中,我們使用preScaledrawable的中心。

使用postScale ,將在translate後應用比例轉換。 由於影象已經在檢視中居中,我們必須使用檢視的中心點作為樞軸。

val (viewCenterX, viewCenterY) = vWidth / 2f to vHeight / 2f
postScale(0.5f, 0.5f, viewCenterX, viewCenterY)
複製程式碼

所以從頭開始回顧這個例子 - 為什麼應用scaleType="matrix"只是起作用? 使用預設martix,比例將為1,平移,旋轉和傾斜將為0,因此影象將在左上角繪製。 所以它完全符合我的需要!

下次您必須以預設scaleType不起作用的方式佈局影象時 - 嘗試使用Matrix

matrix

相關文章