自定義View_1

一偶垮、先來看看Android各控件的繼承關系(搞清控件怎么來的包斑,我們又怎么樣去借鑒模仿重新定義一個view)摘昌。

下面是安卓控件的繼承關系類圖,其中紅色為常用控件,Android中所有控件都繼承自android.view.View沥匈,其中android.view.ViewGroup是View的一個重要子類蔗喂,絕大部分的布局都繼承自ViewGroup。


20140404100148953(1).jpg

雖然看上去錯綜復雜高帖,理清思路條線還是比較明晰缰儿,view相當于java的object,是所有控件的子類散址,所以你如果想創(chuàng)建一個全新的控件先要繼承view乖阵,這是第一步。

二预麸、繼承了view必然要寫構造函數瞪浸,這是重點
1、先看看view的構造函數吏祸,它有四個
View(Context)
View(Context, AttributeSet)
View(Context, AttributeSet, defStyleAttr)
View(Context, AttributeSet, defStyleAttr, defStyleRes)
一般我們定義好了CustomView類对蒲,在xml引用時是調用View(Context, AttributeSet),我剛開始的時候看見Attributeset時候也是懵的贡翘,這個參數可復雜了蹈矮,稍后講;
先繼續(xù)看看view(context)的源碼鸣驱,如下圖:


粘貼圖片.png

上面這圖確實是實現了構造函數得作用——初始化數據泛鸟;

接著看View(Context, AttributeSet, defStyleAttr)的源碼,如下圖:


粘貼圖片(2).png

上面這圖的構造函數里調用了View(Context, AttributeSet, defStyleAttr)這個構造函數踊东;

粘貼圖片(3).png

上面這圖的構造函數里調用了View(Context, AttributeSet, defStyleAttr, defStyleRes)構造函數北滥;

粘貼圖片(1).png

上面這圖的構造函數里調用了View(context)構造函數,這樣便于不重復寫代碼(數據初始化的代碼)闸翅;
我們大概清楚了自定義view的構造方法碑韵,現在具體說說構造函數里的參數:
context這個自然不用多說,Android最常用的缎脾,上下文本;
AttributeSet占卧,這個參數是關鍵遗菠,有關于你自定義view的新屬性值在你在xml引用的時候都是通過AttributeSet獲取的,從源碼里我們看到 final TypedArray a = context.obtainStyledAttributes( attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);這段源碼华蜒,TypedArray 這個又是什么呢辙纬?

Google 開發(fā)者平臺是這么解釋這個類的:


20160326141058890.png

大體意思是:TypedArray 是一個數組容器,在這個容器中裝由 obtainStyledAttributes(AttributeSet, int[], int, int) 或者 obtainAttributes(AttributeSet, int[]) 函數獲取到的屬性值叭喜。用完之后記得調用 recycle() 函數回收資源贺拣。索引值用來獲取 Attributes 對應的屬性值(這個 Attributes 將會被傳入 obtainStyledAttributes() 函數)。
具體我們來實際操作下 ,我們先自定義一個view譬涡,先不看自定義view的功能闪幽,自定義view的類如下如:

  public class VideoLoadingView extends View { 
  public VideoLoadingView(Context context) {
      super(context);
      init(context);
  }
  
public VideoLoadingView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
}

public VideoLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    TypedArray attributes = context.getTheme()
            .obtainStyledAttributes(attrs, R.styleable.VideoLoadingView, defStyleAttr, 0);
    initAttrs(attributes);
    init(context);
}

private void initAttrs(TypedArray attributes) {
    try {
        mArcColor = attributes.getColor(R.styleable.VideoLoadingView_ArcColor, Color.GREEN);
        mTriangleColor = attributes.getColor(R.styleable.VideoLoadingView_TriangleColor, Color.GREEN);
    } finally {
        attributes.recycle();
    }
}

private void init(Context context) {
    mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mArcPaint.setColor(mArcColor);
    mArcPaint.setStyle(Paint.Style.STROKE);

    mTrianglePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    mTrianglePaint.setColor(mTriangleColor);
    mTrianglePaint.setStrokeWidth(2);
    mTrianglePaint.setStyle(Paint.Style.FILL);         
      } 
 }

1.在資源文件 values 下創(chuàng)建文件 attrs.xml,如下:

粘貼圖片(4).png

2.在xml里引用這個自定義的view

?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   xmlns:app="http://schemas.android.com/apk/res-auto"  
   tools:context="com.goldmantis.wb.viewdemo.MainActivity">

