我們經(jīng)常在項目中使用
ImageView
的scaleType來設(shè)置顯示圖片的顯示方式诫惭,其中有一種type是矩陣變化matrix
方式我們可能在項目中會相對少用毛雇,具體matrix
的使用方式锭吨,不是文章的重點阿蝶,可以自行g(shù)oogle或者參考一下其他同學(xué)寫的文章,主要我們講一下Matrix.setScale()
方式在使用過程中踩的坑饼丘,同樣可以引申到Matrix的其他方法
參考文章
基本方法介紹
接下來我們簡單介紹一下Matrix
類常用的幾種變化:
-
setTranslate(float dx,float dy)
:控制Matrix進行平移夸浅; -
setSkew(float kx,float ky,float px,float py)
:控制Matrix以px,py為軸心進行傾斜仑最,kx,ky為X,Y方向上的傾斜距離; -
setRotate(float degress)
:控制Matrix進行旋轉(zhuǎn)帆喇,degress控制旋轉(zhuǎn)的角度警医; -
setRorate(float degress,float px,float py)
:設(shè)置以px,py為軸心進行旋轉(zhuǎn),degress控制旋轉(zhuǎn)角度番枚; -
setScale(float sx,float sy)
:設(shè)置Matrix進行縮放法严,sx,sy控制X,Y方向上的縮放比例; -
setScale(float sx,float sy,float px,float py)
:設(shè)置Matrix以px,py為軸心進行縮放(此處有坑)葫笼,sx,sy控制X,Y方向上的縮放比例深啤;
當(dāng)然這里只是set方法,還有相應(yīng)的preXXX()
和postXXX()
方法路星,其中的區(qū)別就是pre和post和執(zhí)行的順序有關(guān)系溯街,set開頭的方法是直接清除之前Matrix
內(nèi)的所有變化,重新設(shè)置一次洋丐。
- set是直接設(shè)置Matrix的值呈昔,每調(diào)用一次,之前
Matrix
內(nèi)的所有變化都重置友绝,整個Matrix的數(shù)組都會變掉 - post是后乘堤尾,當(dāng)前的矩陣乘以參數(shù)給出的矩陣∏停可以連續(xù)多次使用post郭宝,來完成所需的整個變換
- pre是前乘辞槐,參數(shù)給出的矩陣乘以當(dāng)前的矩陣。所以操作是在當(dāng)前矩陣的最前面發(fā)生的粘室。
我們知道m(xù)atrix實際上是一個3x3的矩陣榄檬,根據(jù)線性代數(shù)里面的知識,矩陣相乘衔统,前乘和后乘得到的結(jié)果是不一樣的鹿榜,所以我們做的矩陣任何的變化(平移、縮放锦爵、旋轉(zhuǎn)等)本質(zhì)上都是對matrix矩陣進行操作舱殿,理解了這一點,對我們后面的踩坑有莫大的幫助
踩坑
這里講一下在上面setScale(float sx,float sy,float px,float py)
在實際應(yīng)用中的坑险掀,我們先看下Android注釋是怎么解釋這個方法的
Set the matrix to scale by sx and sy, with a pivot point at (px, py).The pivot point is the coordinate that should remain unchanged by the specified transformation.
這里什么意思呢怀薛?就是設(shè)置matrix根據(jù)sx和sy的值進行縮放,那么px和py是什么鬼呢迷郑?網(wǎng)絡(luò)上很多解釋是解釋成中心點,在這個中心點的基礎(chǔ)上進行縮放创倔,其實不然嗡害,后面我們會講到這里為什么會有坑,如果你把這兩個參數(shù)當(dāng)成縮放的中心點來看的話畦攘,在實際測試過程中你會發(fā)現(xiàn)圖片并不是在當(dāng)前中心點縮放霸妹,在縮放的同時,位置還會進行偏移(這里行為跟postScale(float sx,float sy,float px,float py)
中的px與py不一致知押,post行為是真的以此處為中心點進行縮放)叹螟。這里為什么會這樣呢?坑了我好久台盯,不死心罢绽,覺得一定有什么貓膩,所以最好的方式静盅,就是看看他的源碼良价,看下到底是什么原因?qū)е碌?/p>
先看下Matrix.java
類
/**
* Set the matrix to scale by sx and sy, with a pivot point at (px, py).
* The pivot point is the coordinate that should remain unchanged by the
* specified transformation.
*/
public void setScale(float sx, float sy, float px, float py) {
native_setScale(native_instance, sx, sy, px, py);
}
納尼?調(diào)用了native_setScale
方法蒿叠,可能很多人看到這一步需要去看native層的方法就放棄明垢,但是沒有什么能難倒攻城獅,不急市咽,我們接著往下看
private static native void native_setScale(int native_object,
float sx, float sy, float px, float py);
根據(jù)一些jni的知識痊银,我們知道在native層的代碼肯定會有一個native_setScale
的方法與之對應(yīng),所以我們需要去找到該native層的實現(xiàn)方法施绎,看看他的代碼溯革。那么接下來我們?nèi)ツ睦镎夷卣晟靠赡芎芏嗤瑢W(xué)會想到去下源碼,可是源碼何其多鬓照,網(wǎng)絡(luò)情況良好的情況下個幾天都是正常的熔酷,這里我推薦一個網(wǎng)站androidxref,這里面有你所需要的各個版本的源碼豺裆,并且可以根據(jù)查詢條件快速查找拒秘,查找也是非常快的臭猜。最后我們在Matrix.cpp
中找到了
{"native_setScale","(IFFFF)V", (void*) SkMatrixGlue::setScale__FFFF},
// 對應(yīng)的方法
static void setScale__FFFF(JNIEnv* env, jobject clazz, SkMatrix* obj, jfloat sx, jfloat sy, jfloat px, jfloat py) {
SkScalar sx_ = SkFloatToScalar(sx);
SkScalar sy_ = SkFloatToScalar(sy);
SkScalar px_ = SkFloatToScalar(px);
SkScalar py_ = SkFloatToScalar(py);
obj->setScale(sx_, sy_, px_, py_);
}
最后我們看一下obj->setScale(sx_, sy_, px_, py_);
的實現(xiàn)
void SkMatrix::setScale(SkScalar sx, SkScalar sy, SkScalar px, SkScalar py) {
if (SK_Scalar1 == sx && SK_Scalar1 == sy) {
this->reset();
} else {
fMat[kMScaleX] = sx;
fMat[kMScaleY] = sy;
fMat[kMTransX] = px - SkScalarMul(sx, px);
fMat[kMTransY] = py - SkScalarMul(sy, py);
fMat[kMPersp2] = kMatrix22Elem;
fMat[kMSkewX] = fMat[kMSkewY] =
fMat[kMPersp0] = fMat[kMPersp1] = 0;
this->setTypeMask(kScale_Mask | kTranslate_Mask | kRectStaysRect_Mask);
}
}
這里我們看到實際上不當(dāng)當(dāng)進行了縮放操作躺酒,還進行了平移操作,所以我們實際進行縮放的時候蔑歌,還伴隨著平移操作羹应,實際上這里最后換算成的算法是fMat[kMTransX] = px - sx * px;
。所以想要保持在縮放過程中不進行平移次屠,那么我們需要改造一下算法px = fMat[kMTransX]/(1-sx)
园匹,那么有人會問,那我怎么能提前知道fMat[kMTransX]
的值呢劫灶,這里就要在每次縮放操作前裸违,根據(jù)當(dāng)前需要縮放的比例,預(yù)測量出值本昏,這里就不介紹怎么預(yù)測量供汛,相信機智的小伙伴肯定已經(jīng)有想法了~好了,這里就講到這里涌穆,有問題請留言怔昨,我會及時回復(fù),歡迎交流~