在學習了博客裏前輩們的關於自定義View知識,來總結一下我學習自定義View構造函數的收穫,以及還有遇到的一些問題。遇到的問題,在以後解決後,會補充。
開始做一件事情,不要求自己一步做到完美,我會慢慢改進學習,去完善。
問題,驅動學習!
一、自定義View構造函數。
一共有四個構造函數,關於有四個參數的構造函數,是在Android API 21 之後才添加的。(AndroidL的新特性)
public class CustonViewComstructorStudy extends View {
/**在Java代碼中,new一個View時候,通常使用*/
public CustonViewComstructorStudy(Context context) {
super(context);
}
/**在xml中引入一個視圖View時,這個View是我們自己定義在xml佈局文件當中。
* 官方文檔:Constructor that is called when inflating a view from XML.*/
public CustonViewComstructorStudy(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**在打印日誌裏,始終沒有看到程序走第三個構造函數*/
public CustonViewComstructorStudy(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
/**基於Android API 21之後*/
public CustonViewComstructorStudy(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
二、添加自定義屬性,給要操作的自定義View使用。(這些自定義屬性都是針對兩個,或者兩個以上參數的構造函數使用。)
1、在res目錄下,values文件內,新建一個attrs.xml。
通過<declare-styleable>標籤內的子標籤<attr>,添加View的自定義屬性。
<attr name="填寫屬性名稱" format="屬性類型">
代碼a attr.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--這裏是我們自己寫的內容,declare-styleable 的name默認提示與自定義View的類名一致-->
<declare-styleable name="CustomViewStudy">
<attr name="attr_string1" format="string"/>
<attr name="attr_integer2" format="integer"/>
<attr name="attr_boolean3" format="boolean"/>
<attr name="attr_float4" format="float"/>
<attr name="attr_color5" format="color"/>
<attr name="attr_string6" format="string"/>
<attr name="android:text"/>
<attr name="android:textSize"/>
</declare-styleable>
<attr name="customize_reference" format="reference"/>
</resources>
解釋說明:關於format的屬性類型,參看博客:http://blog.csdn.net/mybeta/article/details/39962235 自定義View屬性。
關於<atte name="customize_refernce" format="reference">也可以在<declare-styleable>標籤內,或者標籤外。移動位置以後,需要clean下代碼。任意一條屬性都可以。
2、隨即,一個問題來,<declare-styleable>標籤的作用是什麼?
首先不管是否使用<declare-styleable>,系統都會在R.attr中生成他們對應的attribute值。
public static final int customize_reference=0x7f010000;
如果使用了該標籤,系統還會在R.styleable中生成相關屬性。此處打一個Tag,後序還會用到該屬性。
以下代碼都在R.java中的 public static fianl class styleable{} 內部類中。
public static final int[] CustomViewStudy = {
0x01010095, 0x0101014f, 0x7f0100a1, 0x7f0100a2,
0x7f0100a3, 0x7f0100a4, 0x7f0100a5, 0x7f0100a6
};
public static final int CustomViewStudy_android_text = 1;
public static final int CustomViewStudy_android_textSize = 0;
public static final int CustomViewStudy_attr_boolean3 = 4;
public static final int CustomViewStudy_attr_color5 = 6;
public static final int CustomViewStudy_attr_float4 = 5;
public static final int CustomViewStudy_attr_integer2 = 3;
public static final int CustomViewStudy_attr_string1 = 2;
public static final int CustomViewStudy_attr_string6 = 7;
1、首先要在根佈局中,添加命名空間:
//這是Android系統自帶
xmlns:android="http://schemas.android.com/apk/res/android
xmlns:cv="http://schemas.android.com/apk/res-auto"
cv:是給自己的自定義屬性起的命名空間。放在Android系統自帶下邊即可。
Tip: studio中是/apk/res-auto。eclispe中是全路徑名 /apk/自定義View全路徑名
2、在xml佈局中,創建自定義View佈局。不多講,上代碼:
代碼b:
<com.fight.liuhaoqing.a0917customviewconstructor2.CustomViewStudy
android:layout_width="wrap_content"
android:layout_height="wrap_content"
cv:attr_string1="@string/custom_view_string"
style="@style/customize_style"/>
3、給自定義屬性賦值。
有三種方式賦值方式:
①直接在xml中,給屬性直接賦值。cv:attr_string1=@string/custom_view_string
②設置style,並在style中設置屬性值,style="@style/customsize_style"
在values文件下styles.xml文件coding。見代碼c
③在Application與Activity中指定Theme,可以在Theme中指定在當前Activity或Application中
屬性默認值。也是在styles.xml文件coding。
通過attrs.xml 中聲明的reference屬性,在AppTheme中:
<item name="customize_reference">@style/customize_style_in_theme</item>調用。
問題是,我在打印時,該引用的自定義屬性值,沒有打印出來,懷疑是parent屬性導致。(留坑,待解決)
還有一種方式在Theme中,直接給自定義屬性賦值。在打印時,可以看到值。
代碼c :styles.xml
<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>
<item name="customize_reference">@style/customize_style_in_theme</item>
<item name="attr_string6">string6_in_app_theme</item>
</style>
<!--自定義View xml中引用的style 屬性賦值-->
<style name="customize_style">
<item name="attr_string1">你是我的眼</item>
<item name="attr_integer2">9527</item>
<item name="attr_boolean3">false</item>
</style>
<!--在AppTheme中引用的style 屬性賦值-->
<style name="customize_style_in_theme">
<item name="attr_integer2">8888</item>
<item name="attr_float4">88.88</item>
<item name="attr_color5">#000000</item>
</style>
<!--在defStyleAttribute不生效時的備用屬性賦值-->
<style name="customize_style_def_attr_res">
<item name="attr_color5">#ffffff</item>
</style>
</resources>
四、兩個參數的構造函數。 主要看第二個參數:AttributeSet attrs
通過名字可以看出是一個Set集合。
源碼給出含義:@param attrs The attributes of the XML tag that is inflating the view.
public View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
①關於attrs使用方式,上代碼:
public CustomViewStudy(Context context, AttributeSet attrs) {
super(context, attrs);
/**此處就是AttributeSet參數的本質上的使用方式。能夠取到當前View聲明的所有屬性。*/
//獲取當前 attrs 集合Set大小。
int count = attrs.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = attrs.getAttributeName(i);//獲取當屬性名稱
String value = attrs.getAttributeValue(i);//獲取當前屬性值
Log.w(TAG, "Attribute name :" + name);
Log.w(TAG, "Attribute value :" + value);
}
//通過如下結果可以看出,屬性有具體值時可以打印出值,如果是值引用,打印出來的就是一個引用值
//所以,通過TypedArray ta = context.obtainStyledAttribute()方法來取當前View裏所有的屬性值。
// W/CustomViewStudy: Attribute name :layout_width
// W/CustomViewStudy: Attribute value :-2
// W/CustomViewStudy: Attribute name :layout_height
// W/CustomViewStudy: Attribute value :-2
// W/CustomViewStudy: Attribute name :attr_string1
// W/CustomViewStudy: Attribute value :@2131099681
}
在R.java中生成的是一個數組,見代碼b,如果佈局中的屬性的值是引用類型(比如:@string/custom_view_string),
如果使用AttributeSet去獲得最終的像素值,那麼需要第一步拿到id,第二步再去解析id。而TypedArray正是幫我們簡化了這個過程。
②獲取TypedArray 對象 需要通過 TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CustomViewStudy);方法。
但是obtainStyledAttributes方法最終調用的是getTheme去獲取該obtainStyledAttributes系列函數。
public TypedArray obtainStyledAttributes(int[]attrs)
//我主要使用這一個,給自定義屬性賦值,使用兩個參數的構造函數
public TypedArray obtainStyledAttributes(intresid,int[]attrs)throws NotFoundException {}
public TypedArray obtainStyledAttributes(AttributeSetset, int[]attrs,intdefStyleAttr,intdefStyleRes){}
說一下其參數含義:
set:屬性值的集合
attrs:我們要獲取的屬性的資源ID的一個數組
defStyleAttr:這個是當前Theme中的一個attribute,是指向style的一個引用,當在layoutxml中和style中都沒有爲View指定屬性時,會從Theme中這個attribute指向的Style中查找相應的屬性值,這就是defStyle的意思,如果沒有指定屬性值,就用這個值,
所以是默認值,但這個attribute要在Theme中指定,且是指向一個Style的引用,如果這個參數傳入0表示不向Theme中搜索默認值
defStyleRes:這個也是指向一個Style的資源ID,但是僅在defStyleAttr爲0或defStyleAttr不爲0但Theme中沒有爲defStyleAttr屬性賦值時起作用
public CustomViewStudy(Context context, AttributeSet attrs) {
super(context, attrs);
/**此處就是AttributeSet參數的本質上的使用方式。能夠取到當前View聲明的所有屬性。*/
//獲取當前 attrs 集合Set大小。
// int count = attrs.getAttributeCount();
// for(int i = 0; i < count; i++){
// String name = attrs.getAttributeName(i);//獲取當屬性名稱
// String value = attrs.getAttributeValue(i);//獲取當前屬性值
//
// Log.w(TAG,"Attribute name :" + name);
// Log.w(TAG,"Attribute value :" + value);
// }
//通過如下結果可以看出,屬性有具體值時可以打印出值,如果是值引用,打印出來的就是一個引用值
//所以,通過TypedArray ta = context.obtainStyledAttribute()方法來取當前View裏所有的屬性值。
// W/CustomViewStudy: Attribute name :layout_width
// W/CustomViewStudy: Attribute value :-2
// W/CustomViewStudy: Attribute name :layout_height
// W/CustomViewStudy: Attribute value :-2
// W/CustomViewStudy: Attribute name :attr_string1
// W/CustomViewStudy: Attribute value :@2131099681
TypedArray ta = context.obtainStyledAttributes(attrs,R.styleable.CustomViewStudy);
String attr_str = ta.getString(R.styleable.CustomViewStudy_attr_string1);
int attr_int = ta.getInteger(R.styleable.CustomViewStudy_attr_integer2,314);
boolean attr_boolean = ta.getBoolean(R.styleable.CustomViewStudy_attr_boolean3,true);
float attr_float = ta.getFloat(R.styleable.CustomViewStudy_attr_float4,3.14f);
//getColor返回的是一個int值
int attr_color = ta.getColor(R.styleable.CustomViewStudy_attr_color5, Color.parseColor("#5affefff"));//或者格式:Color.BLUE
String attr_str6 = ta.getString(R.styleable.CustomViewStudy_attr_string6);
Log.e(TAG,"perform 2 params constructor");
Log.e(TAG,"Attribute attr_str:" + attr_str);
Log.e(TAG,"Attribute attr_int:" + attr_int);
Log.e(TAG,"Attribute attr_boolean:" + attr_boolean);
Log.e(TAG,"Attribute attr_float:" + attr_float);
Log.e(TAG,"Attribute attr_color:" + attr_color);
Log.e(TAG,"Attribute attr_str6:" + attr_str6);
ta.recycle();//調用後不會對下次使用造成影響。
}
關於屬性值定義的優先級:xml>style>Theme中的默認Sytle>默認Style(通過obtainStyledAttributes的第四個參數指定)>在Theme中直接指定屬性值
我在自己的打印日誌中,始終看不到通過Theme引用的style方式賦值屬性,以及默認的defStyleRes的賦值打印信息。順序就先牢記,(留坑,待補充)
關於三個參數構造,四個參數構造,我始終在代碼中,沒有執行走到,就先不寫了。自定義View基礎先學習到這。
2016-09-28修改start:
關於系統沒有調用三個參數,或者四個參數的構造方法,是需要我們自己去在代碼中,顯示調用三個參數,或者四個參數的構造方法。
2016-09-28修改end。
後記:雖然學習一邊,感覺總結有點亂,以後慢慢整理思路。
參考文章:
http://blog.csdn.net/lmj623565791/article/details/45022631/
http://www.cnblogs.com/angeldevil/p/3479431.html
http://blog.csdn.net/mybeta/article/details/39962235