<com.goldmantis.wb.viewdemo.VideoLoadingView
    android:id="@+id/videoLoadingView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/stop_btn"
    android:layout_centerHorizontal="true"
    android:layout_marginBottom="90dp"
    app:ArcColor="@color/colorPrimaryDark"
    app:TriangleColor="@color/colorPrimary" />
  </RelativeLayout>

上面的 app:ArcColor="@color/colorPrimaryDark"
app:TriangleColor="@color/colorPrimary"
這兩個屬性值就是通過下面的代碼獲取的


粘貼圖片(5).png

是不是對于自定義的新屬性取值大概有了個明白涡匀,其實對于這個自定義新屬性的取值還有好多要講的盯腌,繼續(xù)深入TypedArray ,獲取 TypedArray 對象 的函數一共四個:

1.public TypedArray obtainStyledAttributes (int[] attrs)陨瘩;

2.public TypedArray obtainStyledAttributes (int resid, int[] attrs)腕够;

3.public TypedArray obtainAttributes (AttributeSet set, int[] attrs);

4.public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)舌劳。

講解之前帚湘,需要說明一點:函數 1、2甚淡、4 都是 Resources.Theme 的函數大诸,而 3 是 Resources 的函數。
obtainStyledAttributes (int[] attrs)
Google Developer 是這么解釋這個函數的:

20160327145022999.png

上面主要信息Return a TypedArray holding the values defined by Theme which are listed in attrs
它的大意是:返回一個與 attrs 中列舉出的屬性相關的數組材诽,數組里面的值由 Theme 指定底挫。從上面的概述中,我們可以知道:這個 Theme 就是關鍵脸侥。找各種資料發(fā)現attrs 對應的屬性值必須定義在 Application 中 Android:theme 對應的 style 下建邓,換句話說在定義應用主題時我們要在對應主題下設置attrs屬性,
我們在為 Application 設置主題的同時需要在對應的主題下為 attrs 設置相關的屬性睁枕,理論與實踐相結合官边,人這個動物思維+視覺 才會把抽象的事物具體展現在大腦中,印象更深刻外遇,理解更透徹注簿,

粘貼圖片(7).png
粘貼圖片(8).png

上面style中是不是包含我們上面自定義view的屬性app:ArcColor="@color/colorPrimaryDark"
app:TriangleColor="@color/colorPrimary" 然后我們自定義view的構造函數里也要改一改,改成下圖:

粘貼圖片(9).png

上面這函數不管你在布局xml里面引用的app:ArcColor="@color/xxx" app:TriangleColor="@color/xxx"屬性采用什么顏色值跳仿,最終顯示的是style里設置的屬性值的顏色诡渴。

obtainStyledAttributes (int resid, int[] attrs)
Google Developer 是這么解釋這個函數的:

20160327160456295.png

上面主要信息Return a TypedArray holding the values defined by the style resource resid which are listed in attrs
意思跟上面的一個參數差不多,只不過區(qū)別在于可以不用在android:them指定的style里加自定義屬性菲语,在另外的style里加自定義屬性妄辩。

粘貼圖片(10).png

粘貼圖片(11).png

作用效果跟一個參數的作用差不多,不累贅山上。

obtainAttributes (AttributeSet set, int[] attrs)
Google Developer 是這么解釋這個函數的:

20160327163828401.png

上面主要信息Retrieve a set of basic attribute values from an AttributeSet, not performing styling of them using a theme and/or style resources.這句話的大意是:從 AttributeSet 中獲取 attrs 對應的屬性值眼耀,不為這些屬性值設置樣式。
同樣構造函數要改下佩憾,style里面跟沒有自定義view一樣哮伟,在布局文件里引用自定義view干花,構造函數修改如下:

粘貼圖片(12).png

這樣的自定義view展示出來的效果根據你在布局xml里定義新屬性值而定的,但是這樣會有一個問題楞黄,那就是你引用自定義view的布局xml里必須把你新定義屬性全部寫在xml文件中池凄,不然就會有報錯出現,具體表現在如下圖代碼谅辣,他沒有相應的屬性值可取修赞,自然而然會報錯。


粘貼圖片(13).png

obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Google Developer 是這么解釋這個函數的:

20160327170932196.png

上面的 Google 開發(fā)者文檔的大意是:

返回一個與 attrs 屬性相對應的數組桑阶。另外柏副,如果在 AttributeSet 中為 attrs 指定了樣式屬性,那么這個樣式屬    性就會應用在這些屬性上蚣录。

attribute 最終由下面四個因素決定:

在 AttributeSet 中定義的屬性(Any attribute values in the given AttributeSet)割择;

AttributeSet 指定的樣式資源文件(The style resource specified in the AttributeSet (named “style”));

由 defStyleAttr 和 defStyleRes 指定的樣式資源文件(The default style specified by defStyleAttr and defStyleRes)萎河;

