一偶垮、先來看看Android各控件的繼承關系(搞清控件怎么來的包斑,我們又怎么樣去借鑒模仿重新定義一個view)摘昌。
下面是安卓控件的繼承關系類圖,其中紅色為常用控件,Android中所有控件都繼承自android.view.View沥匈,其中android.view.ViewGroup是View的一個重要子類蔗喂,絕大部分的布局都繼承自ViewGroup。
雖然看上去錯綜復雜高帖,理清思路條線還是比較明晰缰儿,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)的源碼鸣驱,如下圖:
上面這圖確實是實現了構造函數得作用——初始化數據泛鸟;
接著看View(Context, AttributeSet, defStyleAttr)的源碼,如下圖:
上面這圖的構造函數里調用了View(Context, AttributeSet, defStyleAttr)這個構造函數踊东;
上面這圖的構造函數里調用了View(Context, AttributeSet, defStyleAttr, defStyleRes)構造函數北滥;
上面這圖的構造函數里調用了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ā)者平臺是這么解釋這個類的:
大體意思是: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,如下:
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"
這兩個屬性值就是通過下面的代碼獲取的
是不是對于自定義的新屬性取值大概有了個明白涡匀,其實對于這個自定義新屬性的取值還有好多要講的盯腌,繼續(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 是這么解釋這個函數的:
上面主要信息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 設置相關的屬性睁枕,理論與實踐相結合官边,人這個動物思維+視覺 才會把抽象的事物具體展現在大腦中,印象更深刻外遇,理解更透徹注簿,
上面style中是不是包含我們上面自定義view的屬性app:ArcColor="@color/colorPrimaryDark"
app:TriangleColor="@color/colorPrimary" 然后我們自定義view的構造函數里也要改一改,改成下圖:
上面這函數不管你在布局xml里面引用的app:ArcColor="@color/xxx" app:TriangleColor="@color/xxx"屬性采用什么顏色值跳仿,最終顯示的是style里設置的屬性值的顏色诡渴。
obtainStyledAttributes (int resid, int[] attrs)
Google Developer 是這么解釋這個函數的:
上面主要信息Return a TypedArray holding the values defined by the style resource resid which are listed in attrs
意思跟上面的一個參數差不多,只不過區(qū)別在于可以不用在android:them指定的style里加自定義屬性菲语,在另外的style里加自定義屬性妄辩。
作用效果跟一個參數的作用差不多,不累贅山上。
obtainAttributes (AttributeSet set, int[] attrs)
Google Developer 是這么解釋這個函數的:
上面主要信息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干花,構造函數修改如下:
這樣的自定義view展示出來的效果根據你在布局xml里定義新屬性值而定的,但是這樣會有一個問題楞黄,那就是你引用自定義view的布局xml里必須把你新定義屬性全部寫在xml文件中池凄,不然就會有報錯出現,具體表現在如下圖代碼谅辣,他沒有相應的屬性值可取修赞,自然而然會報錯。
obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
Google Developer 是這么解釋這個函數的:
上面的 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 則表示不進行默認設置贯底。
對于第三個參數要重點講下:
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
它的調用邏輯是這樣的:
這篇的字數有點多,有點難駕馭乓搬,大家看的也比較頭疼思犁,先到這里,有空再繼續(xù)进肯。