自定义View 属性解析

引言

在自定义View中可以定义、获取并设置属性。那么同样的Button在安卓不同版本下面拥有不同的风格,这是怎样实现的呢?在本文将会对一下几点进行探索。

1.资源文件declare-styleable标签的详解

2.如何获取自定义属性

3.如何针对同一view设置不同主题


declare-styleable标签详解

首先,我自定义一个StarView继承自TextView

public class StarView extends TextView{
    public StarView(Context context) {
        super(context);
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public StarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}
然后,在res/values/attr.xml中添加以下代码:

    <declare-styleable name="StarView">
        <attr name="name" format="string"></attr><!-- 名称 -->
        <attr name="speed" format="float"></attr><!-- 速度 -->
        <attr name="type">
            <flag name="planet" value="0"/><!-- 行星 -->
            <flag name="star" value="1"/><!-- 恒星 -->
            <flag name="satellite" value="2"/><!-- 卫星 -->
        </attr>
        <attr name="textSize" format="dimension"/>
    </declare-styleable>
标签declare-styleable的name属性 必须是类名,不可为其他。在其中,通过attr标签的name可以定义属性名称,format可指定属性类型。属性类型有
        <attr name="tag1" format="boolean"></attr><!-- 布尔值 -->
        <attr name="tag2" format="integer"></attr><!-- 整型值 -->
        <attr name="tag3" format="string"></attr><!-- 字符串 -->
        <attr name="tag4" format="color"></attr><!-- 颜色值 -->
        <attr name="tag5" format="dimension"></attr><!-- 尺寸 -->
        <attr name="tag6" format="enum"></attr><!-- 枚举值 -->
        <attr name="tag7" format="flag"></attr><!-- 标签值 -->
        <attr name="tag8" format="fraction"></attr><!-- 百分值 -->
        <attr name="tag9" format="reference"></attr><!--参考某一资源ID 一旦设置为reference,值的引用就必须用资源引用的方式来设置,否则会报错 -->
        <attr name="tag10" format="float"></attr><!-- 浮点值 -->
设置完属性后,如果需要应用的话,那么则需要在引用该view的layout中加入

xmlns:starattrs="http://schemas.android.com/apk/res-auto"
其中starattrs是自定义标签头,一般将该语句加入到根布局中,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:starattrs="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.dls.myview.widget.StarView
        starattrs:name="太阳"
        starattrs:type="star"
        starattrs:speed="123.5"
        starattrs:textSize="@dimen/y14"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>
获取属性值
public class StarView extends TextView{
    public StarView(Context context) {
        super(context);
    }

    public StarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView);
        String name = typedArray.getString(R.styleable.StarView_name);//获取名称
        String speed = typedArray.getFloat(R.styleable.StarView_speed,0f)+"km/s";//获取速度
        int type = typedArray.getInt(R.styleable.StarView_type,0);//获取类型
        float textSize = typedArray.getDimensionPixelSize(R.styleable.StarView_textSize,10);//获取尺寸大小,默认会转换成px
        typedArray.recycle();//回收资源
        setTextSize(textSize);
        setText(name+"\n"+speed+"\n"+getType(type)+"\n");
    }

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public StarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    public String getType(int type){
        String typeStr = "";
        switch (type){
            case 0:
                typeStr= "行星";
                break;
            case 1:
                typeStr= "恒星";
                break;
            case 2:
                typeStr= "卫星";
                break;
            default:
                typeStr = "行星";
                break;
        }
        return typeStr;
    }
}
这样,一运行后,就会将获得到的属性值通过setText()显示到UI中。

看了一下View的源码,发现源码中也会用到switch来获取属性,如下:

       final int N = a.getIndexCount();
        for (int i = 0; i < N; i++) {
            int attr = a.getIndex(i);
            switch (attr) {
                case com.android.internal.R.styleable.View_background:
                    background = a.getDrawable(attr);
                    break;
                case com.android.internal.R.styleable.View_padding:
                    padding = a.getDimensionPixelSize(attr, -1);
                    mUserPaddingLeftInitial = padding;
                    mUserPaddingRightInitial = padding;
                    leftPaddingDefined = true;
                    rightPaddingDefined = true;
                    break;
                 case com.android.internal.R.styleable.View_paddingLeft:
                    leftPadding = a.getDimensionPixelSize(attr, -1);
                    mUserPaddingLeftInitial = leftPadding;
                    leftPaddingDefined = true;
                    break;


针对同一view设置不同主题

获取属性的构造方法如下,主要看看参数defStyleAttr和defStyleRes

    public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
    }
将实现三种不同的效果:太阳设置为红色的,火星设置为黄色的,地球设置为蓝色的。

defStyleAttr的注释如下:

@param defStyleAttr An attribute in the current theme that contains a
     *        reference to a style resource that supplies default values for
     *        the view. Can be 0 to not look for defaults.
显而易见,defStyleAttr是针对当前主题提供默认样式的引用。可以为0.

为了使用defStyleAttr,首先在attr.xml中定义

<attr name="sunStyle" format="reference"/>
再为StarView添加一条颜色属性

<attr name="textColor" format="color"/>
在style.xml中添加一条颜色style

    <style name="SunStyle">
        <item name="textColor">#ff0000</item>
    </style>
然后在AppTheme中加入上面SunStyle

<item name="sunStyle">@style/SunStyle</item>
在获取属性的时候传入参数:

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView,R.attr.sunStyle,0);
此时,StarView的颜色就变成上面定义好的颜色了。

defStyleRes的注释如下

A resource identifier of a style resource that
     *        supplies default values for the view, used only if
     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
     *        to not look for defaults.
以上,表明defStyleRes是在非主题引用的时候加载的style资源引用。而且只有当defStyleAttr为0或者是无法找到的时候,才会去调用defStyleRes。则表明,defStyleRes的优先级要低于defStyleAttr。当两者同时设置时,默认先调用defStyleAttr.

使用defStyleRes就更简单了,只需要在style.xml中自定义一个style然后在获取属性的时候传入该style就可以了:

    <style name="SunSpStyle">
        <item name="textColor">#ff0000</item>
        <item name="name">太阳</item>
        <item name="speed">10</item>
        <item name="type">star</item>
        <item name="textSize">@dimen/y12</item>
    </style>
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.StarView,0,R.style.SunSpStyle);
以上,就是针对View设置主题的两种不同方式。















猜你喜欢

转载自blog.csdn.net/shirakawakanaki/article/details/53082065