主題中的默認值(The base values in this theme)荔泳。

上面四種元素的優(yōu)先級是從上到下排序的,也就是說:如果在 AttributeSet 中定義了某個屬性的值虐杯,那么無論后面的樣式屬性如何定義玛歌,它的值都不會改變。

接下來我們分別解釋下擎椰,函數中各參數的含義:

AttributeSet set :XML 中定義的屬性值支子,可能為 null;

int[] attrs :目標屬性值达舒;

int defStyleAttr :在當前主題中有一個引用指向樣式文件值朋,這個樣式文件將 TypedArray 設置默認值。如果此參數為 0 則表示不進行默認值設置巩搏。

int defStyleRes :默認的樣式資源文件昨登,只有當 defStyleAttr 為 0 或者無法在對應的主題下找到資源文件時才起作用。如果此參數為 0 則表示不進行默認設置贯底。
粘貼圖片(14).png
粘貼圖片(17).png
粘貼圖片(18).png

對于第三個參數要重點講下:
defStyleAttr丰辣,這個參數表示的是一個<style>中某個屬性的ID,當Android在AttributeSet和style屬性所定義的style資源中都沒有找到XML屬性值時禽捆,就會嘗試查找當前theme(theme其實就是一個<style>資源)中屬性為defStyleAttr的值笙什,如果其值是一個style資源,那么Android就會去該資源中再去查找XML屬性值睦擂。在AppTheme中,我們設置了viewStyle這個屬性的值<item name="viewStyle">@style/AppTheme2</item>
viewStyle這個屬性是在values/attrs.xml中定義了<attr name="customstyle" format="reference"/>
viewStyle被定義為一個reference格式杖玲,即其值指向一個資源類型顿仇,我們在AppTheme中將其賦值為@style/AppTheme2,即在AppTheme中,viewStyle的就是AppTheme2臼闻,其指向了一個style資源鸿吆。
如上圖所示運行后顯示的結果是根據布局xml文件里設置的新屬性值app:ArcColor="@color/colorPrimaryDark"
app:TriangleColor="@color/colorPrimary"來顯示。
但如果TypedArray typedArray = theme.obtainStyledAttributes(null, R.styleable.MyView, R.attr.viewStyle, R.style.AppTheme1);則顯示的結果會根據R.attr.viewStyle里的屬性值展示述呐。
但如果TypedArray typedArray = theme.obtainStyledAttributes(null, R.styleable.MyView, 0, R.style.AppTheme1);則顯示的結果會根據R.style.AppTheme1里的屬性值展示惩淳。

有沒有發(fā)現它們的優(yōu)先級是從高到底依次排列的:
AttributeSet > defStyleAttr > defStyleRes
它的調用邏輯是這樣的:

20160327225112499.png

這篇的字數有點多,有點難駕馭乓搬,大家看的也比較頭疼思犁,先到這里,有空再繼續(xù)进肯。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末激蹲,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子江掩,更是在濱河造成了極大的恐慌学辱,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件环形,死亡現場離奇詭異策泣,居然都是意外死亡,警方通過查閱死者的電腦和手機抬吟,發(fā)現死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門萨咕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人拗军,你說我怎么就攤上這事任洞。” “怎么了发侵?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵交掏,是天一觀的道長。 經常有香客問我刃鳄,道長盅弛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任叔锐,我火速辦了婚禮挪鹏,結果婚禮上,老公的妹妹穿的比我還像新娘愉烙。我一直安慰自己讨盒,他們只是感情好,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布步责。 她就那樣靜靜地躺著返顺,像睡著了一般禀苦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上遂鹊,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天振乏,我揣著相機與錄音,去河邊找鬼秉扑。 笑死慧邮,一個胖子當著我的面吹牛,可吹牛的內容都是我干的舟陆。 我是一名探鬼主播误澳,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼吨娜!你這毒婦竟也來了脓匿?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宦赠,失蹤者是張志新(化名)和其女友劉穎陪毡,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體勾扭,經...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡毡琉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了妙色。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桅滋。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖身辨,靈堂內的尸體忽然破棺而出丐谋,到底是詐尸還是另有隱情,我是刑警寧澤煌珊,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布号俐,位于F島的核電站,受9級特大地震影響定庵,放射性物質發(fā)生泄漏吏饿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一蔬浙、第九天 我趴在偏房一處隱蔽的房頂上張望猪落。 院中可真熱鬧,春花似錦畴博、人聲如沸笨忌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽官疲。三九已至杂曲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間袁余,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工咱揍, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留颖榜,地道東北人。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓煤裙,卻偏偏與公主長得像掩完,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子硼砰,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內容