View有四個構造函數以下php
public View(Context context) {}
public View(Context context, AttributeSet attrs) {}
public View(Context context, AttributeSet attrs, int defStyleAttr) {}
public View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
複製代碼
本文先以TextView
爲例理論講解這四個構造函數如何使用,再用一個自定義View來進行實戰。java
一個參數的構造函數是在代碼中建立的,例如把一個TextView
添加到Activity
佈局中android
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 建立TextView對象,並設置屬性
TextView textView = new TextView(this);
textView.setText(R.string.app_name);
textView.setTextSize(30);
// 把TextView對象添加到佈局中
setContentView(textView);
}
}
複製代碼
從這個例子中能夠發現,使用一個參數的構造函數建立對象後,須要手動調用設置屬性的方法。canvas
如今假設在一個佈局中聲明瞭一個TextView
控件,代碼以下app
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" android:textSize="24sp" />
</RelativeLayout>
複製代碼
系統在解析這個XML佈局的時候,會使用TextView
兩個參數的構造函數,代碼以下ide
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
複製代碼
第二個參數AttributeSet attrs
就表明了在XML中爲TextView
聲明的屬性集,咱們能夠利用它解析出聲明的屬性值,例如android:text
和android:textSize
。函數
咱們知道,能夠經過Theme
全局控制控件的樣式,其中的原理就是使用三個參數的構造函數佈局
三個參數構造函數的使用方式有點特別,通常是二個參數的構造函數中傳入一個Theme
中的屬性this
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
複製代碼
能夠看到,TextView
兩個參數的構造函數調用了三個參數的構造函數,而第三個參數使用的值就是Theme
中的com.android.internal.R.attr.textViewStyle
屬性值。spa
若是咱們想覆蓋Theme
中的com.android.internal.R.attr.textViewStyle
,就須要自定義這個屬性的值,代碼以下
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!--定義TextView使用的屬性--> <item name="android:textViewStyle">@style/MyTextViewStyle</item> </style>
<!--自定義TextView的顏色-->
<style name="MyTextViewStyle" parent="Widget.AppCompat.TextView"> <item name="android:textColor">@color/colorAccent</item> </style>
複製代碼
Theme
是全局控制樣式的,可是時候咱們只想爲某幾個TextView
單獨定義樣式,那就得使用四個參數的構造函數。
四個參數構造函數的使用方式,通常是在三個參數的構造函數中調用,並傳入自定義Style
。
首先,在styles.xml
中聲明一個TextView
使用的Style
<style name="CustomTextViewStyle" parent="Widget.AppCompat.TextView" > <item name="android:textColor">@color/colorPrimaryDark</item> </style>
複製代碼
這個Style
只是簡單定義了TextView
的文本顏色。
而後自定義一個繼承自TextView
的控件
public class MyTextView extends TextView {
public MyTextView(Context context) {
this(context, null);
}
public MyTextView(Context context, @Nullable AttributeSet attrs) {
// 注意,這裏的第三個參數爲0
this(context, attrs, 0);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.CustomTextViewStyle);
}
public MyTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
複製代碼
在兩個參數的構造函數中,調用了三個參數的構造函數,可是傳入的第三個參數的值爲0。至因而什麼緣由,後面會講到。
在三個參數的構造函數中,調用四個參數的構造函數,第四個參數須要傳入自定義的Style
。
既然有這麼多地方能控制屬性值,那麼就有個有限順序。其實能夠從四個參數的obtainStyledAttributes()
方法中看到這個規則
public final TypedArray obtainStyledAttributes( AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {}
複製代碼
第一個參數AttributeSet set
指的是XML中聲明的屬性集。
第三個參數int defStyleAttr
指的是Theme
中的控制控件的屬性。
第四個參數int defStyleRes
指的是自定義的Style
。
那麼最簡單屬性值獲取的優先規則就是第一個參數,第三個參數,第四個參數。
若是在XML給控件使用style
屬性呢?它的優先級是介於第一個參數和第三個參數之間。
那麼最終的優先規則以下
如今,經過一個自定義View演示四個構造函數如何使用。
自定義View名字叫SimpleView
,在這個控件中,只簡單繪製一個圓,而且它有一個自定義屬性,能夠控制圓的顏色。
首先聲明SimpleView
使用的自定義屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--SimpleView的自定義屬性,控制圓的顏色-->
<declare-styleable name="SimpleView">
<attr name="circleColor" format="color|reference" />
</declare-styleable>
</resources>
複製代碼
而後,在兩個參數的構造函數中解析這個屬性顏色值,並使用這個顏色值繪製圓
public class SimpleView extends View {
Paint mPaint = new Paint();
int mColor = Color.BLACK;
int mCenterX, mCenterY;
int mRadius;
public SimpleView(Context context) {
this(context, null);
}
public SimpleView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
// 獲取XML中聲明的屬性集,並解析屬性
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleView);
for (int i = 0; i < a.getIndexCount(); i++) {
int attrIndex = a.getIndex(i);
if (attrIndex == R.styleable.SimpleView_circleColor) {
mColor = a.getColor(attrIndex, Color.BLACK);
}
}
a.recycle();
mPaint.setColor(mColor);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mCenterX = w / 2;
mCenterY = h / 2;
mRadius = w < h ? w / 4 : h / 4;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
}
}
複製代碼
SimpleView
一個參數的構造函數調用了兩個參數的構造函數,在兩個參數的構造函數中解析了自定義的屬性circleColor
。
那麼如今,在XML中使用SimpleView
,而後設置circleColor
屬性,就能夠繪製你想要的顏色的圓
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" xmlns:app="http://schemas.android.com/apk/res-auto">
<com.umx.viewconstructortest.SimpleView app:circleColor="@color/colorAccent" android:layout_width="wrap_content" android:layout_height="wrap_content" />
</RelativeLayout>
複製代碼
如今,咱們想經過Theme
的屬性控制SimpleView
樣式。按照以前所說,那就須要一個屬性並在Theme
中聲明,而後經過三個參數的構造函數來完成Theme
的控制。
首先定義Theme
使用的屬性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--SimpleView的自定義屬性-->
<declare-styleable name="SimpleView">
<attr name="circleColor" format="color|reference" />
</declare-styleable>
<!--在Theme中控制SimpleView樣式的屬性-->
<attr name="SimpleViewStyle" format="reference" />
</resources>
複製代碼
SimpleViewStyle
就是SimpleView
要使用的Theme
的屬性。
如今,咱們在Theme
中加入這個屬性
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!--SimpleViewStyle控制SimpleView樣式--> <item name="SimpleViewStyle">@style/MySimpleViewStyle</item> </style>
<style name="MySimpleViewStyle"> <!--統一控制SimpleView使用的顏色爲黑色--> <item name="circleColor">#000000</item> </style>
</resources>
複製代碼
這一切準備就緒後,就來實現三個參數的構造函數
public class SimpleView extends View {
public SimpleView(Context context) {
this(context, null);
}
public SimpleView(Context context, @Nullable AttributeSet attrs) {
// 調用三個參數的構造函數,傳入的第三個參數爲Theme中聲明的SimpleViewStyle屬性
this(context, attrs, R.attr.SimpleViewStyle);
}
public SimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 注意,這裏使用的obtainStyledAttributes方法有四個參數
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleView, defStyleAttr, 0);
for (int i = 0; i < a.getIndexCount(); i++) {
int attrIndex = a.getIndex(i);
if (attrIndex == R.styleable.SimpleView_circleColor) {
mColor = a.getColor(attrIndex, Color.BLACK);
}
}
a.recycle();
mPaint.setColor(mColor);
}
}
複製代碼
在兩個參數的構造函數中調用了三個參數的構造函數,而且第三個參數傳入的就是剛纔自定義且在Theme
中聲明的屬性。而後在三個參數的構造函數中,爲了使用剛剛傳入的Theme
屬性,必須使用有四個參數的obtainStyledAttributes()
方法,這裏必定要注意。
而後在三個參數的構造函數中完成了自定義屬性的解析,取代兩個參數的構造函數的工做。
若是你想自定義一個擁有特殊樣式的SimpleView
,按照前面的分析,你須要使用四個參數的構造函數,而且須要繼承SimpleView
。
首先,須要在SimpleView
中加入四個參數的構造函數
public class SimpleView extends View {
public SimpleView(Context context) {
this(context, null);
}
public SimpleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, R.attr.SimpleViewStyle);
}
public SimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public SimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// 注意,這裏也是使用四個參數的obtainStyledAttributes()方法
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SimpleView, defStyleAttr, defStyleRes);
for (int i = 0; i < a.getIndexCount(); i++) {
int attrIndex = a.getIndex(i);
if (attrIndex == R.styleable.SimpleView_circleColor) {
mColor = a.getColor(attrIndex, Color.BLACK);
}
}
a.recycle();
mPaint.setColor(mColor);
}
}
複製代碼
實現四個參數的構造函數很是簡單,只須要在三個參數的構造函數中調用四個參數的構造函數,而後把第四個參數傳入0便可。同時我把屬性的解析移到了四個參數的構造函數中。
如今,定義一個繼承子SimpleView
的類CustomSimpleView
,而且傳入自定義的樣式
public class CustomSimpleView extends SimpleView {
public CustomSimpleView(Context context) {
this(context, null);
}
public CustomSimpleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomSimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, R.style.CustomSimpleViewStyle);
}
public CustomSimpleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
複製代碼
CustomSimpleView
的兩個參數的構造函數中,調用了三個參數的構造函數,然而第三個參數傳入的0,也就是說CustomSimpleView
不想使用Theme
控制它的樣式。
CustomSimpleView
的三個參數的構造函數中,調用了四個參數的構造函數,而且第四個參數傳入了自定義的樣式R.style.CustomSimpleViewStyle
。
CustomSimpleViewStyle
自定義樣式以下
<resources>
<style name="CustomSimpleViewStyle"> <item name="circleColor">#ff0000</item> </style>
</resources>
複製代碼
如此一來,CusstomSimpleView
就不能經過Theme
控制樣式,而使用的是自定義的Style
控制樣式。
日常咱們可能用不到這些知識,可是在寫系統控件的時候,這個就不能忽視,尤爲在咱們自定義系統的Theme
的時候,就顯得尤其重要。