常見(jiàn)構(gòu)造方式:
在Android 中要使用一個(gè)view铛楣,通常會(huì)有兩種方式,1、xml中 2芳来、通過(guò)構(gòu)造函數(shù)new出一個(gè)指定的view對(duì)象。
/**
* 關(guān)于view構(gòu)造參數(shù)的詳解
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout container = (LinearLayout) findViewById(R.id.activity_main);
/**
* 一個(gè)參數(shù)的構(gòu)造函數(shù)
*/
Button buttonOne = new Button(this);
buttonOne.setText("(Context)");
/**
* 三個(gè)參數(shù)的構(gòu)造函數(shù)
*/
Button buttonThree = new Button(this, null, 0);
buttonThree.setText("(Context,AttributeSet,0)");
container.addView(buttonOne);
container.addView(buttonThree);
}
}
顯然使用三個(gè)構(gòu)造函數(shù)生成的button猜拾,樣式不正確而且無(wú)法完成點(diǎn)擊
View的構(gòu)造函數(shù):
通用構(gòu)造函數(shù)展示如下:
public View(Context context);
public View(Context context, AttributeSet attrs);
public View(Context context, AttributeSet attrs, int defStyle);
關(guān)于button的構(gòu)造函數(shù)展示如下:
public class Button extends TextView {
public Button(Context context) {
this(context, null);
}
public Button(Context context, AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.buttonStyle);
}
public Button(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
}
Button 繼承自 TextView即舌,顯然由構(gòu)造函數(shù)我們可知,二者的區(qū)分主要來(lái)自com.android.internal.R.attr.buttonStyle這第三個(gè)參數(shù)挎袜。
View構(gòu)造方法中的第三個(gè)參數(shù):
- 用來(lái)給View提供一個(gè)基本的style顽聂,如果我們沒(méi)有對(duì)view設(shè)置某些屬性,就使用這個(gè)style的屬性盯仪。
通過(guò)三個(gè)參數(shù)的構(gòu)造函數(shù)紊搪,進(jìn)入TextView中查看構(gòu)造方法,當(dāng)中有一句關(guān)鍵代碼如下全景,:
TypedArray a = theme.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
obtainStyledAttributes方法介紹:
public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
- set:在XML中明確寫(xiě)出的屬性集合耀石。(比如:android:layout_width 等)
- attrs:需要我們?cè)谏厦娴膕et集合中查詢(xún)哪些內(nèi)容,如果是自定義view爸黄,一般我們會(huì)把自定義的屬性寫(xiě)在declare-styleable中滞伟,代表我們想要查詢(xún)這些自定義屬性。
- defStyleAttr:這是一個(gè)定義在attrs.xml文件中的attribute 這個(gè)值起作用需要兩個(gè)條件:1炕贵、值不為0 2梆奈、在Theme中出現(xiàn)過(guò)(出現(xiàn)即可)
- defStyleRes:這是在styles.xml文件中定義的一個(gè)style。只有當(dāng)defStyleAttr沒(méi)有起作用称开,才會(huì)使用到這個(gè)值亩钟。
顯然,一個(gè)屬性最終的取值鳖轰,是有一個(gè)順序的問(wèn)題清酥,這個(gè)順序的優(yōu)先級(jí)從高到低依次是:
1、直接中在XML文件中定義蕴侣。
2焰轻、在XML文件中通過(guò)style這個(gè)屬性定義。
3睛蛛、通過(guò)defStyleAttr定義鹦马。
4胧谈、通過(guò)defStyleRes定義忆肾。
5、直接在當(dāng)前工程的theme主題下定義菱肖。
關(guān)于com.android.internal.R.attr.buttonStyle 這個(gè)位于客冈,frameworks\base\core\res\res\values\attrs.xml 中:
<attr name="buttonStyle" format="reference" />
只是定義了一個(gè)引用,在theme.xml文件下稳强,有這樣一個(gè)style:
<style name="Theme">
...
<item name="buttonStyle">@android:style/Widget.Button</item>
...
</style>
在這里使用到了buttonStyle屬性场仲,它指向另外一個(gè)style和悦,這個(gè)style在styles.xml文件下:
<style name="Widget.Button">
<item name="android:background">@android:drawable/btn_default</item>
<item name="android:focusable">true</item>
<item name="android:clickable">true</item>
<item name="android:textAppearance">?android:attr/textAppearanceSmallInverse</item>
<item name="android:textColor">@android:color/primary_text_light</item>
<item name="android:gravity">center_vertical|center_horizontal</item>
</style>
這些屬性都是用來(lái)配置Button的,如果在XML文件中沒(méi)有給Button配置背景渠缕、內(nèi)容的位置等屬性們就會(huì)默認(rèn)的使用這里的屬性鸽素。當(dāng)前這是在使用了defStyleAttr的情況下才會(huì)出現(xiàn)的。
上面的themes.xml中的那個(gè)style的名稱(chēng)為T(mén)heme亦鳞,而在我們自己的工程中馍忽,在配置menifest文件的時(shí)候,給application或者activity設(shè)置的主題android:theme一般都是這個(gè)style的子類(lèi)燕差,所以也就這樣使用到了defStyleAttr定義的屬性了
還有一個(gè)defStyleRes參數(shù)遭笋,我們可以發(fā)現(xiàn)在TextView、ImageView等控件中徒探,這個(gè)值傳的都是0瓦呼,也就是不使用它。它的作用就像是一個(gè)替補(bǔ)测暗,當(dāng)defStyleAttr不起作用的時(shí)候它就上場(chǎng)央串,因?yàn)樗彩且粋€(gè)style,這個(gè)參數(shù)是怎么起作用的在下面的實(shí)例中有提到碗啄。
public class DefineView extends View {
static final String LOG_TAG = "DefineView";
public DefineView(Context context) {
this(context, null);
}
public DefineView(Context context, AttributeSet attrs) {
/**
* 在style中蹋辅,theme style中建立對(duì)應(yīng)的attrs和style之間的對(duì)應(yīng)關(guān)系
*/
this(context, attrs, R.attr.defineViewStyle);
}
public DefineView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
/**
* 打印各個(gè)屬性的值
*/
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefineView, defStyleAttr, 0);
Log.d(LOG_TAG, "attr1 => " + array.getString(R.styleable.DefineView_attr1));
Log.d(LOG_TAG, "attr2 => " + array.getString(R.styleable.DefineView_attr2));
Log.d(LOG_TAG, "attr3 => " + array.getString(R.styleable.DefineView_attr3));
Log.d(LOG_TAG, "attr4 => " + array.getString(R.styleable.DefineView_attr4));
Log.d(LOG_TAG, "attr5 => " + array.getString(R.styleable.DefineView_attr5));
Log.d(LOG_TAG, "attr6 => " + array.getString(R.styleable.DefineView_attr6));
Log.d(LOG_TAG, "attr6 => " + array.getString(R.styleable.DefineView_attr7));
}
}
<com.example.leiiiooo92.defineview.DefineView
style="@style/xml_style"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:attr1="attr1 from xml"
app:attr2="attr2 from xml" />
<resources>
<!--定義自定義view的屬性-->
<declare-styleable name="DefineView">
<attr name="attr1" format="string" />
<attr name="attr2" format="string" />
<attr name="attr3" format="string" />
<attr name="attr4" format="string" />
<attr name="attr5" format="string" />
<attr name="attr6" format="string" />
<attr name="attr7" format="string" />
</declare-styleable>
<attr name="defineViewStyle" format="reference" />
</resources>
<resources>
<!-- Base application theme. -->
<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>
<!--綁定attrs和style中對(duì)應(yīng)項(xiàng)的關(guān)系-->
<item name="defineViewStyle">@style/define_view_style</item>
</style>
<!-- 首先定義我們的defStyleAttr屬性(在本項(xiàng)目中是defineViewStyle屬性)需要用到的style(位于styles.xml文件中) -->
<style name="define_view_style">
<item name="attr3">attr3 from define_view_style</item>
<item name="attr4">attr4 from define_view_style</item>
</style>
<!-- 定義一個(gè)在xml布局中需要使用到的style -->
<style name="xml_style">
<item name="attr2">attr2 from xml_style</item>
<item name="attr3">attr3 from xml_style</item>
</style>
</resources>
分析:
attr1只在xml布局文件中設(shè)置,所以值為attr1 from xml挫掏。
attr2在xml布局文件和xml style中都設(shè)置了侦另,取值為布局文件中設(shè)置的值,所以為attr2 from xml尉共。
attr3沒(méi)有在xml布局文件中設(shè)置褒傅,但是在xml style和defStyleAttr定義的style中設(shè)置了,取xml style中的值袄友,所以值為attr3 from xml_style殿托。
attr4只在defStyleAttr定義的style中設(shè)置了,所以值為attr4 from custom_view_style剧蚣。
下面測(cè)試一下defStyleRes這個(gè)參數(shù)支竹,它是一個(gè)style
<style name="define_view_res_style">
<item name="attr4">attr4 from define_view_res_style</item>
<item name="attr5">attr5 from define_view_res_style</item>
</style>
當(dāng)defStyleAttr這個(gè)參數(shù)定義為0(即不使用這個(gè)參數(shù)),或者是在theme中找不到defStyleAttr這個(gè)屬性時(shí)(即使在theme中的配置是這樣的:<item name="defStyleAttr">@null</item>鸠按,也代表找到了defStyleAttr屬性礼搁,defStyleRes參數(shù)也不會(huì)生效),defStyleRes參數(shù)才會(huì)生效目尖。
修改對(duì)應(yīng)的獲取自定義屬性語(yǔ)句:
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefineView, 0, R.style.define_view_res_style);
由于defStyleAttr已經(jīng)失效馒吴,所以attr4和attr5都是從define_view_res_style中獲取到的值。
在主題中添加指定的屬性值:
<item name="attr5">attr5 from AppTheme</item>
<item name="attr6">attr6 from AppTheme</item>
attr5在define_view_res_style和theme下都定義了,取define-res-style下的值饮戳,所以為attr5 from define_view_res_style豪治。
attr6只在theme下定義了,所以取值為attr6 from AppTheme扯罐。
當(dāng)未設(shè)置def-res-style時(shí)负拟,主題中的屬性是否生效:
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefineView, defStyle, 0);
總結(jié):
View中的屬性有多處地方可以設(shè)置值,這個(gè)優(yōu)先級(jí)是:
1歹河、直接在XML布局文件中設(shè)置的值優(yōu)先級(jí)最高齿椅,如果這里設(shè)置了值,就不會(huì)去取其他地方的值了启泣。
2涣脚、XML布局文件中有一個(gè)叫“style”的屬性,它指向一個(gè)style寥茫,在這個(gè)style中設(shè)置的屬性值優(yōu)先級(jí)次之遣蚀。
3、如果上面兩個(gè)地方都沒(méi)有設(shè)置值纱耻,那么就會(huì)根據(jù)View帶三個(gè)參數(shù)的構(gòu)造方法中的第三個(gè)參數(shù)attribute指向的style設(shè)置值芭梯,前提是這個(gè)attribute的值不為0。
4弄喘、如果上面的attribute設(shè)置為0了玖喘,我們就根據(jù)obtainStyledAttributes()方法中的最后一個(gè)參數(shù)指向的style來(lái)設(shè)置值。
5蘑志、如果仍然沒(méi)有設(shè)置到值累奈,就會(huì)用theme中直接設(shè)置的屬性值,而不會(huì)去管第3步和第4步中是否設(shè)置了值急但。
必須要注意:要想讓View構(gòu)造方法的第三個(gè)參數(shù)生效澎媒,必須讓它出現(xiàn)在我們自己的Application或者Activity的android:theme所指向的style中。設(shè)置Activity的theme一樣可以波桩。