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
的时候,就显得尤其重要。