當我們寫一個自定義控件時需要重寫其構造方法,比如這樣:
//1個參數的構造方法
public BaseLoadingLayout(Context context) {
super(context);
}
//2個參數的構造方法
public BaseLoadingLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
//3個參數的構造方法
public BaseLoadingLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//4個參數的構造方法
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public BaseLoadingLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr,defStyleRes);
}
其中,第四個構造方法(也就是有四個參數的方法)是sdk21之后view和viewGroup才有的。
如果是new一個自定義view的話,會根據參數來進行構造方法的重載。
直接在xml里引用控件時,對LayoutInflater代碼分析,發現會調用兩個參數的構造方法,所以要想自定義view能在xml里被引用,要重寫其兩個參數的構造方法。
if (mConstructorArgs[0] == null) {
// Fill in the context if not already within inflation.
mConstructorArgs[0] = mContext;
}
Object[] args = mConstructorArgs;
args[1] = attrs;
final View view = constructor.newInstance(args);
自定義view有時需要自定義屬性
1.在value/attrs.xml里新建StyleableRes 也就是declare-styleable 節點,如下:
<declare-styleable name="CircleProgressbar">
<attr name="radius" format="dimension"/>
<attr name="strokeWidth" format="dimension"/>
<attr name="ringColor" format="color"/>
<attr name="textColor" format="color"/>
</declare-styleable>
<attr name="myProgressBaraaa" format="reference"/>
2.在view構造方法里取出屬性值,并根據屬性操作view,代碼如下:
private void initAttrs(Context context, AttributeSet attrs) {
TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressbar, R.attr.DefaultBaseLoadingLayoutViewStyle , R.style.DefaultCustomViewStyle);
radius = typeArray.getDimension(R.styleable.CircleProgressbar_radius, 80);
strokeWidth = typeArray.getDimension(R.styleable.CircleProgressbar_strokeWidth, 10);
ringColor = typeArray.getColor(R.styleable.CircleProgressbar_ringColor, 0xFF0000);
textColor = typeArray.getColor(R.styleable.CircleProgressbar_textColor, 0xFFFFFF);
typeArray.recycle();
}
其中obtainStyledAttributes方法用來得到最終的view的所有屬性值,想寫好一個自定義view是要考慮如何獲取該view的屬性,給view添加屬性最直接的方式就是寫在layout的xml里面,如果xml里面沒寫屬性或者想直接new一個view時,我們就要去theme里面找,看有theme里面有沒有定義這個view的屬性,如果沒有,需要給view設置一個默認的屬性值,obtainStyledAttributes方法就是為了完成以上任務的,來看看他的參數有哪些:
public final TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) {
return getTheme().obtainStyledAttributes(
set, attrs, defStyleAttr, defStyleRes);
}
AttributeSet set 代表控件寫在布局xml里的屬性
@StyleableRes int[] attrs :我們想要得到的所有屬性,這是我們自己聲明的value/attrs.xml里declare-styleable 節點下的屬性
@AttrRes int defStyleAttr:下文再表
/@StyleRes int defStyleRes:如果defStyleAttr傳的是0,則取defStyleRes里面的style屬性
下面我們來講defStyleAttr
如果set 為null時(set 是從xml里取到的屬性值,當我們直接new一個view而不是從xml里inflate時,set為null),就從defStyleAttr里讀取屬性值,defStyleAttr從名字來看,它帶一個attr,說明它是view的一個屬性,只是這個屬性是用來定義這個view的默認樣式的(當set為空時的樣式)。那我們要如何使用它呢?很多人都簡單粗暴的直接傳0進去,傳0進去這一個參數將使view失去默認樣式,我們可以定義一個默認樣式:
第一步,在values/attrs.xml里加上一個自定義view樣式的屬性
<attr name="costumeViewStyle" format="reference"/>
第二步,在values/attrs.xml里定義一個自定義view的默認樣式,比如:
<style name="myProgressBarRefer" parent="Base.Widget.AppCompat.ProgressBar">
<item name="android:indeterminateTint" tools:targetApi="lollipop">#fff787</item>
<item name="android:indeterminateTintMode" tools:targetApi="lollipop">@color/colorYellow</item>
<item name="android:minHeight">200dp</item>>
<item name="android:minWidth">200dp</item>
</style>
第三步,在values/styles.xml里定義一個theme,theme里加上一行對自定義屬性,并引用剛剛定義好的默認style
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="costumeViewStyle">@style/myProgressBarRefer</item>
</style>
第四步,把我們自定義的theme應用在application或者對于的activity 。
說白了defStyleAttr就是一個view的style屬性,該屬性可以在theme里被賦值