# View的生命周期

*2016年3月30日 偶尔的坚持比科学更重要*

最近常常用到自定义的View和ViewGroup，且发现在自定义时总是出现一些莫名奇妙的问题，其大部分的原因可以归咎于View的生命周期。

View相当于一个Panel，所有的UI组件都继承了View。为了了解他，让我们先看看他的一些方法。

### View常被重写的方法

void onFinishInflate()：当应用程序通过XML布局文件加载该组件从而进行构建界面时，该方法将会被调用

onMeasure(int widthMeasureSpec, int heightMeasureSpec)：用来检测View组件和其子组件的大小,其参数为为该View设置的长和宽

onLayout(boolean changed, int left, int top, int right, int bottom) ：用于分配view在父控件的位置，即给view布局

onSizeChanged(int w, int h, int oldw, int oldh)：当该组件的大小被改变时，调用该方法

onDraw(Canvas canvas)：用于绘制组件

onKeyDown(int keyCode, KeyEvent event)：当有按键被按下时出发该方法（可以监听返回键，home键）

onKeyUp(int keyCode, KeyEvent event)：当有按键被松开时出发该方法

onTouchEvent(MotionEvent event)：当发生触摸屏事件时触发该方法

onAttachedToWindow()：当将这个组件放入某个窗口时调用该方法

onDetachedFromWindow()：当将这个组件从某个窗口分离时调用这个方法

onWindowVisibilityChanged(int visibility)：当包含该组件的窗口的可见性发生改变时调用该方法

onVisibilityChanged(View changedView, int visibility)：当该view的是否可见发生变化时，调用该方法

onWindowVisibilityChanged和onVisibilityChanged中visibility参数的三个值： visible 0 代表view可见，是默认值 invisible 4 代表组件不可见，但保留控件的布局位置 gone 8 代表组件隐藏，不保留控件的布局位置，如果组建的visibility属性为gone，则代表该组件生命周期结束

### 测试View的生命周期

简单的介绍了这些方法之后，让我们再来看这些这些方法什么时候会被调用。

下面是我的测试代码，还是有必要贴出来的：

```java
public class MyView extends View {
    public MyView(Context context) {
        super(context);
        Log.i("infoview", "==========MyView(context)");
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Log.i("infoview", "MyView(context,attrs)" + attrs.getAttributeName(0)
                + "===" + attrs.getAttributeName(2));
    }

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

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        Log.i("infoview", ==========onSizeChanged"
                + "===w:" + w + "===h" + h +"===oldw" + oldw + "===oldh" + oldh);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.i("infoview", ==========onLayout"
        + "===changed:" + changed +"====l t r b:" + left +"---"+ top +"---"+ right +"---"+ bottom);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.i("infoview", ==========onMeasure" + "===width:" + MeasureSpec.getSize(widthMeasureSpec)
                + "===height:" + MeasureSpec.getSize(heightMeasureSpec));
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        Log.i("infoview", ==========onFinishInflate");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.i("infoview", ==========ondraw");
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        Log.i("infoview", ==========onAttachedToWindow");
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        Log.i("infoview", ==========onDetachedFromWindow");
    }

    @Override
    protected void onWindowVisibilityChanged(int visibility) {
        super.onWindowVisibilityChanged(visibility);
        Log.i("infoview", ==========onWindowVisibilityChanged" + visibility);
    }

    @Override
    protected void onVisibilityChanged(View changedView, int visibility) {
        super.onVisibilityChanged(changedView, visibility);
        Log.i("infoview", ==========onVisibilityChanged" + "====visibility" + visibility
                + "changedView.toString:" + changedView.toString());
    }
}

```

xml文件： 为了方便测试我使用了绝对布局：

```xml
<AbsoluteLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <com.example.feathers.viewtest.MyView
        android:layout_height="50dp"
        android:layout_width="50dp"
        android:id="@+id/test"
        android:layout_x="50px"
        android:layout_y="50px"/>

</AbsoluteLayout>

```

通过自定义View并输出的Log信息： 刚刚创建应用程序输出的Log：

```
I/infoview: ==========MyView(context,attrs)id===layout_height
I/infoview: ==========onFinishInflate
I/infoview: ==========onVisibilityChanged====visibility4changedView.toString:com.android.internal.policy.impl.PhoneWindow$DecorView{267a1931 I.E..... R.....ID 0,0-0,0}
I/infoview: ==========onVisibilityChanged====visibility0changedView.toString:com.android.internal.policy.impl.PhoneWindow$DecorView{267a1931 V.E..... R.....ID 0,0-0,0}
I/infoview: ==========onAttachedToWindow
I/infoview: ==========onWindowVisibilityChanged0
I/infoview: ==========onMeasure===width:100===height:100
I/infoview: ==========onSizeChanged===w:100===h100===oldw0===oldh0
I/infoview: ==========onLayout===changed:true====l t r b:50---50---150---150
I/infoview: ==========onMeasure===width:100===height:100
I/infoview: ==========onLayout===changed:false====l t r b:50---50---150---150
I/infoview: ==========ondraw
```

从上面的Log信息可以看出，当应用第一次被打开时，首先会调用其构造方法，因为这个View是在XML中创建的，所以会调用MyView(context,attrs)这个构造器。 第二个参数代表XML传入的属性，比如：

`attrs.getAttributeName(0)+ "===" + attrs.getAttributeName(2)`

所获取的值为 id===layout\_height 正好对应XML文件中设置的属性名称

```xml
<com.example.feathers.viewtest.MyView
        android:layout_height="50dp"
        android:layout_width="50dp"
        android:id="@+id/test"
        android:layout_x="50px"
        android:layout_y="50px"/>
```

然后调用了onFinishInflate方法，代表从XML文件加载该View完成 接着**调用了onVisibilityChanged方法两次，第一次将View的visibility的属性设置为4 即不可见，第二次设置为0 即可见，这里不是很懂，为什么要调用两次**。 接着调用了onAttachedToWindow，将其附加到窗口上。 onWindowVisibilityChanged0，窗口的Visibility属性为可见，这是我们可以看到窗口显示出来了。 调用了onMeasure方法，测量组件的大小，发现大小发生了变化，就调用了onSizeChanged方法，可以看见，View最初的w和h是0和0。 onMeasure方法结束后，就要为view设置他的布局了，所以这里调用了onLayout方法进行布局。 最后布局完成后，又再次调用onMeasure方法对组件的大小测量，因为在其他方法中view的组件的大小有可能再次发生变化。所以再次调用onMeasure和onLayout方法。 最后调用onDraw方法进行view的绘制。

当我点击home键时，View隐藏在了后台，其Log为：

```
I/infoview: ==========onWindowVisibilityChanged8
I/infoview: ==========onVisibilityChanged4
```

然后我再次返回到View中

```
I/infoview: ==========onWindowVisibilityChanged4
I/infoview: ==========onVisibilityChanged0
I/infoview: ==========onWindowVisibilityChanged0
I/infoview: ==========ondraw

```

当我点击返回键时

```
I/infoview: ==========onWindowVisibilityChanged8
I/infoview: ==========onDetachedFromWindow

```

view被从window中分离了出来，代表他的生命周期已经结束。

*本文如有错，烦请指出，谢谢，欢迎交流。*


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/front-end/android/view-de-sheng-ming-zhou-qi.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
