自定义View相关属性细节

参考:
http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650820236&idx=1&sn=6dec4ff1efeda3224b5a40fdad862404&mpshare=1&scene=23&srcid=0228jLwl3fR8II6i7V1zzMxK#rd

自定义View的流程##

  1. 一般我们新建一个View,继承自现有View,或直接继承自View/ViewGroup,为了让自定义的View有更多属性可配置,类似于textview 的textSize,textColor等属性,我们会定义一个attrs文件中,新建一个declare-styleable,用来定义该控件的自定义属性,如
<declare-styleable name="DivideTextView">
        <attr name="dt_drawable" format="reference|color" />
        <attr name="dt_dividerSize" format="dimension" />
        <!-- 分割线padding,左右上下padding,上下左右padding -->
        <attr name="dt_dividerPadding" format="dimension" />
        <attr name="dt_divideGravity">
            <flag name="none" value="0x01" />
            <flag name="left" value="0x02" />
            <flag name="top" value="0x04" />
            <flag name="right" value="0x08" />
            <flag name="bottom" value="0x10" />
        </attr>
    </declare-styleable>
  1. 在layout文件中,使用自定义属性,如:
<ui.better.DivideTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10dp"
        android:text="开始放大招了"
        android:textSize="20sp"
        app:dt_divideGravity="top|right|bottom|left"
        app:dt_dividerPadding="2dp"
        app:dt_dividerSize="1dp"
        app:dt_drawable="#22bbcc" />
  1. 在自定义View类中,通过obtainStyledAttributes来获取自定义属性值:
public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);

        // 初始化属性
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr, 0);
        try {
            setDivideDrawable(a.getDrawable(R.styleable.DivideTextView_dt_drawable));
            setDivideGravityInner(a.getInt(R.styleable.DivideTextView_dt_divideGravity, NONE));
            setDivideSize((int) a.getDimension(R.styleable.DivideTextView_dt_dividerSize, 0f));
            setDividePadding((int) a.getDimension(R.styleable.DivideTextView_dt_dividerPadding, 0f));
        } finally {
            if (a != null) {
                a.recycle();
            }
        }
    }

obtainStyledAttributes说明##

自定义View中通过 obtainStyledAttributes来获取自定义属性的值;
她有4个重载方法,如图,

obtainStyledAttributes

最终都会调用第一个,也就是4个参数的那个方法;

 public final TypedArray obtainStyledAttributes(
            AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr,
            @StyleRes int defStyleRes) {
        return getTheme().obtainStyledAttributes(
            set, attrs, defStyleAttr, defStyleRes);
    }

参数的意思是:

第4个参数:defStyleRes:####

英文说明:

A resource identifier of a style resource that supplies default values for the TypedArray, used only if defStyleAttr is 0 or can not be found in the theme. Can be 0 to not look for defaults.

用于指定一个style,即默认的style,意思是:如果没有在layout中设置属性,就从style中获取属性的默认值;
比如在style文件中,新建style如下:

<style name="Def_Style_DividerTextView">
        <item name="dt_divideGravity">none</item>
        <item name="dt_dividerPadding">0dp</item>
        <item name="dt_dividerSize">1dp</item>
        <item name="dt_drawable">#88cc00</item>
</style>

修改 获取属性的代码 如下:

final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr,
         R.style.Def_Style_DividerTextView);
 Log.e("better", "dividerPadding: " + padding);
 Log.e("better", "dividerSize: " + size);
 Log.e("better", "Drawable: " + a.getColor(R.styleable.DivideTextView_dt_drawable, 0));

输出如下:

defStyleRes

第3个参数:defStyleAttr:####

英文说明:

An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray. Can be 0 to not look for defaults.

说明它是一个引用类型的属性,指向一个style,并且在当前的theme中进行设置;

使用步骤:

  1. 在attrs.xml中,声明一个attr,创建一个新的引用格式属性‘Def_Style_Attr_DividerTextView’, 如:
<!-- 指向style的引入类型 -->
<attr name="Def_Style_Attr_DividerTextView" format="reference" />
  1. 在styles.xml文件里面,找到我们所使用的theme,添加一条item:
 <!-- 在当前使用的主题中,配置 Def_Style_Attr_DividerTextView -->
 <!-- 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>

        <!-- 在当前使用的主题中,配置 Def_Style_Attr_DividerTextView -->
        <item name="Def_Style_Attr_DividerTextView">@style/Def_Style_DividerTextView</item>
    </style>
  1. 修改 获取属性的代码 如下
 // final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, defStyleAttr, R.style.Def_Style_DividerTextView);
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DivideTextView, R.attr.Def_Style_Attr_DividerTextView, 0);
Log.e("better", "dividerPadding: " + padding);
Log.e("better", "dividerSize: " + size);
Log.e("better", "Drawable: " + a.getColor(R.styleable.DivideTextView_dt_drawable, 0));

