一.如何获取View控件
要想获得控件,必须先得到Activity的rootView。在Android中,对于一般的Activity或其对话框,其rootView叫做DecorView,其实就是Activity和Dialog外面的那层框(关于Activity或dialog的层次可以用HierarchyViewer来查看)
虽然通过Activity类的getWindow().getDecorView可以获取到Activity自身的DecorView,但是无法获取到对话框的,因此Robotium中界面控件是从WindowManagerGlobal(或WindowManagerImpl)中的mViews获取到的。当然mViews中不但包含DecorView,还包含同进程内的所有界面的根节(如悬浮框的根节点)。
首先,在getAllViews()方法,其中使用了getWindowDecorViews(),在这个方法中使用反射的机制拿到WindowManager类中mView属性的值(mView相当于一个大容器,包含同进程内的所有界面的根节),具体方法如下:
public View[] getWindowDecorViews()
{
Field viewsField;
Field instanceField;
try {
//获取windowManager类中的mView属性
viewsField = windowManager.getDeclaredField("mViews");
//windowManager类是单例模式,获取实例对象属性
instanceField = windowManager.getDeclaredField(windowManagerString);
viewsField.setAccessible(true);
instanceField.setAccessible(true);
//传入一个null来获取静态实例对象
Object instance = instanceField.get(null);
View[] result;
if (android.os.Build.VERSION.SDK_INT >= 19) {
//通过windowManager的实例 获取实例中mView属性的值
result = ((ArrayList<View>) viewsField.get(instance)).toArray(new View[0]);
} else {
result = (View[]) viewsField.get(instance);
}
return result;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
以上代码中,首先使用反射获取windowManager类中的mView属性,在获取windowManager类的实例,进而得到了实例中mView属性的值
其中windowManager在android不同版本使用不同的类名,定义如下:
private static Class<?> windowManager;
static{
try {
String windowManagerClassName;
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerClassName = "android.view.WindowManagerGlobal";
} else {
windowManagerClassName = "android.view.WindowManagerImpl";
}
windowManager = Class.forName(windowManagerClassName);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
e.printStackTrace();
}
}
windowManager类中定义的实例对象名也因android版本不同而有所变化
private void setWindowManagerString(){
if (android.os.Build.VERSION.SDK_INT >= 17) {
windowManagerString = "sDefaultWindowManager";
} else if(android.os.Build.VERSION.SDK_INT >= 13) {
windowManagerString = "sWindowManager";
} else {
windowManagerString = "mWindowManager";
}
}
到此我们已经得到了所有界面的大容器,下一步需要从中筛选出当前显示的页面DecorView()
mView中包含三类View分别是:
1) 当前显示的以及没有显示的Activity的DecorView
2) 当前对话框的DecorView
3) 悬浮框View等其他不属于DecorView的独立View
下面调用getNotDecorView方法筛选所有不是decorViews的view,并接将他们的子控件筛选出来
private final View[] getNonDecorViews(View[] views) {
View[] decorViews = null;
if(views != null) {
decorViews = new View[views.length];
int i = 0;
View view;
for (int j = 0; j < views.length; j++) {
view = views[j];
if (view != null && !(view.getClass().getName()
.equals("com.android.internal.policy.impl.PhoneWindow$DecorView"))) {
decorViews[i] = view;
i++;
}
}
}
return decorViews;
}
对于Activity/Dialog的筛选,使用getRecentDecorView()方法,找到一个最后创建的DrcotView(即为最上层Activity/Dialog的DecorView)
public final View getRecentDecorView(View[] views) {
if(views == null)
return null;
final View[] decorViews = new View[views.length];
int i = 0;
View view;
for (int j = 0; j < views.length; j++) {
view = views[j];
if (view != null){
String nameOfClass = view.getClass().getName();
if(nameOfClass.equals("com.android.internal.policy.impl.PhoneWindow$DecorView") || nameOfClass
.equals("com.android.internal.policy.impl.MultiPhoneWindow$MultiPhoneDecorView")) {
decorViews[i] = view;
i++;
}
}
}
return getRecentContainer(decorViews);
}
private final View getRecentContainer(View[] views) {
View container = null;
long drawingTime = 0;
View view;
for(int i = 0; i < views.length; i++){
view = views[i];
if (view != null && view.isShown() && view.hasWindowFocus() && view.getDrawingTime() > drawingTime) {
container = view;
drawingTime = view.getDrawingTime();
}
}
return container;
}
在这里如果是获取所有控件,onlySufficientlyVisible传入的值是false,所有创建view列表时不会检查控件是否显示在屏幕上,如果onlySufficientlyVisible是ture那么在加入列表时会对控件的可见性进行检查(在这的可可见性是指由于界面滚动而导致的没有显示或显示不完全的可见性)
private void addChildren(ArrayList<View> views, ViewGroup viewGroup, boolean onlySufficientlyVisible) {
if(viewGroup != null){
for (int i = 0; i < viewGroup.getChildCount(); i++) {
final View child = viewGroup.getChildAt(i);
if(onlySufficientlyVisible && isViewSufficientlyShown(child))
views.add(child);
else if(!onlySufficientlyVisible)
views.add(child);
if (child instanceof ViewGroup) {
addChildren(views, (ViewGroup) child, onlySufficientlyVisible);
}
}
}
}
目前为止我们已经得到了所有的View