以前很少注意到的一些问题,在看过一些大神的文章以后有了一些新的认识。真正觉得自己接触到的android只是冰山一角,大神们确实在这些方面已经超越了我太多太多了。所以虚心学习才能百尺竿头,废话不多说。

       本文主要讲解的是自定义控件当中的有关自定义属性的问题,以前的做法是:

        1.在res/value目录下创建一个xml文件,在resource节点下,声明declare-styleable标签,在标签里面声明item标签,每个item需要声明name跟format属性  每个item就是一个自定义属性。

        2.定义完属性就需要在xml里面使用。首先在布局文件的最外层定义命名空间,然后在自定义view里面使用自定义属性。

        3.在自定义view中获取xml文件里面使用的自定义属性  TypedArray array = context.obtainStyledAttributes();第一个参数是xml布局传递过来的属性,第二个参数是自定义的的属性文件,这个函数的作用在笔者看来是为了将xml里面设置的属性跟自定义属性匹配存储到array里面。

        4.获取自定义属性的value值,并用于操作。array.getInt();array.getString()等等诸如此类。



    然而今天看了大神的文章,又学到了一些东西:

    1.构造方法方面的问题,以前从来没有注意过这个问题:众所周知自定义view需要重新父类的构造方法,有一个参数、两个参数、三个参数、四个参数的。一个参数的使用场景主要是用在new 自定义view的时候,两个参数的使用场景主要是布局里面使用自定义view的时候。其余两种构造方法都很少用到。

     先来说明一个问题:我在自定义一个button的时候,重写了构造方法,但是在使用的时候,出现的并不是button的样式。看完大神的文章,我才知道了这个错误出现在什么地方,原来是我在自定义view的时候,构造方法里面没有使用默认的样式导致的。都知道构造方法的作用是:初始化一些相关内容以及获取自定义属性。之前我都是这么写的。

    

public MyCustomImageView(Context context) {
        this(context, null);
    }

    public MyCustomImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);//在此处写入要使用的默认样式文件
    }

    public MyCustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        //解析xml中自定义控件的属性,并将xml里面的属性与自定义属性进行绑定
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyCustomImageView);

    原来构造器的写法是有两种方式的:

    第一种方式:

public MyCustomImageView(Context context) {
        super(context);
        init();
    }

    public MyCustomImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyCustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }



第二种方式:


public MyCustomImageView(Context context) {
        this(context, null);
    }

    public MyCustomImageView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public MyCustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);





之所以会出现上述所说的自定义出来的view没有父类控件的默认样式的问题,是因为第二种方式更容易把theme里面的默认样式覆盖掉,这个场景下使用第一种方式更安全。如果需要设置defStyleAttr,使用第二种方式会比较好。


2.获取自定义属性的两种方式:

获取自定义的属性都需要将xml里面设置的属性跟自定义属性文件进行匹配。

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyCustomImageView);

这样xml里面的属性值就跟自定义属性文件里面的属性绑定在一起并存储在typedarray里面。

获取方式一:

String name = typedArray.getString();参数为属性id

int i = typedArray.getInt();第一个参数为属性id,第二个参数为默认值

获取方式二:

int count = typedArray.getIndexCount();//获取typedArray里面所有数据的数量
        for (int i = 0; i < count; i++) {
            //
            Log.i("当前的属性是:", typedArray.getIndex(i) + "");
            //要获得自定义属性,根据自定义属性获得xml中自定义属性设置的内容
            int attr = typedArray.getIndex(i);//获取指定位置的index
            switch (attr) {
                case R.styleable.MyCustomImageView_src:
                    //获取xml里面设置的内容
                    int imageId = typedArray.getResourceId(R.styleable.MyCustomImageView_src, 0);
//                    int resoourId = typedArray.getInt(R.styleable.MyCustomImageView_src, R.mipmap.ic_launcher); 这样获得的是全路径
                    imageView.setBackgroundResource(imageId > 0 ? imageId : R.mipmap.ic_launcher);
                    break;
                case R.styleable.MyCustomImageView_text:
                    //获取xml里面的内容设置到textview
                    String text = typedArray.getString(R.styleable.MyCustomImageView_text);
                    int resourceid = typedArray.getResourceId(R.styleable.MyCustomImageView_text, 0);
                    textView.setText(resourceid > 0 ? typedArray.getResources().getText(resourceid) : typedArray.getString(R.styleable.MyCustomImageView_text));
                    break;
            }
//在xml文件里面如果使用的是引用,可以使用getResourceId();,如果使用的直接是文字或者数字,可以直接使用 typedArray.getString();或者typedArray.getInt()




3.defStyleRes 这个的作用是指定一个默认的style资源的。如果没有在布局文件里面指定自定义属性,就会自动加载该文件下的属性。


  


   defStyleAttr 是一个attr,它引用了一个style文件,并在当前theme下进行设置。指定一个默认的属性样式。可用于切换不同theme,view进行切换不同样式的场景。


大致如下:


1.首先定义一个attr文件:


 


<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomImageView">
        <attr name="text" format="string"></attr>
        <attr name="src" format="integer"></attr>
        <attr name="attrViewStyleRef" format="reference"></attr>//它的作用就是为了在不同theme下切换不同属性内容的
    </declare-styleable>
</resources>




2.然后在theme下进行配置


<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="attrViewStyleRef">@style/ref</item>//对应自定义属性当中的属性
    </style>

    <style name="ref">//不同theme下设置不同的内容style
        <item name="text">加油</item>
        <item name="src">1</item>
    </style>




3.在自定义view的构造函数中进行设置


TypedArray typedArray =context.obtainStyledAttributes(attrs,R.styleable.MyCustomImageView,R.attr.attrViewStyleRef,0);