输出:

defStyleAttr

对于第三个参数:defStyleAttr
系统中的Button,EdiText,他们会在构造函数中,指定第3个参数:

public Button(Context context, AttributeSet attrs) {
    this(context, attrs, com.android.internal.R.attr.buttonStyle);
}

通过在Theme里,设置参数的样式,比如:background、textColor等,当切换不同的主题时,控件的样式会发生变化,这是因为,不同的主题设置了不同的style;

** 系统的button defStyleAttr目录:**
默认Theme:

D:\workTools\sdk\platforms\android-25\data\res\values\attrs.xml, 有如下:

    <!-- Normal Button style. -->
    <attr name="buttonStyle" format="reference" />

D:\workTools\sdk\platforms\android-25\data\res\values\theme.xml, 有如下:

    <!-- Button styles -->
    <item name="buttonStyle">@style/Widget.Button</item>

D:\workTools\sdk\platforms\android-25\data\res\values\styles.xml, 有如下:

<!---->
<style name="Widget.Button">
    <item name="background">@drawable/btn_default</item>
    <item name="focusable">true</item>
    <item name="clickable">true</item>
    <item name="textAppearance">?attr/textAppearanceSmallInverse</item>
    <item name="textColor">@color/primary_text_light</item>
    <item name="gravity">center_vertical|center_horizontal</item>
</style>

Holo_Theme:

D:\workTools\sdk\platforms\android-25\data\res\values\themes_holo.xml, 有如下:

    <!-- Button styles -->
    <item name="buttonStyle">@style/Widget.Holo.Button</item>

D:\workTools\sdk\platforms\android-25\data\res\values\styles_holo.xml, 有如下:

    <!--可看到继承自:Widget.Button-->
    <style name="Widget.Holo.Button" parent="Widget.Button">
        <item name="background">@drawable/btn_default_holo_dark</item>
        <item name="textAppearance">?attr/textAppearanceMedium</item>
        <item name="textColor">@color/primary_text_holo_dark</item>
        <item name="minHeight">48dip</item>
        <item name="minWidth">64dip</item>
     </style>

** 参考系统的实现,如果我们自定义的View,的自定义属性非常多,也可以去提供默认的style,然后让用户去设置到theme里面即可**

自定义View的构造方法##

  1. 第一种:
  public DivideTextView(Context context) {
        this(context, null);
  }
  public DivideTextView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
 }
 public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
  1. 第二种:
 public DivideTextView(Context context) {
       super(context);
       init();
 }
 public DivideTextView(Context context, AttributeSet attrs) {
       super(context, attrs);
       init();
}
public DivideTextView(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
       init();
}
private void init() {
  
}

使用场景的区别:

  1. 如果需要设置 obtainStyledAttributes 的第3个参数,‘defStyleAttr’,使用第一种方式,没有这个需求,2种没区别;
  2. 继承系统已有控件去定义View时,使用第一种方式,会覆盖掉系统的在theme里面设置的style,如上面的 button的构造函数的第3个参数,这样的话,使用第二种,会继续使用系统提供的style,如果非要用第一种,也可以,将系统的 defStyleAttr copy进来即可,如下示例代码:
 public ClearEditText(Context context) {
        super(context);
        this(context, null);
    }
    public ClearEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
         // 注意这里,将系统的样式拿过来,如果传递0,将丢失样式,切记!!!
        this(context, attrs, android.R.attr.editTextStyle); 
    }
    public ClearEditText(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

获取自定义属性的2种写法##

  1. 第一种方式,如上:
// 这种形式
setDivideDrawable(a.getDrawable(R.styleable.DivideTextView_dt_drawable));

2.第二种形式:

       // 第二种获取自定义属性的方式,通过循环遍历所有属性:
        final int n = a.getIndexCount();
        for (int i = 0; i < n; i++) {
            if (i == R.styleable.DivideTextView_dt_drawable) {
            } else if (i == R.styleable.DivideTextView_dt_divideGravity) {
                setDivideDrawable(a.getDrawable(i));
            } else if (i == R.styleable.DivideTextView_dt_dividerSize) {
                setDivideGravityInner(a.getInt(i, NONE));
            } else if (i == R.styleable.DivideTextView_dt_dividerPadding) {
                setDividePadding((int) a.getDimension(i, 0f));
            }
        }

区别

第一种写法,不管有没有在布局文件中使用该属性,都会执行getXXX方法赋值;
第二种写法,只有在布局文件中,设置该属性后,才去调用getXXX方法(indexCount);
注意
父view已经对某个属性赋值了,如果采用第一种方式,可能用户根本没有设置值,但是会执行getXXX方法,可能就把父View的赋值给覆盖了;

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容