1.Android控制項架構下圖是UI界面架構圖,每個Activity都有一個Window對象,通常是由PhoneWindow類來實現的。PhoneWindow將DecorView作為整個應用視窗的根View,DecorView將屏幕分成兩部分:TitleView和ContentView。Conten ...
1.Android控制項架構
下圖是UI界面架構圖,每個Activity都有一個Window對象,通常是由PhoneWindow類來實現的。
PhoneWindow將DecorView作為整個應用視窗的根View,DecorView將屏幕分成兩部分:TitleView和ContentView。
ContentView實際上是一個FrameLayout,裡面容納的就是我們在xml佈局文件中定義的佈局。
為什麼調用requestWindowFeature()方法一定要在setContentView()方法調用之前?
當程式在onCreate()方法中調用setContentView()方法後,ActivityManagerService會回調onResume()方法,此時系統才會將整個DecorView添加到PhoneWindow中,並讓其顯示出來,從而完成界面的繪製。
2.View的測量:MeasureSpec和測量模式
MeasureSpec是一個32位的int值,其中高2位是測量的模式,低30位是測量的大小 (使用位運算是為了提高效率和節省空間)
測量模式有三種:
(1)EXACTLY
:精確值模式,屬性設置為精確數值或者match_parent
時,系統使用的是EXACTLY
模式
(2)AT_MOST
:最大值模式,屬性設置為wrap_content
時,系統使用的是AT_MOST
模式
(3)UNSPECIFIED
:不指定大小測量模式,通常情況下在繪製自定義View時才會用到
View類預設的onMeasure()方法只支持EXACTLY模式,所以如果在自定義View的時候不重寫onMeasure方法的話,就只能使用EXACTLY模式。自定義View可以響應你指定的具體的寬高值或者是match_parent屬性,但是,如果要讓自定義View支持wrap_content屬性的話,那麼就必須要重寫onMeasure方法來指定wrap_content時view的大小。
重寫onMeasure方法的最終工作就是把測量後的寬高值作為參數設置給setMeasuredDimension方法。
@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的繪製方法來完成繪製工作的。
4.自定義View(ViewGroup)
三種自定義View的方式:
(1)對現有控制項進行擴展
對現有控制項進行擴展的代碼結構通常如下:
@Override
|
例如,書中對TextView進行擴展代碼節選
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.事件攔截機制分析 [後面有專門對Android事件攔截機制分析的部分,此處略過]
第四章 ListView使用技巧
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
|
第五章 Android Scroll分析
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 { |