更多內容:
(一)自定義View的分類點擊打開鏈接
(二)自定義View的構造方法及自定義屬性
(三)自定義View的常用方法(測量、繪製、位置)
(四)自定義View的具體實現
(五)事件分發機制
一、自定義View的構造方法
無論是繼承自View還是ViewGroup的自定義控件,編譯器都會提示我們
Implicit super constructor ViewGroup() is undefined for default constructor. Must define an explicit constructor
意思是基類沒有無參的構造方法,必須定義一個帶參的構造方法。
我們可以重載下面三個構造方法(API21已經有四個參數的構造方法)。
public MyView(Context context) {
super(context);
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
首先解決兩個問題
一、我們在構造方法中具體做什麼一般做一些初始化工作,獲取自定義屬性、獲取子View等等,以後的demo中會具體講解
二、如何選擇構造方法
關於三個方法的選擇,源碼的註釋裏已經寫得非常清楚了
/**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
*/
public View(Context context) {
……
}
帶一個參數的構造方法:
Simple constructor to use when creating a view from code
當我們在代碼裏創建一個view時,使用此方法
/**
* Constructor that is called when inflating a view from XML. This is called
* when a view is being constructed from an XML file, supplying attributes
* that were specified in the XML file. This version uses a default style of
* 0, so the only attribute values applied are those in the Context's Theme
* and the given AttributeSet.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @see #View(Context, AttributeSet, int)
*/
public View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
帶兩個參數的構造方法:
Constructor that is called when inflating a view from XML. This is called when a view is being constructed from an XML file, supplying attributes that were specified in the XML file. This version uses a default style
of 0, so the only attribute values applied are those in the Context's Theme and the given AttributeSet.
當我們在xml文件中創建一個控件時,使用此方法。這個方法傳遞了控件在xml文件中定義的所有屬性(包括自定義屬性),即attars。它所使用的的默認樣式是0,這意味Context和Theme給定的attributeset是唯一用到的屬性值。
/**
* Perform inflation from XML and apply a class-specific base style. This
* constructor of View allows subclasses to use their own base style when
* they are inflating. For example, a Button class's constructor would call
* this version of the super class constructor and supply
* <code>R.attr.buttonStyle</code> for <var>defStyle</var>; this allows
* the theme's button style to modify all of the base view attributes (in
* particular its background) as well as the Button class's attributes.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource to apply to this view. If 0, no
* default style will be applied.
* @see #View(Context, AttributeSet)
*/
public View(Context context, AttributeSet attrs, int defStyleAttr) {
this(context);
……
}
帶三個參數的構造方法:
Perform inflation from XML and apply a class-specific base style. This constructor of View allows subclasses to use their own base style when
they are inflating. For example, a Button class's constructor would call this version of the super class constructor and supply
R.attr.buttonStyle for defStyle ; this allows the theme's button style to modify all of the base view attributes (in particular its background) as well as the Button class's attributes.
在xml創建一個控件並且使用某個特定類型的基礎樣式時,使用此方法。
這個方法允許子類在加載時使用它們自己的基礎樣式。比如說,一個按鈕類重載此構造方法時,將要調用基類的同類型構造方法,使用R.attr.buttonStyle作爲默認樣式。這使得theme的按鈕樣式修改所有基礎控件屬性(特別是其背景)以及按鈕類的屬性。
這裏我們再看一下defStyleAttr參數的解釋:
defStyleAttr: An attribute in the current theme that contains a reference to a style resource to apply to this view. If 0, no default style will be applied.
當前theme中引用自style的屬性,如果是0的話,不使用任何style。
補充一句:系統不會主動的調用這個方法,需要我們顯式的調用,比如:
public MyView(Context context, AttributeSet attrs) {
//顯式調用三個參數的構造方法 ,將R.attr.buttonStyle作爲控件的基礎樣式
this(context, attrs, R.attr.buttonStyle);
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
當我們顯式的調用這個方法時,我們的自定義控件將使用這個defStyle作爲基礎的樣式,但是這個defStyle所定義的屬性是可以被覆蓋的。比如我們在自定義控件時在xml文件中重寫了defStyle的某些屬性,這些屬性就被覆蓋了。
這是因爲同時定義一個屬性時,是有優先級的:xml>style>defStyle>theme
在實際運用中,爲了方便,我們一般會按如下方式同時重載這三個構造方法
public MyView(Context context) {
this(context, null);// 調用兩個參數的構造方法
}
public MyView(Context context, AttributeSet attrs) {
this(context, attrs, 0);//調用三個參數的構造方法
}
public MyView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);//調用基類構造方法
}
二、自定義View的自定義屬性
對於一個自定義控件,它擁有基類所有的屬性,同時也可以定義自己的屬性,這就是自定義屬性。
舉個最簡單的例子,我們現在自義定一個圓形頭像,首先讓它繼承ImageView,這樣就擁有了背景這個屬性。但是作爲一個圓形頭像它是需要半徑這個屬性的,這就需要我們去自定義了。
①在res文件的values目錄下,創建一個xml文件,我們將在這個文件中編寫自義定屬性。我創建的是attrs.xml ,名字不是固定的可以自己取。
② <declare-styleable name="控件名">
<attr name="屬性名" format="屬性類型" />
……
</declare-styleable>
<resources>
<declare-styleable name="CircleIcon">
<attr name="circle_radius" format="float" />
</declare-styleable>
</resources>
format選項說明及使用,這篇博客歸納的非常完整Android自定義屬性時format選項參數說明及用法
③我們怎麼得到已經寫好的自定義屬性呢,還記得剛剛講過的兩個參數的構造方法嗎,它的參數 AttributeSet attrs傳入了自定義控件的所有屬性,包括xml文件裏申明的,也包括自定義的,因此我們在兩個參數的構造方法中獲取自定義屬性
public CircleView(Context context, AttributeSet attrs) {
this(context, attrs, defStyle);//調用基類構造方法
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CircleIcon);// <declare-styleable name="CircleIcon">
float radius = a.getFloat(R.styleable.CircleIcon_circle_radius,//控件名_屬性名
1);
a.recycle();//一定要寫
}
當我們獲取了自定義屬性時,就可以使用它們來構建控件了,具體的內容將在後面的文章詳細講解