說來說去都不如 畫圖示意 簡單易懂啊!!!真是的! 來吧~~先上張圖~~!
(一)首先明確一下 android 中的座標系統 :
(三)MotionEvent類中 getRowX()和 getX()的區別:
event.getRowX():觸摸點相對於屏幕原點的x座標
event.getX(): 觸摸點相對於其所在組件原點的x座標
於是乎: view.getScrollY() + event.getY() 就得到了view中的觸摸點在Y軸上的偏移量
(四)TextView類中
有個 getLayout()方法:the Layout that is currently being used to display the text. This
can be null if the text or width has recently changes.
其返回類型是Layout ,也就是返回textView的佈局。
然後重要的是通過這個 layout調用一個方法:
getLineForVertical(int verticalPointPosition) //得到某點在垂直方向上的行數值
於是綜上所述,在實際的觸摸事件中可以這樣使用:
Layout layout=textView.getLayout();
int line = layout.getLineForVertical(textView.getScrollY() + (int) event.getY());
//得到觸摸點在textView中垂直方向上的行數值。參數是觸摸點在Y軸上的偏移量
接下來繼續介紹一個方法,要用到上邊的 layout 和 line:
layout.getOffsetForHorizontal( line , (int) event.getX() );
//得到觸摸點在某一行水平方向上的偏移量。
參數分別是: 該行行數值 和 觸摸點在該行X軸上的偏移量。
此方法得到的該值會根據該行上的文字的多少而變化,並不是橫向上的像素大小;
android.view.View.layout(int l, int t, int r, int b) layout的過程就是確定View在屏幕上顯示的具體位置,在代碼中就是設置其成員變量mLeft,mTop,mRight,mBottom的值,這幾個值構成的矩形區域就是該View顯示的位置,不過這裏的具體位置都是相對與父視圖的位置。mLeft代表當前view.layout的這個view的左邊緣離它的父視圖左邊緣的距離,拿上面“子視圖2.layout(int l, int t, int r, int b) ”來說,它的父視圖便是子視圖1,2,3合起來形成的整個大矩形,那麼這裏將父視圖的左上角定爲(0,0),那麼可以確定mLeft爲一個子視圖寬度320,以此類推,mTop指當前view的上邊緣離父視圖上邊緣的距離。而以此爲界,mRight所指的是當前view的右邊緣離父視圖左邊緣的距離,一眼可以看出值爲640(mLeft+自己的寬度),mBottom也是指當前view的下邊緣離父視圖的上邊緣的距離。至於爲何如此,大概是因爲座標系的緣故,座標中的任何點都必須以(0,0)爲起點,XY軸爲衡量。
視圖左側位置 view.getLeft()
視圖右側位置 view.getRight()
視圖頂部位置 view.getTop();
視圖底部位置 view.getBottom();
這四個方法所獲取到的各個左上右下的值與layout的四個參數代表的是一樣的,都是相對父視圖的左邊緣與上邊緣。
視圖寬度 view.getWidth();
視圖高度 view.getHeight() ;
這兩個方法獲取的是該view的高和寬,僅僅在滑動的情況下,或者說該view的大小如果不發生變化,它的值是不會變的。
getMeasuredWidth();
getMeasuredHeight();
說到這裏就不得不提getWidth()、getHeight()和getMeasuredWidth()、getMeasuredHeight()這兩對函數之間的區別,getMeasuredWidth()、getMeasuredHeight()返回的是measure過程得到的mMeasuredWidth和mMeasuredHeight的值,而getWidth()和getHeight()返回的是mRight - mLeft和mBottom - mTop的值。一般情況下layout過程會參考measure過程中計算得到的mMeasuredWidth和mMeasuredHeight來安排子視圖在父視圖中顯示的位置,但這不是必須的,measure過程得到的結果可能完全沒有實際用處,特別是對於一些自定義的ViewGroup,其子視圖的個數、位置和大小都是固定的,這時候我們可以忽略整個measure過程,只在layout函數中傳入的4個參數來安排每個子視圖的具體位置。
view.getX();
view.getY();
getX和getY獲取到的值爲相對於父視圖而言的兩個左邊緣和上邊緣的距離。
view.getLocationOnScreen(location); 該方法可以獲取到當前view與屏幕的關係,location(0)代表X值,表示該view的左邊緣與屏幕的左邊緣之間的距離。可以想象,當滑屏產生,view開始移動該值肯定會改變的。location(1)代表Y值,表示該view的上邊緣與屏幕的上邊緣之間的距離,該距離肯定是包含標題欄的高度的。
getLocationInWindow();
ps:View.getLocationInWindow()和 View.getLocationOnScreen()在window佔據全部screen時,返回值相同,不同的典型情況是在Dialog中時。當Dialog出現在屏幕中間時,View.getLocationOnScreen()取得的值要比View.getLocationInWindow()取得的值要大。
VelocityTracker.getXVelocity() 指滑動速度包括速率和方向兩個方面,往左滑動小於0,值爲負;往右滑動大於0,值爲正。
view.scrollTo(x,y) 將整個父視圖的左上角定爲(0,0),再移動這個屏幕的左上角到父視圖的點(x,y)處,注意此處的x和y是根據父視圖的座標系來定的。
view.scrollBy(x,y) x代表橫向移動的距離,y代表縱向移動的距離
view.getScrollX
view.getScrollY
將整個父視圖的左上角定爲(0,0),那麼子view.getScrollX會獲取到屏幕左邊緣減去父視圖的左邊緣爲0的距離,特別當滑屏時,父視圖會被迫隱藏一部分,因爲屏幕的大小是固定的。getScrollY以此類推。
event.getX()
event.getY()
該方法是不受視圖影響的,X和Y的值僅僅代表手指在以左上角(0,0)爲原點的屏幕觸摸點的座標值。
Scroller.getCurrY()
Scroller.getCurrX()
該方法拿橫軸來說,代表屏幕的左邊緣離父視圖的左邊緣的距離。
Scroller.startScroll(int startX, int startY, int dx, int dy)
四個參數分別表示起點的座標和滑動的向量,即從(startX,startY)開始滑動,橫向滑動dx的距離,縱向滑動dy的距離(正值向左滑,負值向右滑),而這裏的startX,startY又是參照的父視圖左上角爲原點座標的座標系,滑屏時經常使用getScrollX()和getScrollY()來代表屏幕左邊緣和上邊緣處於父視圖座標系的具體位置
TranslateAnimation()
參數參照:http://blog.sina.com.cn/s/blog_90b91bf10101ai3e.html
以上是在做滑屏控件經常用到的方法,一方面需要了解layout和measure的基本流程,更重要一方面,當你想要實現某一個效果的時候,比如slidingmenu那樣的控件,查看源碼我們可以知道它是繼承的ViewGroup,該怎樣入手去做呢。
首先,需要了解它的父視圖是什麼,slidingmenu爲例,打開程序,第一眼,是一個很普通的視圖頁面,當向右滑動手指,這個視圖頁面開始向右邊移動,而從左邊會慢慢移出來另一部分視圖,看上去像是抽出來的或者是隱藏的,事實上拋開陰影效果來講,想象手機屏幕的左邊有一部分我們看不到的視圖,它就是這個被抽出來的menu視圖了。概括來說,一個主view,一個menu其實是並排於一個大視圖上面的。
找到了父視圖,接下來就好辦了,認定這個父視圖的寬度就是主view的寬度和menu的寬度之和(暫不考慮padding之類),高度就是屏幕的高度,那麼在思維當中這個二維平面就產生了,將它想成一張紙,然後對準主view將這張紙貼到手機屏幕上,左右滑動,會看到其實slidingmenu也就是這麼個效果。
然後,實現的思路會清晰很多。定義這個父視圖爲myview繼承viewgroup,原因在於儘管主view和menu並排在一個大view下,但畢竟兩者的內容不同,後面需要放進不同的控件處理不同的事件,這個父視圖內包含着兩個view,到時候處理起來會方便很多,setcontentview爲這個父視圖,那麼打開程序的第一眼就會看到它。再定義這兩個view設置好兩個內容佈局,並將它們addview添加到myview當中。外部工作基本就完成了,可以呈現父視圖,並且父視圖內有兩個子view。
接下來,需要去完善一些細節,父視圖內的子view該如何放置,這是關乎成敗的一環,也就是如何將這張紙貼到我們希望的位置,這時就是onlayout的處理了,處理好屏幕,父視圖子view之間的位置關係,通過各自的layout參數設置來擺放妥當各個view,比如開始的時候menu是隱藏的,這個就是通過位置的擺放設置的,然後它是從左邊滑出來的,說明它處於父視圖的左邊位置,而主view處於相對右邊的位置,而屏幕剛好也處於父視圖右邊的位置,恰好能看到主view的全貌,在腦海裏如果能有清晰的畫面出現,實現起來就會輕鬆很多。當實現了這個擺放,就可以理解menudrawer裏面上下左右都可以滑出menu的結構了。
最後,便是滑動效果,請相信這樣的控件裏面,任何處理肯定都會和view位置的擺放扯上關係,滑動方向,滑動距離等等都涉及到座標的處理。這也是爲何上面列出那些常用的獲取view座標的方法。
總結下來,構建類似這樣的控件,也就這三點,明確父子視圖和屏幕的關係,通過座標和位置參數設置它們的關係,處理這些關係發生變化的情況。
當然,事實上slidingmenu遠遠沒這麼簡單,其中爲了方便後續開發,它內置了很多接口和處理,大多數都是位置座標和事件監聽相關聯,而萬變不離其宗的是,它也肯定有這三個方面的構建,理解了這些基本的東西,嘗試做一些自己想象的效果,對自定義的理解來說,進步會非常大。
資料來自互聯網
getLocalVisibleRect , 返回一個填充的Rect對象, 感覺是這個View的Rect大小,left,top取到的都是0
getGlobalVisibleRect , 獲取全局座標系的一個視圖區域, 返回一個填充的Rect對象;該Rect是基於總整個屏幕的
getLocationOnScreen ,計算該視圖在全局座標系中的x,y值,(注意這個值是要從屏幕頂端算起,也就是索包括了通知欄的高度)//獲取在當前屏幕內的絕對座標
getLocationInWindow ,計算該視圖在它所在的widnow的座標x,y值,//獲取在整個窗口內的絕對座標 (不是很理解= =、)
getLeft , getTop, getBottom, getRight, 這一組是獲取相對在它父親裏的座標
**注**:如果在Activity的OnCreate()事件輸出那些參數,是全爲0,要等UI控件都加載完了才能獲取到這些
example:
int[] location = new int[2];
v.getLocationOnScreen(location);
int x = location[0];
int y = location[1];