【本文出自大圣代的技術(shù)專欄 http://blog.csdn.net/qq_23191031】
【轉(zhuǎn)載煩請注明出處杯道,尊重他人勞動成果就是對您自己的尊重】
前言
View在Android的世界中扮演著重要的角色,正是這些控件組成了一個又一個精美的App。View體系是Android界面編程的核心,雖然它不屬于四大組件但是它的重要行卻毫不遜色,這個系列我會陸續(xù)從View的滑動事件、View 的事件反饋、自定義View等多個方面逐步介紹Android View體系痛垛。如果能幫助到你,那是我莫大的榮幸桶蛔。
Android控件框架
在Android的世界中View是所有控件的基類(祖宗)匙头,其中也包括ViewGroup在內(nèi)。View是一個抽象的概念仔雷,特指界面中的某一個控件蹂析。而ViewGroup是代表著控件的集合舔示,其中可以包含多個View控件,并管理他們电抚。從某種角度上來講Android中的控件可以分為兩大類:View與ViewGroup惕稻。通過ViewGroup,整個界面的控件形成了一個樹形結(jié)構(gòu)蝙叛,這也就是我們常說的控件樹俺祠,上層的控件要負責(zé)測量與繪制下層的控件,并傳遞交互事件借帘。我們在開發(fā)中常常使用到的findViewById()方法锻煌,就是在控件樹中進行深度遍歷來查找對應(yīng)元素的。在每棵控件樹的頂部都存在著一個ViewParent對象姻蚓,它是整棵控件樹的核心所在,所有的交互管理事件都由它來統(tǒng)一調(diào)度和分配匣沼,從而對整個視圖進行整體控制狰挡。
在每一個Activity中都包含了一個Window,而這個Window通常上是由PhoneWindow實現(xiàn)的释涛,而PhoneWindow又將DecorView設(shè)置為整個界面的根布局加叁,DecorView作為根布局將要顯示的具體內(nèi)容呈現(xiàn)在PhoneWindow上,并提供了一些通用方法來操作界面唇撬。這里所有View的交互事件都由WindowManagerService(WMS)進行接收它匕,并通過Activity回調(diào)相應(yīng)的onClickListener。
在上面的視圖上我們可以看到此時屏幕被分成了兩部分:TitleView與ContentView窖认。如圖紅色的區(qū)域就是ContentView豫柬,contentView是一個ID為content的Framelayou這也是我們通過布局文件可以控制的區(qū)域,實際上我們所有的布局都設(shè)置在這樣的Fragmelayout中扑浸。
這也就是為什么Activity烧给、Fragment中設(shè)置根布局的方法叫做setContentView了。
插播: requestWindowFeature(Window.FEATURE_NO_TITLE) 與 setContentView() 調(diào)用順序的關(guān)系
在設(shè)置setContentView()方法之前我們可以通過
requestWindowFeature(Window.FEATURE_NO_TITLE)
方法設(shè)置標(biāo)簽來顯示全屏喝噪。如果你看了Activity源碼中的setContentVeiw()方法你會發(fā)現(xiàn)础嫡,當(dāng)setContentView()一旦調(diào)用,ContentView布局與TitleView會同時被加載酝惧,加載之后在調(diào)用requestWindowFeature(Window.FEATURE_NO_TITLE)
方法設(shè)置標(biāo)簽已經(jīng)沒有作用了榴鼎。所以只有在setContentView()
方法之前設(shè)置標(biāo)簽才能剔除TitleView
達到ContentView
占據(jù)全屏的效果。
當(dāng)Acitivity的生命周期中晚唇,當(dāng)onCreate()方法中調(diào)用setContentView方法后巫财,ActivityManagerService(AMS)會調(diào)用onResume()方法,此時系統(tǒng)才會把整個DecorView添加到PhoneWindow中顯示出來哩陕,至此界面回執(zhí)完成翁涤。
貼一張圖匯總一下吧
更詳細的說明請參見【Android View源碼分析(一)】setContentView加載視圖機制深度分析
Android的常用坐標(biāo)系
在Android的世界中我們最常用到的就是Android坐標(biāo)系(我認為稱為世界坐標(biāo)系更準(zhǔn)確)和視圖坐標(biāo)系了桥言。對于一個控件而言,它在Android世界坐標(biāo)系中的位置我們可以稱之為:絕對坐標(biāo)系葵礼;而在視圖坐標(biāo)系中号阿,指示的就是它的相對位置了。下面我們就來分析一下他們吧
1鸳粉,世界坐標(biāo)系
在Android的世界中扔涧,屏幕的左上角定點作為世界坐標(biāo)系的原點,從這個原點水平向右為X軸正方向届谈,原點垂直向下為Y軸正反向枯夜。
Android系統(tǒng)中為我們提供了getLocationOnScreen(int[] location)
方法來獲取控件在整個屏幕的絕對坐標(biāo),此時要注意的是:該坐標(biāo)是從屏幕的左上角(原點)開始獲取的艰山,所以也包括了狀態(tài)欄的高度湖雹,如下圖。
1.1曙搬,世界坐標(biāo)系中屏幕區(qū)域的劃分
通過上圖我們可以很直觀的看到Android的屏幕區(qū)域是如何劃分的摔吏。接下來我們就看看如何或者這些區(qū)域中的坐標(biāo)和度量方法吧。
//獲取屏幕區(qū)域的寬高等尺寸獲取
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
int widthPixels = metrics.widthPixels;
int heightPixels = metrics.heightPixels;
//應(yīng)用程序App區(qū)域?qū)捀叩瘸叽绔@取
Rect rect = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
//獲取狀態(tài)欄高度
Rect rect= new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);
int statusBarHeight = rectangle.top;
//View布局區(qū)域?qū)捀叩瘸叽绔@取
Rect rect = new Rect();
getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(rect);
注意:
這些方法最好都在Activity的
onWindowFocusChanged()
方法之后調(diào)用纵装,因為在Activity的聲明周期中 onCreate征讲、onStar、 onResume這些方法都不是界面visible的真正時刻橡娄,真正的visible是在onWindowFocusChanged()
方法執(zhí)行時才被執(zhí)行的诗箍。(onWindowFocusChanged()
是在onResume()
之后調(diào)用的,所以有的文章也會說是onResume()
之后調(diào)用,其實更加準(zhǔn)確的是在onWindowFocusChanged()
之后挽唉,此處我會在以后的文章中做詳細介紹)
2滤祖,視圖坐標(biāo)系
在日常開發(fā)中我們接觸最對的就是視圖坐標(biāo)系了,視圖坐標(biāo)系描述的是子控件在父控件中相對位置瓶籽。貼一張圖來說明一下
所謂視圖坐標(biāo)系是以控件(例如圖中的TextView)父視圖(圖中的ViewGroup))的左上角為坐標(biāo)原點的(綠色部分)氨距,從原出發(fā)水平向右為x軸正方向,垂直向下為y軸正方向來表示控件的相對位置的棘劣。
那么這個相對位置到底如何表示呢俏让,同樣看圖說話。
簡單的總結(jié)一下:
View提供的獲取坐標(biāo)方法
通過如下方法可以獲得View到其父控件(ViewGroup)的距離:
方法 | 解釋 |
---|---|
getTop() | 獲取View自身頂邊到其父布局頂邊的距離 |
getLeft() | 獲取View自身左邊到其父布局左邊的距離 |
getRight() | 獲取View自身右邊到其父布局左邊的距離 |
getBottom() | 獲取View自身底邊到其父布局頂邊的距離 |
getX() | 返回值為getLeft()+getTranslationX()茬暇,當(dāng)setTranslationX()時getLeft()不變首昔,getX()變。 |
getY() | 返回值為getTop()+getTranslationY()糙俗,當(dāng)setTranslationY()時getTop()不變勒奇,getY()變。 |
MotionEvent提供的獲取坐標(biāo)方法
我們看上圖那個觸摸點巧骚,我們知道無論是View還是ViewGroup赊颠,最終的點擊事件都會由onTouchEvent(MotionEvent event)方法來處理格二,MotionEvent也提供了各種獲取焦點坐標(biāo)的方法:
方法 | 解釋 |
---|---|
getX() | 獲取點擊事件距離控件左邊的距離,即視圖坐標(biāo) |
getY() | 獲取點擊事件距離控件頂邊的距離竣蹦,即視圖坐標(biāo) |
getRawX() | 獲取點擊事件距離整個屏幕左邊距離顶猜,即絕對坐標(biāo) |
getRawY() | 獲取點擊事件距離整個屏幕頂邊的的距離,即絕對坐標(biāo) |
注意:
View中的
getX()
痘括、getY()
方法只是與MotionEvent中的getX(
)长窄、getY()
方法只是重名而已,并不是一個纲菌。
上面就解釋了你在很多代碼中看見各種getXXX方法進行數(shù)學(xué)邏輯運算判斷的含義挠日。不過上面只是說了一些相對靜止的世界坐標(biāo)點關(guān)系,下面我們來看看幾個和上面方法緊密相關(guān)的View方法翰舌,此處在本篇文章中不是重點嚣潜,我會在以后的文章中做詳細講解。:
View寬高方法 | 解釋 |
---|---|
getWidth() | layout后有效椅贱,返回值是mRight-mLeft懂算,一般會參考measure的寬度(measure可能沒用),但不是必須的夜涕。 |
getHeight() | layout后有效,返回值是mBottom-mTop属愤,一般會參考measure的高度(measure可能沒用)女器,但不是必須的。 |
getMeasuredWidth() | 返回measure過程得到的mMeasuredWidth值住诸,供layout參考驾胆,或許沒用。 |
getMeasuredHeight() | 返回measure過程得到的mMeasuredHeight值贱呐,供layout參考丧诺,或許沒用。 |
參考:
如果說我比別人看得更遠些,那是因為我站在了巨人的肩上
《Android群英傳第三章》
Android中的坐標(biāo)系以及獲取坐標(biāo)的方法
Android應(yīng)用坐標(biāo)系統(tǒng)全面詳解