美文网首页
你真的了解Android View构造函数么

你真的了解Android View构造函数么

作者: f6876752a23f | 来源:发表于2017-04-10 13:50 被阅读98次

    译自:http://blog.danlew.net/2016/07/19/a-deep-dive-into-android-view-constructors/

    曾几何时,我经常对Android View构造函数感到疑惑。为什么有四个构造函数?每个参数做什么?我需要实现哪些构造函数?

    TL,DR:

    如果你想要快速实用的建议,只需关注以下几点:

           1、使用 单参构造方法 View(Context)在代码中创建Views。

           2、当需要从Xml挂载视图Views的时候重写View(Context, AttributeSet)。

           3、忽略其他你不需要的部分。

    注意了,还在车上的人,拉好扶手,开车了。

    Constructor parameters 构造函数参数

    构造函数最多有四个参数,简单的说,他们就是:

          Context  - 用于视图中的所有位置(推荐持有Activity context);

          AttributeSet  - Xml属性(挂载xml视图);

          int defStyleAttr - 应用于View的默认样式(定义于Theme中);

          int defStyleResource - 如果defStyleAttr未使用,则应用于View的默认样式;

    除了Context ,其他的参数仅用于根据Xml属性(布局、样式。主题)配置视图的初始状态。

    Attributes 属性

    我们先来谈谈如何定义有效的Xml属性。看XML中ImageView 基本的属性:

    <ImageView 

    android:layout_width="wrap_content"

    android:layout_height="wrap_content"

    android:src="@drawable/icon"/>

    有没有想过 layout_width 、layout_height、src从何而来?当然最开始我是没有想过的,反正拿起来用就行。后来发现要耍女朋友就要买车房,MD,家不在雄安,要来钱就只有提高自己了,扯淡了。当然那些属性不是凭空而来,它实际上是系统通过处理<declare-styleable>来声明的。例如:src 就是这样定义的:

    <declare-styleable name = "ImageView">

         <attr name = "src"  format="reference|color"/>

         <!-- ...snipped for brevity...-->

    </declare-styleable>

    每个声明样式都会为每个属性生成一个R.styleable.[name] 和一个R.styleable.[name]_[attribute]。比如, 上面的src样式 就生成R.styleable.ImageView 和 R.styleable.ImageView_src。

    当然那R.styleable.[name] 和一个R.styleable.[name]_[attribute] 都是什么玩意呢?注意了,那就是:R.styleable.[name]就是所有属性资源的数组,系统用它来查找属性值。而每个一个R.styleable.[name]_[attribute]只是该数组中的一个索引,因此,你可以一次性检索所有的属性,然后单独查找每个值。当然你也可以把它想象成一个 cursor,R.styleable.[name] 作为查询列,而R.styleable.[name]_[attribute]就是列索引。

    关于更多的declare-styleable ,参见官方文档  the official documentation

    AttributeSet 属性集

    我们上面写的Xml被赋予给View作为AttributeSet 属性集。

    通常我们不直接访问AttributeSet,而是使用Theme.obtainStyledAttributes()。这是因为原始属性通常需要解析引用并应用样式。例如,如果你在XML中定义style = @ style / MyStyle,则此方法可解析MyStyle,并将其属性添加到属性集中。最后,我们可以通过getsStyledAttributes()返回一个TypedArray,用它来访问相应属性。

    不喜欢看文字,哥也不喜欢,上代码:

    public ImageView(Context context, AttributeSet attrs) {

    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ImageView, 0, 0);

    Drawable src = ta.getDrawable(R.styleable.ImageView_src);

    setImageDrawable(src);

    ta.recycle();

    }

    在这种情况下,我们传递两个参数来获取StateedAttributes()。第一个是AttributeSet attrs,来自XML的属性。第二个是数组R.styleable.ImageView,它告诉方法我们要提取哪些属性(以什么顺序)。

    如上,我们取得返回的TypedArray,我们现在可以访问各个属性。我们需要使用R.styleable.ImageView_src,以便我们正确地索引数组中的属性。

    (回收TypedArray也是非常重要的! 非常重要! 非常重要!  ,3遍了哈,如上,我可是加上了的。。。)

    通常您可以一次提取多个属性。事实上,ImageView实现比上面我们看见的复杂得多(因为ImageView本身有更多的需要关注的属性)。

    想了解更多,参见官方文档 the official documentation

    Theme Attributes 主题属性

    旁注,为了完整性:在最后一节中使用 getsStyledAttributes()时,AttributeSet 不是我们获取值的唯一的地方。属性也可以存在于主题中。

    对于View的挂载,这个很少起到作用。因为你的主题不应该像src那样设置属性,但是如果您使用getsStyledAttributes()来检索主题属性(这是有用的,但不属于本文的范围),则可以发挥作用。

    Default Style Attribute 默认样式属性

    你可能已经注意到我在getsStyledAttributes()中为最后两个参数传入了0。它们实际上是两个资源引用 -  defStyleAttr和defStyleRes。我将重点关注第一个,也就是defStyleAttr。

    到目前为止,defStyleAttr是getsStyledAttributes()最令人困惑的参数。根据文档说明:

    An attribute in the current theme that contains a reference to a style resource that supplies defaults values for the TypedArray.

    wow,真绕口。还是讲人话吧,这是一种能够为某种类型的所有视图定义基本样式的方式。例如,如果要一次修改所有应用程序的TextView,您可以在主题中设置textViewStyle。如果不这样做,那么您必须手动为每个TextView设置样式。

    这里以TextView为例,介绍实际工作原理。

    首先,它是一个属性(在这种情况下为R.attr.textViewStyle)。这里是Android平台定义textViewStyle的地方:

    <resources>

        <declare-styleable name="Theme">

               <!-- ...snip... -->

               <!-- Default TextView style.-->

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

               <!-- ...etc... -->

      </declare-styleable>

    </resources>

    再次,我们使用declare-styleable,但这次是定义可以存在于主题中的属性。在这里,我们说textViewStyle是一个引用 - 也就是说,它的值只是一个对资源的引用。在这种情况下,它应该是一种风格的引用。(怎么感觉拗口,是时候多看看书学习下了......)

    接下来我们必须在当前主题中设置textViewStyle。默认的Android主题如下所示:

    <resources>

           <declare-styleable name="Theme">

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

          </declare-styleable >

    </resources>

    然后,您的Application或activity必须为主题设置,通常通过清单:

    <activity

    android:name=".MyActivity"

    android:theme="@style/Theme" />

    现在我们可以在getsStyledAttributes()中使用它:

    TypedArray ta = theme.obtainStyledAttributes(attrs, R.styleable.TextView, R.attr.textViewStyle, 0);

    最终结果是:没有由AttributeSet定义的任何属性的都使用textViewStyle引用的样式来填充。

    除非你是核心人物,否则你不需要知道所有这些实现细节。它之所以在就是方便Android框架在主题中定义各种视图的基本样式。

    Default Style Resource 默认风格资源

    defStyleRes比其兄弟姐妹更简单。它只是一种风格资源(即@ style / Widget.TextView)。

    defStyleRes中样式的属性只有在defStyleAttr未定义(0或未在主题中设置)时才会应用。

    Precedence 优先

    现在我们已经有一堆通过getsStyledAttributes()来获取属性的值方法。这是他们的优先顺序,从最高到最低:

    1、AttributeSet中定义的任何值。

    2、AttributeSet中定义的样式资源(即style = @ style / blah)。

    3、由defStyleAttr指定的默认样式属性。

    4、由defStyleResource指定的默认样式资源(如果没有指定defStyleAttr)。

    5、主题中设置的值

    换言之,您在xml中直接设置的任何属性将首先使用。但是如果你不自己设置的话,这些属性可以从其他地方检索到。

    View constructors 构造函数

    这篇文章应该是关于View构造函数的,对吧?

    共有四个,每个增加一个参数:

    View(Context)

    View(Context, AttributeSet)

    View(Context, AttributeSet, defStyleAttr)

    View(Context, AttributeSet, defStyleAttr, defStyleRes)

    一个重要的注意事项:最后一个添加到API 21中,所以除非minSdkVersion == 21,否则现在应该避免它。 (如果你想使用defStyleRes,只要自己调用getsStyledAttributes(),因为它始终被支持。)

    他们级联,所以如果你调用一个,你最终调用他们所有(通过super)。级联也意味着你只需要重写你使用的构造函数。一般来说,这意味着你只需要实现前两个(一个用于代码构造函数,另一个用于挂载XML视图)。

    自定义view,通常如下:

    MyView(Context c){

    this(context, null);

    }

    MyView(Context  c, AttributeSet attrs){

    super(c, attrs);

    //相关处理

    }

    在双参数构造函数中,您可以以任何姿势使用obtainStyledAttributes()。实现默认样式的一种快速方法是只向其提供defStyleRes;这样你就不需要经过在对接defstyleattr的繁琐。

    无论如何,我希望这不仅有助于您了解视图构造函数,而且还可以在视图构建过程中检索属性!

    坐到这里的都是真爱啊! 下车了,老司机!

    相关文章

      网友评论

          本文标题:你真的了解Android View构造函数么

          本文链接:https://www.haomeiwen.com/subject/ccvfattx.html