• android: View的生命周期


    今天看到了一篇不错的文章,是一位外国小哥写的,个人觉得不错,遂翻译之,英文好的同学可以直接移步 ——> 生肉: https://proandroiddev.com/the-life-cycle-of-a-view-in-android-6a2c4665b95e

    概述

    当我们查看一款App的时候,首先引起我们注意的就是屏幕上显示的内容,而屏幕上显示的内容就是 View 。

    View是UI界面的基本构建块,它占据了一块矩形区域,负责绘图和事件处理。View同时也是android上其它UI控件的基类,可以用来创建其它交互式的UI组件(比如Button, TextView, 等等)。View的子类 ViewGroup 则是布局的基类,ViewGroup是一个不可见的容器,用于容纳其它的View(或其它的ViewGroup),而且还定义了相关的布局属性。

    下图描述了android上的View与其它UI组件之间的关系:

    View的生命周期

    每个Activity 都有自己的生命周期,同样,View也有自己的生命周期。在屏幕上渲染的View必须经历这些生命周期方法才能正确地在屏幕上绘制。这些生命周期方法的每一种都有其重要性。下面我们来深入了解下View的生命周期:

    构造函数

    通常,我们对于View为什么有四种类型的构造函数感到困惑:

    View(Context context) 
    View(Context context, @Nullable AttributeSet attrs) 
    View(Context context, @Nullable AttributeSet attrs, int defStyleAttr) 
    View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)

    View(Context context)

    当从代码动态的创建View时就会用到这个简易的构造方法。这里的参数 context 是运行视图的上下文,通过它可以访问到当前的主题(theme), 资源(resources)等东西。

    View(Context context, @Nullable AttributeSet attrs)

    从XML布局里加载View时调用的构造方法。当从XML文件里创建View同时也为这个View指定了一些相应的属性时,就会调用此方法。这个版本的构造函数使用的默认样式是0,因此唯一应用的属性值是上下文主题和给定AttrubiteSet中的属性值。

    View(Context context, @Nullable AttributeSet attrs, int defStyleAttr)

    从XML文件执行加载并从主题属性(defStyleAttr) 中应用基本样式。View的这个构造方法允许View在加载时使用它自己定义的基本样式。比如,系统Button类的构造函数就调用了这个构造函数,并且为  defStyleAttr  这个参数提供 R.attr.buttonStyle 的样式属性;这样可以使得系统提供的按钮主题样式可以修改所有的View(特别是View的背景),以及Button类的样式属性。

    参数  defStyleAttr 是当前主题的一个属性,其中包含了对样式资源的引用,这个样式资源为View的属性提供了一个默认值,不查找默认值可以将其设置为0。

    View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)

    从XML文件执行加载,并从主题属性或样式资源中应用特定于类的基本样式。View的这个构造方法允许View在加载时使用它自己的基本样式,与上个构造方法类似。

    参数 defStyleRes 是样式资源的资源标识符,为View提供默认值,仅当 defStyleAttr 为0或在主题中找不到时使用。如果不查找默认值,则可以为0。

    View的生命周期主要由三部分组成:

    Attachment / Detachment (关联/分离)

    Traversals(遍历)

    State Save / Restore (状态保存/ 恢复)

    一. Attachment / Detachment

    这是将View关联到窗口(Window)或从窗口分离时的阶段。在此阶段,我们有一些方法可以接收回调来执行适当的操作。

    ● onAttachedToWindow()

    当View关联到窗口时调用。在这个阶段,View知道它处于活动的状态并且具有可绘制的表面。因此,我们在此阶段就可以开始分配任何资源或设置listeners。

    ● onDetachedFromWindow()

    当View与窗口分离时将调用此方法。此时,View不再具有可绘制的表面。在此阶段,你需要停止任何已执行的任务或清理任何已分配的资源。当我们在ViewGroup类上调用 removeView() 或者Activity被销毁时将调用此方法。

    ● onFinishInflate()

    当布局加载器(LayoutInflater)从XML文件里完成了所有子View的加载时,将会调用此方法。

    二. Traversals

    之所以把此阶段称为“遍历”,是因为View的视图层次就像是从父节点(ViewGroup)到子节点的树状结构。因此,每个方法都是从父节点开始,一直遍历执行到最后一个节点:

     测量(Measure)阶段和布局(Layout)阶段始终是一起发生。如上图所示,这是一个顺序的过程。

    ● onMeasure()

     这一步用于测量View到底应该有多大。如果是ViewGroup,它会对它的每一个子View都调用测量,测量的结果可以帮助ViewGroup确定其自身的大小。

    onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    @param widthMeasureSpec Horizontal space requirements as imposed by the parent
    @param heightMeasureSpec Vertical space requirements as imposed by the parent

     onMeasure() 没有返回值,由系统调用。

     setMeasuredDimension() 用于显式地设置宽度(width)和高度(height)值。

    ● MeasureSpec

     MeasureSpec 类封装了从父View传递到子View的布局要求。每个 MeasureSpec 代表对宽度或高度的要求 。 MeasureSpec 由大小(size)和模式(mode)组成,有三种可能的模式:

    MeasureSpec.EXACTLY : 父View已经为View确定了一个确切的尺寸。不管子View有多大,都会给子View一个最大限制。这个属性给View设置了一个固定的宽度(比如为LinearLayout指定weight, 或者是为View指定match_parent属性)。

    MeasureSpec.AT_MOST:  子View可以根据需要变化到它想要的大小。

    MeasureSpec.UNSPECIFIED: 父View没有对子View添加任何约束,子View可以是任意大小。

    ● onLayout()

    View在调用 onMeasure() 完成测量后即会调用此方法,目的是在于确定View在屏幕上的位置。

    ●  onDraw()

    尺寸(通过onMeasure)和位置(通过onLayout())已经由前面的步骤计算出来了,因此View在这个时候已经可以根据上述的尺寸和位置绘制自身。在 onDraw(Canvas canvas)  中,生成或更新的Canvas对象具有要发送到GPU的 OpenGL- ES命令列表(displayList)。

    注意:切勿在onDraw()方法中创建对象,因为这个方法会被多次调用

    当特定的View的属性发生变化时,还有另外两个方法可以被使用,那就是:

    ● invalidate()

    invalidate()方法用于强制重绘我们希望显示更改的特定View。 简单的说,如果View的外观发生了变化,而我们又想看到这些变化,就可以调用invalite()。

    ● requestLayout()

    在某些情况下,View的状态会发生变化, requestLayout() 可以向android的视图系统发出信号,告诉系统,View需要重新执行自身的测量和布局阶段(onMeasure()->onLayout()->onDraw())。简单的说,当View的范围发生变化时,我们可以调用此函数。

    注意:在View上调用任何方法时,必须在UI线程上,如果你正在其它线程上工作,并且想要从该线程更新View的状态,则应使用Handler。

    三. State Save / Restore

    ●  onSaveInstanceState()

    要保存视图状态,首先需要为其提供一个ID。如果你的视图层级结构中有多个具有相同ID的View, 则将保存所有的状态,为了避免这种情况,最好为View指定一个唯一的ID。

    其次,你需要一个类来继承 View.BaseSavedState  , 然后保存其属性。为了更好的理解,下面举例说明:

    ●  onRestoreInstanceState(Parcelable state)

    在这里我们需要重写此方法,并从Parcelable 读取数据,然后根据Parcelable可用的数据编写逻辑。

    @Override
    public Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        // The vars you want to save - in this instance a string and a boolean
        String someString = "something";
        boolean someBoolean = true;
        State state = new State(super.onSaveInstanceState(), someString, someBoolean);
        bundle.putParcelable(State.STATE, state);
        return bundle;
    }
    
    @Override
    public void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) {
            Bundle bundle = (Bundle) state;
            State customViewState = (State) bundle.getParcelable(State.STATE);
            // The vars you saved - do whatever you want with them
            String someString = customViewState.getText();
            boolean someBoolean = customViewState.isSomethingShowing());
            super.onRestoreInstanceState(customViewState.getSuperState());
            return;
        }
        // Stops a bug with the wrong state being passed to the super
        super.onRestoreInstanceState(BaseSavedState.EMPTY_STATE); 
    }
    
    protected static class State extends BaseSavedState {
        protected static final String STATE = "YourCustomView.STATE";
    
        private final String someText;
        private final boolean somethingShowing;
    
        public State(Parcelable superState, String someText, boolean somethingShowing) {
            super(superState);
            this.someText = someText;
            this.somethingShowing = somethingShowing;
        }
    
        public String getText(){
            return this.someText;
        }
    
        public boolean isSomethingShowing(){
            return this.somethingShowing;
        }
    }

    <END>

  • 相关阅读:
    jhat:虚拟机堆转储快照分析工具
    jmap:java内存影像工具
    jinfo:Java配置信息工具
    jstat:虚拟机统计信息监视工具
    jps:虚拟机进程状况工具
    jdk工具总领
    到底是"/"还是"/*"
    jdk_keytool
    html地图定位
    java 手机号码归属地查询
  • 原文地址:https://www.cnblogs.com/yongdaimi/p/13608937.html
Copyright © 2020-2023  润新知