那四个构造函数都以系统会调用的法门,② 、在 View 的构造方法中获取我们自定义的属性澳门金冠网站主页

笔者们自定义控件的时候,最普遍的内需重写构造函数是
public View(Context context) {}
public View(Context context, @Nullable AttributeSet attrs) {},
因为第③个是在 Java 代码中创设 View 时会调用,第1是在 XML
布局文件中引用 View
时,会被调用。那四个构造函数都以系统会调用的法门。不过,还剩八个构造函数“
public View(Context context, @Nullable AttributeSet attrs, int
defStyleAttr) {}public View(Context context, @Nullable AttributeSet
attrs, int defStyleAttr, int defStyleRes) {}`,
是亟需大家机关调用的,系统并不会调用,当大家须要为自定义 View
设置暗中同意的品质的时候,就需求动用了。


那是一段典型的一段自定义 View 的构造函数。

自定义 View首要步骤:

1、自定义View的属性
贰 、在 View 的构造方法中拿走大家自定义的属性
3、重写 onMesure()
4、重写 onDraw()

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

    public AttrTestTextView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.defaultStyleAttr);
    }

    public AttrTestTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, R.style.DefaultStyleRes);
    }

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

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTestTextView,
                defStyleAttr, defStyleRes);

        ...

        typedArray.recycle();
    }
}
1、自定义 View 的属性

能够看出来,属性的得到首要由 context.obtainStyledAttributes
决定,而结尾五个参数defStyleAttrdefStyleRes操纵了最终暗中认可的天性样式是怎么的。

1. 编制 values/attrs.xml,在里边编写 styleable 和 item 等标签成分,format
<resources>
    <declare-styleable name="Test">
        <attr name="text" format="string"/>
        <attr name="num" format="integer"/>
        <attr name="string" format="string" />
    </declare-styleable>
</resources>

为了澄清楚暗中同意样式的周转规律,我们先为这一个自定义 View 创建 多个自定义属性,和1个独自的习性,attrs.xml 文件如下:

贰 、在 View 的构造方法中获得我们自定义的习性
<resources>
    <declare-styleable name="AttrTestTextView">
        <attr name="firstAttr" format="string" />
        <attr name="secondAttr" format="string" />
        <attr name="thirdAttr" format="string" />
        <attr name="fourthAttr" format="string" />
        <attr name="fifthAttr" format="string" />
    </declare-styleable>

    <attr name="defaultStyleAttr" format="reference" />
</resources
1. 自定义三个TestView(extends View )类,平常要求重写多少个构造方法,第一个必须重写。
public class TestView extends View{

    // 在代码中 new TestView(context) 时调用
    public TestView(Context context) {
        this(context,null);
    }

    // 加载布局文件中的 TestView 时调用
    public TestView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    // 一般通过第二个构造方法调用
    public TestView(Context context, AttributeSet attrs, int def) {
        super(context, attrs, def);
    }
}

设置 5 性子情的案由是 Android 的 View 属性覆盖的预先级有 5
处,分别是:

2. 在布局文件中 TestView 使用自定义的质量(注意namespace),
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.example.mytestview.TestView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        view:num="520">
    </com.example.mytestview.TestView>

</LinearLayout>
  1. 布局文件中的直接钦赐的本性
  2. 布局文件中的 style 中钦定的性质
  3. 笔者们友好定义的 Style
  4. Theme 中向来钦赐的 Style 引用
  5. Theme 中一贯钦赐的习性
3. 在 TestView 的构造方法中通过 TypedArray 获取属性,
public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    Log.i(TAG, "use TypedArray");
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Test);
    String text = ta.getString(R.styleable.Test_text);
    int num = ta.getInt(R.styleable.Test_num, 1);
    String string = ta.getString(R.styleable.Test_string);
    Log.i(TAG, "count:" + ta.getIndexCount());
    Log.i(TAG, "text:" + text + "  num:" + num + " string:" + string);
    ta.recycle();

    Log.i(TAG, "use attrs");
    int count = attrs.getAttributeCount();
    Log.i(TAG, "count:" + count);
    for (int i=0; i<count; i++) {
        Log.i(TAG, "attrName:" + attrs.getAttributeName(i) + " attrValue:" + attrs.getAttributeValue(i));
    }
}

// 运行结果
use TypedArray
count:1    // 只获取到 num
text:null  num:520 string:null
use attrs
count:3
attrName:layout_width attrValue:-2
attrName:layout_height attrValue:-2
attrName:num attrValue:520

能够观望,构造方法中流传的 AttributeSet 参数保存的是布局文件中 TestView
中宣示的全体属性,而 TypedArrary
获取的是布局文件中扬言的且是自定义的质量。通过 TypedArrary 和
AttributeSet
都足以得到自定义的性质,但依据运维结果能够见见,假设在安装自定义属性值是向来设置的(view:num=”520″),TypedArrary
和 AttributeSet
的输出结果是一模一样的,而要是是援引有个别财富的话(view:text=”@string/app_name”),TypedArrary
输出了能源对应的值,而 AttributeSet 只可以输出财富 id ,还亟需大家依据资源id 获取相应的值,而 TypedArrary
能够活动解析能源,简化了大家的操作,尤其有利。

透过 AttributeSet获取最后的值的历程:

// 2 为 text 属性在 attrs 数组的下标
int textId =  attrs.getAttributeResourceValue(2, -1);
Log.i(TAG, "text= "+getResources().getString(textId));

着力的概念和得到就已经说完了,下边详细说说设置自定义属性值,关于自定义属性的拿走,大家第二是调用了
context.obtainStyledAttributes()
这么些函数,大家来看一下以此函数的源码完结:

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

通过对源码的追踪,咱们发现 context 的七个参数的 obtainStyledAttributes
方法最后是调用了 Theme 的 4 个参数的 obtainStyledAttributes
方法。大家来看一下以此函数的源码完成:

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

Theme 的 obtainStyledAttributes 方法的多个参数分别为 :

  • AttributeSet set:属性值的聚集。
  • int[] attrs:大家自定义属性集合在 大切诺基 类中生成的 int
    型数组,这么些数组中富含了自定义属性的财富ID。
  • int defStyleAttr:那是现阶段 Theme 中的包含的2个针对性 style
    的引用,当大家从不给自定义 View 设置 declare-styleable
    能源聚合时,私下认可从那几个集合里面查找布局文件中布置属性值,传入 0
    表示不向该 defStyleAttr 中查找暗中同意值。
  • int defStyleRes:那些也是二个针对性 Style 的财富 ID,不过仅在
    defStyleAttr 为 0 可能 defStyleAttr 不为 0 但 Theme 中尚无为
    defStyleAttr 属性赋值时起效果。

在地方得到自定义属性时,我们是通过在布局文件中经过增添命名空间(xmlns:view=”http://schemas.android.com/apk/res-auto“)来直接赋值的(view:num=”520″)。咱俩还足以在
style 中为属性赋值
,首先在 values/styles.xml 中添加自定义的 style,

<style name="Test">
    <item name="text">hello world</item>
    <item name="num">200</item>
</style>

下一场修改布局文件,

<com.example.mytestview.TestView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    view:num="520"
    style="@style/Test">   // 指定 style
</com.example.mytestview.TestView>

运作结果:

text:hello world  num:520 string:null

在自定义 style 中为自定义属性 text 和 num 进行的赋值,然后在布局文件中对
num(view:num=”520″) 再度赋值,能够发现在 style
的赋值成功了,且在布局文件中赋值的优先级更高。
还要,还须要专注的是,只借使 layout 布局文件中,无论是通过 namespace
直接为属性赋值,依旧经过 style
为属性赋值,在构造函数获取时都对应了getTheme().obtainStyledAttributes方法中的第2个参数
@StyleableRes int[] attrs。

通过 defStyleAttr 赋值,defStyleAttr 参数的意趣是:

  • 原文: An attribute in the current theme that contains areference to
    a style resource that supplies defaults values for the TypedArray.
    Can be 0 to not look for defaults.
  • 翻译: 这是现阶段 Theme 中的包括的三个针对性 style
    的引用,当大家从不给自定义 View 设置 declare-styleable
    财富聚合时,私下认可从这些集合里面查找布局文件中配备属性值,传入 0
    表示不向该 defStyleAttr 中查找默许值。

首先, 先声美赞臣(Meadjohnson)个 refrence 格式的属性, 用于表示 style
的引用。在前边的res/values/MyView.xml 文件里即可:

<attr name="MyViewDefStyleAttr" format="reference"/>

接下来,必要到 AndroidManifest.xml 中查看包涵该自定义 View 的 Activity
所使用的核心,

<android:theme="@style/AppTheme">

终极,在 style.xml中 的 AppTheme 大旨下扩展 MyViewDefStyleAttr
的引用达成。

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="MyViewDefStyleAttr">@style/MyViewDefStyleAttrImpl</item>
</style>

<style name="MyViewDefStyleAttrImpl">
    <item name="text">defStyleattr_text</item>
    <item name="num">300</item>
    <item name="string">defStyleattr_string</item>
</style>

若是要运用 obtainStyledAttributes
方法的第多个参数,就需求在第九个构造函数中显得的调用 getTheme() 的
obtainStyledAttributes 方法。

public TestView(Context context, AttributeSet attrs) {
    // 为defStyleAttr进行赋值
    this(context, attrs, R.attr.MyViewDefStyleAttr);
}

public TestView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.Test);
    TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, 
                    R.styleable.Test, defStyleAttr, 0);
    String text = ta.getString(R.styleable.Test_text);
    int num = ta.getInt(R.styleable.Test_num, 1);
    String string = ta.getString(R.styleable.Test_string);
    Log.i(TAG, "text:" + text + "  num:" + num + " string:" + string);
    ta.recycle();
}

运维结果:

text:hello world  num:520 string:defStyleattr_string

从结果能够见到,在大旨中钦点 style
引用的优先级是稍差于在布局文件中央直机关接赋值和利用 style 字段的。
再正是,大家还供给掌握一些:
在Android系统中的控件,很多都在构造参数中利用了第八个参数,例如
Button。那样做的便宜是:
当我们切换差别的宗旨时,Button的样式也能跟着进行变更

通过 defStyleRes 赋值,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的能源 ID,可是仅在 defStyleAttr 为 0 只怕
    defStyleAttr 不为 0 但 Theme 中从未为 defStyleAtt
    r属性赋值时起功用。

通过翻译,我们得以肯定两点:

  • defStyleRes 指向一个 style 引用。
  • defStyleRes 的先行级低于 defStyleAttr。

为了求证,我们先在概念一个style:

<style name="MyViewDefStyleRes">
    <item name="text">MyViewDefStyleRes_text</item>
    <item name="num">300</item>
    <item name="string">MyViewDefStyleRes_string</item>
</style>

接下来将自定义 View 的第3个构造函数的 Rubicon.attr.MyViewDefStyleAttr 改回 0 ,

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

末了为defStyleRes进行赋值,

TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, 
            R.styleable.Test,defStyleAttr, R.style.MyViewDefStyleRes);

运行结果:

text:hello world  num:520 string:MyViewDefStyleRes_string

品质赋值优先级次序表:
在布局文件中一向定义 > 在布局文件中通过 style 定义 > 自定义 View
所在的 Activity 的 Theme 中钦赐 style 引用 > 构造函数中 defStyleRes
钦定的暗中认可值。

她们之间的遮盖的事先级是:

1 > 2 > 3 / 4 > 5

为了印证我们的结论,首先在Application 或 Activity 的 Theme
中央直机关接设置那四个属性,并且为上方定义的 defaultStyleAttr
属性设置3个style的引用,值为世间定义的 @style/DefaultStyleInAppTheme,
而那脾本性将用作自定义View构造函数中的第⑩个值,即该自定义 View 暗许的
style。DefaultStyleRes 则作为自定义 View 的构造函数中的第多个参数,而
LayoutStyle 则在布局中一向引用, styles.xml 文件如下:

<resources>

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <item name="firstAttr">firstAttr from theme attr</item>
        <item name="secondAttr">secondAttr from theme attr</item>
        <item name="thirdAttr">thirdAttr from theme attr</item>
        <item name="fourthAttr">fourthAttr from theme attr</item>
        <item name="fifthAttr">fifthAttr from theme attr</item>

        <item name="defaultStyleAttr">@style/DefaultStyleInAppTheme</item>
    </style>

    <style name="DefaultStyleInAppTheme">
        <item name="firstAttr">firstAttr from theme style</item>
        <item name="secondAttr">secondAttr from theme style</item>
        <item name="thirdAttr">thirdAttr from theme style</item>
    </style>

    <style name="DefaultStyleRes">
        <item name="firstAttr">firstAttr from style res</item>
        <item name="secondAttr">secondAttr from style res</item>
        <item name="thirdAttr">thirdAttr from style res</item>
        <item name="fourthAttr">fourthAttr from style res</item>
    </style>

    <style name="LayoutStyle">
        <item name="firstAttr">firstAttr from layout style</item>
        <item name="secondAttr">secondAttr from layout style</item>
    </style>

</resources

下一场大家自定义二个 TextView, 分别获得那 5 个属性的值,并且展现出来。

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

    public AttrTestTextView(Context context, AttributeSet attrs) {
        this(context, attrs, R.attr.defaultStyleAttr);
    }

    public AttrTestTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr, R.style.DefaultStyleRes);
    }

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

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.AttrTestTextView,
                defStyleAttr, defStyleRes);

        StringBuilder sb = new StringBuilder();
        sb.append(typedArray.getString(R.styleable.AttrTestTextView_firstAttr)).append("\n");
        sb.append(typedArray.getString(R.styleable.AttrTestTextView_secondAttr)).append("\n");
        sb.append(typedArray.getString(R.styleable.AttrTestTextView_thirdAttr)).append("\n");
        sb.append(typedArray.getString(R.styleable.AttrTestTextView_fourthAttr)).append("\n");
        sb.append(typedArray.getString(R.styleable.AttrTestTextView_fifthAttr));

        setText(sb);

        typedArray.recycle();
    }
}

大家领略,属性的拿走末了是基于 obtainStyledAttributes()
方法获得,他的最终多少个参数约等于协会行数的终极多个参数,那四个参数都以设置自定义
View 的暗中同意样式,那么,他们作用的先行级是何许的?

* @param 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.

据书上说 API 文书档案的能够看到,当 defStyleAttr 参数被安装为 0, 或许在
Application 和 Activity 的 Theme 中找不到 defStyleAttr
属性的赋值(例如 styles.xml 中的
<item name="defaultStyleAttr">@style/DefaultStyleInAppTheme</item>
这行代码被删去),那么,那几个 View 的默许样式正是 defStyleRes
参数所引述的习性样式。能够看来那八个参数是抵触的,而且争辩级别是全方位
style, 即 defStyleAttr 参数的 style 能找到状态下就大意了全副
defStyleRes 的属性集。

然后,在布局文件中添加自定义的 View,分别安装了第贰手的属性,firstAttr
和 设置了 styles.xml 中定义的 style。

<RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <com.ryeeeeee.attrtest.AttrTestTextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:firstAttr="firstAttr from layout attr"
            style="@style/LayoutStyle"/>

</RelativeLayout>

运维结果如下图,可以见到 defStyleRes 所引用的 style
中的属性完全被忽略了,因为 styles.xml 中能找到 defStyleAttr
那些特性的赋值:

WechatIMG1.png

现在将 styles.xml 中能找到 defStyleAttr 那几个性格的赋值删除,恐怕将
defStyleAttr 参数使用 0 赋值, 运营结果如下,能够见到 defStyleRes
所引用的style 才显得出来:

WechatIMG2.png

试行结果申明,属性层级的覆盖优先级是
1 > 2 > 3 / 4 > 5,和下面结论一致。须求注意的是,在 style 层级的先行级
defStyleAttr 参数所引述的 style 的预先级是大于 defStyleRes 的。

相关文章