Android 下获取 activity_main.xml 的根 View

最近做测试的时候需要获取当前 XML 的根视图进行操作,查询后发现网上提到了 getDecorView()getRootView(),参照别人的测试自己也做了一个小的测试,首先是一个 View 层次结构图,方便理解测试,原文地址在文章最后: 视图层次

activity_main 和 btn_lay 布局如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.rookieyang.test.MainActivity">

<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello" />

<include layout="@layout/btn_lay" />

</android.support.constraint.ConstraintLayout>

1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/btn2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test" />

</LinearLayout>

接下来是测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class MainActivity extends AppCompatActivity {

private View mDecorView;
private View mRootView;
private View mContentView;
private View mCurrentView;
private View mCurrentView2;
private TextView mOneText;
private TextView mTwoText;
private Button mBtn;
private Button mBtn2;
private Button mBtn3;
private View mView;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

init();
logView();
}

private void init() {
mDecorView = getWindow().getDecorView();
mRootView = mDecorView.getRootView();
mContentView = mDecorView.findViewById(android.R.id.content);
mCurrentView = ((ViewGroup) mContentView).getChildAt(0);
mOneText = (TextView) ((ViewGroup) mCurrentView).getChildAt(0);
mCurrentView2 = ((ViewGroup) mRootView
.findViewById(android.R.id.content)).getChildAt(0);
mTwoText = (TextView) ((ViewGroup) mCurrentView2).getChildAt(0);
mView = LayoutInflater.from(this).inflate(R.layout.btn_lay, null);
mBtn = (Button) findViewById(R.id.btn);
mBtn2 = (Button) mView.findViewById(R.id.btn2);
mBtn3 = (Button) findViewById(R.id.btn2);
}

private void logView() {
Log.i("MainActivity", "DecorView:" + mDecorView );
Log.i("MainActivity", "DecorView ID:" + mDecorView.getId());
Log.i("MainActivity", "RootView:" + mRootView);
Log.i("MainActivity", "RootView ID:" + mRootView.getId());
Log.i("MainActivity", "ContentView:" + mContentView);
Log.i("MainActivity", "ContentView ID:" + mContentView.getId());
Log.i("MainActivity", "CurrentView:" + mCurrentView);
Log.i("MainActivity", "CurrentView ID:" + mCurrentView.getId());
Log.i("MainActivity", "CurrentView2:" + mCurrentView2);
Log.i("MainActivity", "CurrentView2 ID:" + mCurrentView2.getId());
Log.i("MainActivity", "mOneText ID:" + mOneText.getText());
Log.i("MainActivity", "mTwoText ID:" + mTwoText.getText());
Log.i("MainActivity", "mBtn RootView:" + mBtn.getRootView());
Log.i("MainActivity", "mBtn2 RootView:" + mBtn2.getRootView());
Log.i("MainActivity", "mBtn3 RootView:" + mBtn3.getRootView());
}

}
运行结果如下: 运行结果

首先可以看到 getWindow().getDecorView()getDecorView().getRootView() 获得的 View 和 id 是一样的, 说明 DecorView 是整个 Window 界面最顶层的 View。

但是对比 mBtn2.getRootView()mBtn3.getRootView() 却发现两者输出结果完全不同,mBtn3.getRootView() 获取到的是 DecorView,而 mBtn2.getRootView() 获取到的则是 LinearLayout,两者的不同之处在于 mBtn2 通过 mView.findViewById(R.id.btn2) 获取,mBtn3 却是直接通过 findViewById(R.id.btn2) 获取。在查询 LayoutInflater 的 API 文档后,第一行的描述是这样的:
「Instantiates a layout XML file into its corresponding View objects」
也就说通过 LayoutInflater.from(this).inflate(R.layout.btn_lay, null)btn_lay 布局被实例化了到对应的 View 对象中,所以导致 mView 的根视图是 LinearLayout,自然 mBtn2 的根视图也是 LinearLayout。 findViewById(android.R.id.content) 用于获取 activity_main 外层的视图,即上图的 mContentParent。 在获取了 mContentParent 之后,由于 activity_main 的根视图是 mContentParent 的第一个子元素,所以利用 getChildAt(0) 即可获取 ConstraintLayout。mCurrentViewmCurrentView2 的输出结果可以印证该点。

总结:

获取 activity_main.xml 根 View:

1
2
View rootView = ((ViewGroup)(getWindow().getDecorView()
.findViewById(android.R.id.content))).getChildAt(0);
获取 btn_lay.xml 根 View:
1
View rootView = LayoutInflater.from(this).inflate(R.layout.btn_lay, null).getRootView();
getDecorView():
  Retrieve the top-level window decor view (containing the standard window frame/decorations and the client's content inside of that), which can be added as a window to the window manager.
getRootView():
  Finds the topmost view in the current view hierarchy.


参考链接

  1. view.getRootView() 的真正含义及测试
  2. Android View 源码解读