自定义View之构造函数学习

在学习了博客里前辈们的关于自定义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);
    }
}


通过Android source可以看到,第二个,第三个构造函数都是调用了四个参数的构造函数,参数默认补0;


二、添加自定义属性,给要操作的自定义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;


三、声明自定义属性值(在添加attrs.xml中添加了属性以后。)

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
    }


由于声明赋值是通过引用,就暴露了其缺点,所以引入TypedArray类,Tag此处用到。因为在<declare-styleable>,

在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


	












发布了30 篇原创文章 · 获赞 3 · 访问量 11万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章