本文主要讲解 View 的三个回调函数
- onMeasure() : 回调该方法进行测量
- onLayout() : 回调该方法确定显示的位置
- onSizeChange() : 组件大小改变时进行回调
主程序
// By lentitude
package com.example.testfunction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ButtonExample1 button1;
private Button addButton;
private Button deleteButton;
private Button deleteView;
private TextView textView;
private LinearLayoutExample1 layoutExample1;
private LinearLayoutExample2 layoutExample2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
// 获取实例
button1 = (ButtonExample1)findViewById(R.id.button1);
textView = (TextView)findViewById(R.id.text_view);
layoutExample2 = (LinearLayoutExample2)findViewById(R.id.layout2);
layoutExample1 = (LinearLayoutExample1)findViewById(R.id.layout1);
addButton = (Button)findViewById(R.id.add_button);
deleteButton = (Button)findViewById(R.id.delete_button);
deleteView = (Button)findViewById(R.id.delete_text_view);
addButton.setOnClickListener(this);
deleteButton.setOnClickListener(this);
deleteView.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.add_button:
Button button = new Button(this);
button.setText("按钮3");
// 设置长宽
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutExample1.addView(button,params);
break;
case R.id.delete_button:
layoutExample2.removeView(button1);
break;
case R.id.delete_text_view:
layoutExample1.removeView(textView);
break;
default:
}
}
}
其中,ButtonExample1 ,LinearLayoutExample1 ,LinearLayoutExample2 都是重写的 Button 和 LinearLayout ,只是添加了 log 函数,便于观察函数的调用顺序
打开程序,程序的主界面:
logcat 中显示:
05-02 08:06:26.849 18615-18615/com.example.testfunction D/ButtonExample1: onMeasure:
05-02 08:06:26.850 18615-18615/com.example.testfunction D/ButtonExample2: onMeasure:
05-02 08:06:26.850 18615-18615/com.example.testfunction D/ButtonExample1: onMeasure:
05-02 08:06:26.850 18615-18615/com.example.testfunction D/ButtonExample2: onMeasure:
05-02 08:06:26.851 18615-18615/com.example.testfunction D/LinearLayoutExample2: onMeasure:
05-02 08:06:26.851 18615-18615/com.example.testfunction D/TextViewExample: onMeasure:
05-02 08:06:26.851 18615-18615/com.example.testfunction D/LinearLayoutExample1: onMeasure:
05-02 08:06:26.852 18615-18615/com.example.testfunction D/LinearLayoutExample1: onSizeChanged:
05-02 08:06:26.856 18615-18615/com.example.testfunction D/LinearLayoutExample2: onSizeChanged:
05-02 08:06:26.857 18615-18615/com.example.testfunction D/ButtonExample1: onSizeChanged:
05-02 08:06:26.857 18615-18615/com.example.testfunction D/ButtonExample1: onLayout:
05-02 08:06:26.857 18615-18615/com.example.testfunction D/ButtonExample2: onSizeChanged:
05-02 08:06:26.858 18615-18615/com.example.testfunction D/ButtonExample2: onLayout:
05-02 08:06:26.858 18615-18615/com.example.testfunction D/LinearLayoutExample2: onLayout:
05-02 08:06:26.858 18615-18615/com.example.testfunction D/TextViewExample: onSizeChanged:
05-02 08:06:26.859 18615-18615/com.example.testfunction D/TextViewExample: onLayout:
05-02 08:06:26.859 18615-18615/com.example.testfunction D/LinearLayoutExample1: onLayout:
05-02 08:06:26.869 18615-18615/com.example.testfunction D/ButtonExample1: onDraw:
05-02 08:06:26.873 18615-18615/com.example.testfunction D/ButtonExample2: onDraw:
05-02 08:06:26.874 18615-18615/com.example.testfunction D/TextViewExample: onDraw:
- 控件从内到外的顺序调用 onMeasure() 方法来测量大小,一直到 ViewGroup
- 从 ViewGroup 从外到内开始调用 onSizeChange() 方法,因为是第一次打开,所有的控件都会回调这个方法
- 同时子控件从内到外调用 onLayout() 方法,指定具体显示的布局位置,以确定ViewGroup的布局位置
- 最后顺序调用 onDraw() 方法
用流程图表示:
点击删除视图框按钮:
logcat 中显示:
05-02 08:47:16.047 18615-18615/com.example.testfunction D/LinearLayoutExample1: onMeasure:
05-02 08:47:16.047 18615-18615/com.example.testfunction D/LinearLayoutExample1: onLayout:
因为没有对控件的大小进行干扰,只是改变了布局,所以回调父布局的 onMeasure() 方法重新确定大小,回调 onLayout() 方法重新确定布局
点击删除按钮1:
logcat 中显示:
05-02 08:53:36.713 18615-18615/com.example.testfunction D/ButtonExample2: onMeasure:
05-02 08:53:36.730 18615-18615/com.example.testfunction D/LinearLayoutExample2: onMeasure:
05-02 08:53:36.731 18615-18615/com.example.testfunction D/LinearLayoutExample1: onMeasure:
05-02 08:53:36.732 18615-18615/com.example.testfunction D/ButtonExample2: onSizeChanged:
05-02 08:53:36.734 18615-18615/com.example.testfunction D/ButtonExample2: onLayout:
05-02 08:53:36.734 18615-18615/com.example.testfunction D/LinearLayoutExample2: onLayout:
05-02 08:53:36.734 18615-18615/com.example.testfunction D/LinearLayoutExample1: onLayout:
05-02 08:53:36.735 18615-18615/com.example.testfunction D/ButtonExample2: onDraw:
05-02 08:53:36.750 18615-18615/com.example.testfunction D/ButtonExample2: onDraw:
同样是删除一个按钮,对其他控件和 ViewGroup 的影响要多的多,这里的 button1, button2 的按钮布局文件为:
<com.example.testfunction.ButtonExample1
android:id="@+id/button1"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="按钮1"/>
<com.example.testfunction.ButtonExample2
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="按钮2"/>
所以在删除 button1 按钮之后,button2 按钮的大小被改变(相当与 match_parent), 这样 button2 的 onMeasure() ,onSizeChange(), onLayout() 方法都会被调用,则父布局的对应方法也会被调用
- 先是 button2 调用 onMeasure() 方法,则父布局 LinearLayoutExample1, LinearLayoutExample2 都顺序调用
- 因为 大小被改变,则调用 onSizeChange() 方法
- 布局位置被改变,从内到外调用 onLayout() 方法
点击添加按钮3:
logcat 中显示:
05-02 09:06:19.107 18615-18615/com.example.testfunction D/LinearLayoutExample1: onMeasure:
05-02 09:06:19.107 18615-18615/com.example.testfunction D/LinearLayoutExample1: onLayout:
可以看到这里跟前面删除编辑框的效果是一样的,因为没有对其他控件的大小进行干扰,所以回调父布局的 onMeasure() 方法重新确定大小,回调 onLayout() 方法重新确定布局
总结:
- 在打开应用程序的时候,ViewGroup 和 View 都会回调 onSizeChange() 方法
- ViewGroup 的大小和布局都是通过子 View 的大小和布局来确定的,所以 onMeasure(), onLayout() 方法都是先遍历子 View 的大小和布局之后,再调用 ViewGroup 的对应方法 - 由内向外
- 调用顺序: