天天看點

Android中自定義樣式與View的構造函數中的第三個參數defStyle的意義零、序 一、自定義Style 二、在xml中為相應的屬性聲明屬性值 三、在運作時擷取屬性值  結論

  系統自帶的view可以在xml中配置屬性,對于寫的好的custom view同樣可以在xml中配置屬性,為了使自定義的view的屬性可以在xml中配置,需要以下4個步驟:

通過<declare-styleable>為自定義view添加屬性

在xml中為相應的屬性聲明屬性值

在運作時(一般為構造函數)擷取屬性值

将擷取到的屬性值應用到view

  怎麼将擷取到的屬性值應用到view就不用說了,自己定義的屬性什麼用處自己肯定是清楚的,是以接下來看一下前三點。

  通過<declare-styleable>元素聲明custom view需要的屬性即可,下面是一個例子,檔案是res/values/attrs.xml

  在上述xml中,我們聲明了customize與customizesyle,customize包含了attr_one、attr_two、attr_three與attr_four四個attribute,customizestyle也是一個attribute,但是卻沒有聲明在declare-styleable中。

  定義在declare-styleable中與直接用attr定義沒有實質的不同,上述xml中,無論attr_one - attr_four是否聲明在declare-styleable中,系統都會為我們在r.attr中生成5個attribute

  不同的是,如果聲明在declare-styleable中,系統還會為我們在r.styleable中生成相關的屬性。

   如上所示,r.styleable.customize是一個int[],而裡面的元素的值正好和r.attr.attr_one - r.attr.attr_four一一對應,而r.styleable.customize_attr_one等4個值就是r.attr.attr_one-r.attr.attr_four在r.styleable.customize數組中的索引。這個數組和索引在第三步運作時獲得屬性值時會用到,将attr分組聲明在declare-styleabe中的作用就是系統會自動為我們生成這些東西,如果不聲明在declare-styleable中,我們完全可以在需要的時候自己建構這個數組,由于數組是自己建構的,每個屬性的下标索引也就很清楚了,隻是比較麻煩。以上一家之言,不過從使用及實驗難上看确實是這樣的。

        我們知道,在xml中為屬性指派有幾種不同的方式

直接在layout中使用屬性

設定style并在style中設定屬性

application和activity可以指定theme,可以在theme中指定在目前application或activity中屬性的預設值

        下面就分别看一下這三種方式

        在xml layout中使用自定義屬性和使用系統屬性差不多,不過屬性所屬的namespace不同,比如像下面這樣。

  像layout_width等屬性屬于android的namespace,自定義的屬性屬于目前程式的namespace,隻需像聲明android的namespace一樣聲明目前程式的namespace就好,隻需要把上面紅色部分的android換成目前程式的包名。應用屬性的時候也需要注意屬性的namespace。

  看上面xml中綠色的那一行,我們為customtextview聲明了一個style:throughstyle,這個style很簡單,隻是指定了兩個屬性的值

  注意,在style中我們聲明了attr_one的值,同時在xml中也直接向attr_one賦了值,最終用哪一個有個優先級的問題,後面在第三節:在運作時擷取屬性值中再說,接下來看下第三種方式。

   在上述xml中,我們在apptheme中為attr_one、attr_two與attr_three指定了值,但是同時也為customizestyle指定了一個reference,而在這個reference中為attr_one、attr_two與attr_three指定了值,customizestyle就是我們在attrs.xml中定義的一個屬性。在theme中為屬性設定值的方式有兩這種,直接在theme中定義或通過另一個attribute引用一個style,這兩種方式還是有差別的,在下面的擷取屬性值一節中可以看到。

        在styles.xml中,我們同時定義一個defaultcustomizestyle的style,它的作用等下可以看到。

  先看下customtextview的代碼

   主要代碼都在第三個函數中,這裡也沒做什麼,隻是通過context的obtainstyledattributes獲得了前面定義的4個屬性的值并列印了出來。

  如果在code中執行個體化一個view會調用第一個構造函數,如果在xml中定義會調用第二個構造函數,而第三個函數系統是不調用的,要由view(我們自定義的或系統預定義的view,如此處的customtextview和button)顯式調用,比如在這裡我們在第二個構造函數中調用了第三個構造函數,并将r.attr.customizestyle傳給了第三個參數。

  第三個參數的意義就如同它的名字所說的,是預設的style,隻是這裡沒有說清楚,這裡的預設的style是指它在目前application或activity所用的theme中的預設style,以系統中的button為例說明。

   在code中執行個體化view會調用第一個構造函數,在xml中定義會調用第二個構造函數,在button的實作中都調用了第三個構造函數,并且defstyle的值是com.android.internal.r.attr.buttonstyle。buttonstyle是系統中定義的一個attribute,系統預設有一個theme,比如4.0中是theme.holo

  上面是系統預設的theme,為buttonstyle指定了一個style

   這個style定義了系統中button的預設屬性,如background等。

  從文檔中第三個構造函數的說明中也可以看到,這個構造函數的作用是view的子類提供這個類的基礎樣式

view(context context, attributeset attrs, int defstyleattr) perform inflation from xml and apply a class-specific base style.

  上面說的都比較抽象,還是直接看執行個體代碼來的清楚明白,實驗用的代碼上面全都貼完了,這裡直接看結果,但在這之前要先看一個函數:obtainstyledatributes。

  我們要擷取的屬性值都是通過這個函數傳回的typedarray獲得的,這是官方說明:obtainstyledattributes,以下是函數原型:

  4個參數的意思分别是:

    set:屬性值的集合

    attrs:我們要擷取的屬性的資源id的一個數組,如同contextprovider中請求資料庫時的projection數組,就是從一堆屬性中我們希望查詢什麼屬性的值

    defstyleattr:這個是目前theme中的一個attribute,是指向style的一個引用,當在layout xml中和style中都沒有為view指定屬性時,會從theme中這個attribute指向的style中查找相應的屬性值,這就是defstyle的意思,如果沒有指定屬性值,就用這個值,是以是預設值,但這個attribute要在theme中指定,且是指向一個style的引用,如果這個參數傳入0表示不向theme中搜尋預設值

    defstyleres:這個也是指向一個style的資源id,但是僅在defstyleattr為0或defstyleattr不為0但theme中沒有為defstyleattr屬性指派時起作用

  連結中對這個函數說明勉強過得去,這裡簡要概括一下。對于一個屬性可以在多個地方指定它的值,如xml直接定義,style,theme,而這些位置定義的值有一個優先級,按優先級從高到低依次是:

直接在xml中定義>style定義>由defstyleattr和defstyleres指定的預設值>直接在theme中指定的值

  看這個關系式就比較明白了,defstyleattr和defstyleres在前面的參數說明中已經說了,“直接在theme中指定的值”的意思在下面的示代碼中可以看到。

  相關的代碼都前面都已經貼上了,不過為了友善檢視,這裡再把相關的xml一起貼一遍

   在code中是這樣擷取屬性的

  customizestyle是定義的一個attribute,defaultcustomizestyle是定義的一個style。

  從代碼中可以看到,r.attr.customizestyle就是前面提到的defstyleattr,r.style.defaultcustomizestyle就是defstyleres。

  在styles.xml中我們為app定義了一個theme:apptheme,在這個theme中直接為attr_one、attr_two、attr_three定義了屬性值,這個就是前面關系式中說的直接在theme中指定的值。

  在apptheme中同時定義了customizestyle,引用了一個style,在customtextview的構造函數中分别列印了attr_one、attr_two、attr_three、attr_four的值,是以下面我們就可以通過log輸出驗證一下前面的關系式,如果一個attribute在多個位置都定義了值,究竟哪一個起作用。

  

Android中自定義樣式與View的構造函數中的第三個參數defStyle的意義零、序 一、自定義Style 二、在xml中為相應的屬性聲明屬性值 三、在運作時擷取屬性值  結論

  如上圖所示:

attr_one同時在xml、style、defstyleattr、defstyleres與theme中直接定義了值,但起作用的是在xml中的定義

attr_two同時在style、defstyleattr、defstyleres與theme中直接定義了值,但起用的是在style中的定義

attr_three同時在defstyleattr、defstyleres與theme中直接定義了值,但起作用的是defstyleattr

attr_four在defstyleres中定義了,但結果仍是0。

  這可以說明:

1. 直接在xml中定義>style定義>由defstyleattr定義的值>defstyleres指定的預設值、直接在theme中指定的值 2. defstyleattr(即defstyle)不為0且在目前theme中可以找到這個attribute的定義時,defstyleres不起作用,是以attr_four雖然在defstyleres(defaultcustomizestyle)中定義了,但取到的值仍為null。

  為了看一下defstyleres的作用,1. 我們在apptheme中加上attr_four的定義,2. 向obtainstyledattributes的第三個參數傳入0,或者移除apptheme中customizestyle的定義,結果是一樣的

   由于defstyleattr為0,或者defstyleattr不為0但是我們沒有為這個屬性指派,是以defstyleres起作用,當一個屬性沒有在xml和style中指派時,系統會在defstyleres(此處為defaultcustomizestyle)查找屬性的值。

Android中自定義樣式與View的構造函數中的第三個參數defStyle的意義零、序 一、自定義Style 二、在xml中為相應的屬性聲明屬性值 三、在運作時擷取屬性值  結論

  我們看到attr_three的值來自defstyleres,而attr_four的值來自theme中的直接定義(defaultcustomizestyle定義了attr_one、atrr_two、attr_three) ,這就說明

1. defstyleatrtr即defstyle為0或theme中沒有定義defstyle時defstyleres才起作用 2. defstyleres>在theme中直接定義

   從前面的說明可以得到以下結論:

要為自定義view自定義屬性,可以通過declare-styleable聲明需要的屬性

為了在theme設定view的預設樣式,可以同時定義一個format為reference的屬性att_a,在定義theme時為這個attribute指定一個style,并且在view的第二個構造函數中将r.attr.attr_a 作為第三個參數調用第三個構造函數

可以定義一個style作為theme中沒有定義attr_a時view屬性的預設值。

可以在theme中直接為屬性指派,但優先級最低

當defstyleattr(即view的構造函數的第三個參數)不為0且在theme中有為這個attr指派時,defstyleres(通過obtainstyledattributes的第四個參數指定)不起作用

屬性值定義的優先級:xml>style>theme中的預設sytle>預設style(通過obtainstyledattributes的第四個參數指定)>在theme中直接指定屬性值

  代碼下載下傳:customstyle.zip