1.Android控件架構
下圖是UI界面架構圖,每一個Activity都有一個Window對象,一般是由PhoneWindow類來實現的。
PhoneWindow將DecorView做爲整個應用窗口的根View,DecorView將屏幕分紅兩部分:TitleView和ContentView。
ContentView其實是一個FrameLayout,裏面容納的就是咱們在xml佈局文件中定義的佈局。
linux
爲何調用requestWindowFeature()方法必定要在setContentView()方法調用以前?
當程序在onCreate()方法中調用setContentView()方法後,ActivityManagerService會回調onResume()方法,此時系統纔會將整個DecorView添加到PhoneWindow中,並讓其顯示出來,從而完成界面的繪製。android
2.View的測量:MeasureSpec和測量模式
MeasureSpec是一個32位的int值,其中高2位位測量的模式,低30位位測量的大小 (使用位運算是爲了提升效率)
測量模式有三種:
(1)EXACTLY
:精確值模式,屬性設置爲精確數值或者match_parent
時,系統使用的是EXACTLY
模式
(2)AT_MOST
:最大值模式,屬性設置爲wrap_content
時,系統使用的是AT_MOST
模式
(3)UNSPECIFIED
:不指定大小測量模式,一般狀況下在繪製自定義View時纔會用到git
View類默認的onMeasure()方法只支持EXACTLY模式,因此若是在自定義View的時候不重寫onMeasure方法的話,就只能使用EXACTLY模式。自定義View能夠響應你指定的具體的寬高值或者是match_parent屬性,可是,若是要讓自定義View支持wrap_content屬性的話,那麼就必需要重寫onMeasure方法來指定wrap_content時view的大小。github
重寫onMeasure方法的最終工做就是把測量後的寬高值做爲參數設置給setMeasuredDimension方法。canvas
@Override |
3.View和ViewGroup的繪製
View的onDraw()方法包含一個參數Canvas
對象,使用這個Canvas對象就能夠進行繪圖了。
一般狀況下,Canvas對象的建立須要傳入參數Bitmap
,爲何呢?
這是由於傳進去的Bitmap與經過這個Bitmap建立的Canvas畫布是牢牢聯繫在一塊兒的,這個Bitmap用來存儲全部繪製在Canvas上的像素信息,當使用Bitmap建立Canvas以後,後面調用全部的Canvas.drawXXX方法都發生在這個Bitmap上。架構
ViewGroup一般不須要繪製,由於它自己沒有須要繪製的東西,若是不指定ViewGroup的背景顏色,那麼ViewGroup的onDraw方法都不會被調用。可是,ViewGroup會調用dispatchDraw方法來繪製其子view,其過程一樣是經過遍歷全部子view並調用子view的繪製方法來完成繪製工做的。ide
4.自定義View(ViewGroup)
三種自定義View的方式:
(1)對現有控件進行擴展
對現有控件進行擴展的代碼結構一般以下:佈局
@Override |
例如,書中對TextView進行擴展代碼節選post
private void initView() { |
(2)經過組合來實現新的控件
這種方式一般須要繼承一個合適的ViewGroup,再給它添加指定功能的控件,從而組合成新的複合控件。
[之前項目中使用這種方式建立應用內統一的信息界面,能夠是提示正在加載。也能夠是提示數據出錯了等]
例如,書中的TopBar例子:字體
public class TopBar extends RelativeLayout { |
(3)重寫View來實現全新的控件
建立自定義View的難點在於繪製控件和實現交互,一般須要繼承View類,並重寫onDraw、onMeasure等方法來實現繪製邏輯,同時經過重寫onTouchEvent等觸控事件方法來實現交互邏輯。
[這類自定義View是比較經常使用的,本身之前也寫過幾個簡單的例子,參見AnnotationView和ProgressView項目,或者參考以前的博文Android Text View with Custom Font,一個能夠自定義字體的TextView]
例如,書中的弧線展現圖例子
@Override |
5.事件攔截機制分析 [TODO:此內容特別重要,後期製做特別內容詳細分析]
1.使用ViewHolder模式提升效率
這種方式是必需要用的!下面的例子是一個常見的使用ViewHolder而且包含多個item type的Adapter例子:
public class ChatItemListViewAdapter extends BaseAdapter { |
2.listview的一些屬性設置
(1)設置分隔線android:divider=""@android:color/white"
android:dividerHeight="10dp"
android:divider="@null"
(設置分隔線透明)
(2)隱藏滾動條android:scrollbars="none"
(3)取消item的點擊效果android:listSelector="@android:color/transparent"
3.listview的一些方法設置
(1)設置listview顯示在第幾項listview.setSelection(n);
這個方法相似scrollTo瞬間完成移動,平滑移動可使用下面的方式listview.smoothScrollBy(distance, duration);
listview.smoothScrollByOffset(offset);
listview.smoothScrollToPosition(index);
(2)處理空listviewlistview.setEmptyView(View)
4.動態修改listview
在使用adapter的notifyDataSetChanged方法時,必須保證傳進adapter的數據list和發生數據變化的list是同一個對象,不然將沒法看到效果。
5.listview滑動監聽
監聽listview的滑動事件的方法有兩種:一個是OnTouchListener來實現監聽,另外一個是使用OnScrollListener來實現監聽。
例如,書中實現了一個監聽listview上下滑動事件操縱toolbar顯示和隱藏效果的例子:
public class ScrollHideListView extends Activity { |
監聽listview的OnScrollListener的通常實現
mListView.setOnScrollListener(new AbsListView.OnScrollListener() { |
得到當前可視的item的位置等信息的便捷方法
mListView.getLastVisiblePosition();//獲取可視區域最後一個item的id |
1.獲取座標值的各類方法
圖片來自Android中的座標系以及獲取座標的方法
2.實現滑動的基本思想
當觸摸view時,系統記下當前觸摸點座標;當手指移動時,系統記下移動後的觸摸點座標,從而獲取到相對於前一次座標點的偏移量,並經過偏移量來修改view的座標,這樣不斷重複,從而實現滑動過程。
3.經常使用的滑動實現方法
(1)修改view的left、top、right和bottom的值:調用layout
方法或者offsetLeftAndRight
等方法
絕對座標系下
// 絕對座標方式 |
視圖座標系下
// 視圖座標方式 |
(2)修改佈局參數LayoutParams:修改子view的getLayoutParams或者使用ViewGroup.MarginLayoutParams
子view的getLayoutParams獲得的LayoutParams須要和父ViewGroup的Layout類型一致,若是使用ViewGroup.MarginLayoutParams的話那就方便一些,不須要考慮父ViewGroup的具體類型。
@Override |
(3)使用scrollTo和scrollBy方法
scrollTo和scrollBy方法移動的是view的content,即讓view的內容移動。若是在ViewGroup中使用scrollTo或者scrollBy方法,那麼移動的是全部子view。但若是在view中使用,那麼移動的將是view的內容,例如TextView,content就是它的文本;ImageView,content就是它的drawable對象。
@Override |
(4)使用Scroller實現平滑效果
前面的滑動都不是平滑的,而Scroller是能夠實現平滑效果的,它的實現原理很簡單,其實就是不斷調用scrollTo和scrollBy方法來實現view的平滑移動,由於人眼的視覺暫留特性看起來就是平滑的。
使用Scroller主要有三個步驟:
1.初始化Scroller對象,通常在view初始化的時候同時初始化scroller;
2.重寫view的computeScroll
方法,computeScroll
方法是不會自動調用的,只能經過invalidate->draw->computeScroll
來間接調用,實現循環獲取scrollX和scrollY的目的,當移動過程結束以後,Scroller.computeScrollOffset
方法會返回false,從而中斷循環;
3.調用Scroller.startScroll
方法,將起始位置、偏移量以及移動時間(可選)做爲參數傳遞給startScroll
方法。
例如,書中給出的例子,子view在被拖動以後會自動平滑移動到原來的位置
private void ininView(Context context) { |
(5)屬性動畫 [後面有專門對Android動畫分析的部分,此處略過]
(6)使用ViewDragHelper
ViewDragHelper類使用較少,它是support庫中DrawerLayout和SlidingPaneLayout內部實現的重要類!
以前讀過相似側邊欄菜單的實現代碼(SlidingMenu),我的感受ViewDragHelper實際上是更高層次的封裝,將這類效果所需的接口暴露出來以簡化相似的開發工做,書中給了一個例子,介紹了ViewDragHelper的使用,實現了相似側邊欄菜單的效果
public class DragViewGroup extends FrameLayout { |