解決 Android 中 View 的 setPivotX 和 setPivotY 不生效的問題以及設定縮放中心的方法
背景是這樣的:有一個需求要對下方的關注按鈕實現如下動畫,動畫的最後要根據滑動位置對關注按鈕進行縮放,縮放結束時整體大小為控制元件原始大小的90%,最終效果圖如下所示(模擬器是 4.2 的系統,最上面的沉浸式有點問題,忽略之):
如圖,關注按鈕向上滑動到某臨界位置時,按鈕需要縮小;同樣,向下滑動到相同的臨界位置時,需要放大。
在佈局檔案中,關注按鈕整體佈局為RelativeLayout
,愛心ImageView
和文字TextView
居中。
由於動畫需要根據滑動位置動態計算,因此不能用縮放動畫實現。當滑動到臨界位置時,我做如下處理:
float scale = 1- 0.1f * (scrollY - 100 ) / 10; //scale範圍:1.0~0.9
mLayoutFollow.setPivotX(1.0f);
mLayoutFollow.setPivotY(1.0f);
mLayoutFollow.setScaleX(scale);
mLayoutFollow.setScaleY(scale);
上述程式碼中,縮放比例scale
的值是根據滑動距離scrollY
動態計算出來的,其取值範圍為1.0~0.9
;設定setPivotX(1.0f)
和setPivotY(1.0f)
的目的,是想讓關注按鈕(RelativeLayout
)的縮放軸點為右下角,但是悲劇的是,這樣的設定並沒有生效,縮放的中心始終為左上角。這與 xml 中設定PivotX
PivotY
屬性是相矛盾的(在 xml中,都設定為0.0f,則縮放中心為控制元件左上角;都設定為0.5f,則縮放中心為控制元件中心;都設定為1.0f,則縮放中心為控制元件右下角,詳見博文 圖解 Android 動畫中 android:pivotX 和 android:pivotY 屬性的含義)。
測試後發現,只要 setPivotX(float x) 或 setPivotY(float y) 中有一個值為 0,最後的結果都是縮放中心為左上角;而當不設定這兩個值時(即去掉setPivotX/Y
這兩行程式碼),縮放中心為控制元件中心。(奇怪,為什麼呢?。。)
想到 xml 中還可以將PivotX
屬性設定為整數,於是這樣設定:
mLayoutFollow.setPivotX(100);
mLayoutFollow.setPivotY(0.0f);
發現,縮放中心變為控制元件最上方,但偏右一些,說明setPivotX(100)
生效了。
據此啟發,於是,得到最終設定縮放中心為控制元件右下角的方法如下:
float scale = 1- 0.1f * (scrollY - 100) / 10;//scale範圍:1.0~0.9
mLayoutFollow.setPivotX(X); //X為控制元件寬度的px值,以實際情況為準
mLayoutFollow.setPivotY(Y); //Y為控制元件寬度的px值,以實際情況為準
mLayoutFollow.setScaleX(scale);
mLayoutFollow.setScaleY(scale);
設定控制元件縮放中心為其他位置的方法亦類似。
檢視 View 的setPivotX
原始碼如下:
/**
* Sets the x location of the point around which the view is
* {@link #setRotation(float) rotated} and {@link #setScaleX(float) scaled}.
* By default, the pivot point is centered on the object.
* Setting this property disables this behavior and causes the view to use only the
* explicitly set pivotX and pivotY values.
*
* @param pivotX The x location of the pivot point.
* @see #getRotation()
* @see #getScaleX()
* @see #getScaleY()
* @see #getPivotY()
*
* @attr ref android.R.styleable#View_transformPivotX
*/
public void setPivotX(float pivotX) {
if (!mRenderNode.isPivotExplicitlySet() || pivotX != getPivotX()) {
invalidateViewProperty(true, false);
mRenderNode.setPivotX(pivotX);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
}
}
翻譯前面的註釋:預設情況下(不設定時),軸點位於控制元件中心處;設定該屬性後,控制元件只會明確地使用所設定的軸點位置。而引數pivotX
的含義是軸點的 x 位置(這裡應該就是絕對位置了)。
不知道這樣的理解是不是正確的。。。╮(╯▽╰)╭ 尷尬~~~拋磚引玉,還望知道的小夥伴告知~