Android工具HierarchyViewer代码导读(4) -- 前台代码
来源:互联网 发布:苹果网络锁查询 编辑:程序博客网 时间:2024/06/05 11:40
在前文<Android工具HierarchyViewer 代码导读(3) -- 后台代码>中,我们讲解了HierarchyViewe的后台代码,指的是HierarchyViewer如何通过ADB和ViewServer这两个信道和Android设备进行通信,获取Acitivities信息、控件信息和控件截图等信息。本文将讲解HierarchyViewer的前台代码,指的是在后台获取到数据后,HierarchyViewer是如何显示他们的;当用户对视图进行操作时,如选中、放大缩小等,视图是如何响应的。
MVC模式
前文中我们提到,HierarchyViewer代码采用的是典型的MVC构架,我们把上文中使用的MVC模式图再拿出来(这里只讨论控件层次图界面相关的代码结构):
其中,在TreeViewModel.java文件中定义了ITreeChangeListener接口
public
static
interface
ITreeChangeListener {
public
void
treeChanged();
public
void
selectionChanged();
public
void
viewportChanged();
public
void
zoomChanged();
}
所有的Views – LayoutViewer, TreeViewer, PropertyViewer, TreeViewOverview, TreeViewControllers都实现了该接口。 TreeViewModel维护了一个ITreeChangeListener的ArrayList:
private
final
ArrayList<ITreeChangeListener> mTreeChangeListeners =
new
ArrayList<ITreeChangeListener>();
当Views构造时,都会把自己加到mTreeChangeListeners中,当TreeViewModel中的数据改变时,TreeViewModel通过事件通知所有注册到mTreeCHangeListeners中的Views。
这些事件包括:
treeChanged -- 整个TreeView改变时触发
selectionChanged -- 选中的节点改变时触发
viewportChanged -- 当前视见区改变时触发
zoomChanged -- 当前放大缩小比例改变时触发
TreeViewModel中保存了四个数据:
private
DrawableViewNode mTree;
//整个控件树
private
DrawableViewNode mSelectedNode;
//当前选中的控件树
private
Rectangle mViewport;
//视见区
private
double
mZoom;
//放大缩小比例
Views通过读取4个数据进绘制或显示。
TreeView加载
当用户在主界面双击某个Activity,或者在查看控件树界面点击刷新时,整个TreeView将重新加载。双击或者刷新操作将最终调用HierarchyViewerDirector.java的loadViewTreeData方法:
public
void
loadViewTreeData(
final
Window window) {
executeInBackground(
"Loading view hierarchy"
,
new
Runnable() {
public
void
run() {
mFilterText =
""
;
//$NON-NLS-1$
ViewNode viewNode = DeviceBridge.loadWindowData(window);
if
(viewNode !=
null
) {
DeviceBridge.loadProfileData(window, viewNode);
viewNode.setViewCount();
TreeViewModel.getModel().setData(window, viewNode);
}
}
});
}
这个函数我们在上文中已经提到过,本文主要关心其中2个函数:
DeviceBridge.loadWindowData(window) -- 这个函数做了两件事情:1)向ViewServer发送DUMP命令,来获取Acitivity所有控件的信息。 2)获取到的控件树信息是文本的形式返回的,如下是其中一个控件的文本信息:
android.widget.FrameLayout
@44edba90
mForeground=
52
,android.graphics.drawable.NinePatchDrawable
@44edc1e0
mForegroundInPadding=
5
,
false
mForegroundPaddingBottom=
1
,
0
mForegroundPaddingLeft=
1
,
0
mForegroundPaddingRight=
1
,
0
mForegroundPaddingTop=
1
,
0
mMeasureAllChildren=
5
,
false
mForegroundGravity=
2
,
55
getDescendantFocusability()=
24
,FOCUS_BEFORE_DESCENDANTS getPersistentDrawingCache()=
9
,SCROLLING isAlwaysDrawnWithCacheEnabled()=
4
,
true
isAnimationCacheEnabled()=
4
,
true
isChildrenDrawingOrderEnabled()=
5
,
false
isChildrenDrawnWithCacheEnabled()=
5
,
false
mMinWidth=
1
,
0
mPaddingBottom=
1
,
0
mPaddingLeft=
1
,
0
mPaddingRight=
1
,
0
mPaddingTop=
2
,
38
mMinHeight=
1
,
0
mMeasuredWidth=
3
,
480
mMeasuredHeight=
3
,
800
mLeft=
1
,
0
mPrivateFlags_DRAWING_CACHE_INVALID=
3
,
0x0
mPrivateFlags_DRAWN=
4
,
0x20
mPrivateFlags=
8
,
16911408
mID=
10
,id/content mRight=
3
,
480
mScrollX=
1
,
0
mScrollY=
1
,
0
mTop=
1
,
0
mBottom=
3
,
800
mUserPaddingBottom=
1
,
0
mUserPaddingRight=
1
,
0
mViewFlags=
9
,
402653186
getBaseline()=
2
,-
1
getHeight()=
3
,
800
layout_bottomMargin=
1
,
0
layout_leftMargin=
1
,
0
layout_rightMargin=
1
,
0
layout_topMargin=
1
,
0
layout_height=
12
,MATCH_PARENT layout_width=
12
,MATCH_PARENT getTag()=
4
,
null
getVisibility()=
7
,VISIBLE getWidth()=
3
,
480
hasFocus()=
5
,
false
isClickable()=
5
,
false
isDrawingCacheEnabled()=
5
,
false
isEnabled()=
4
,
true
isFocusable()=
5
,
false
isFocusableInTouchMode()=
5
,
false
isFocused()=
5
,
false
isHapticFeedbackEnabled()=
4
,
true
isInTouchMode()=
4
,
true
isOpaque()=
5
,
false
isSelected()=
5
,
false
isSoundEffectsEnabled()=
4
,
true
willNotCacheDrawing()=
5
,
false
willNotDraw()=
5
,
false
该文本将被解析,所有信息将保存在ViewNode对象中。文本中所有的属性都同时保存在ViewNode的List<Property> properties和Map<String, Property> namedProperties中,一些和绘制视图相关的属性,如top,paddingLeft,marginBottom等等,除了保存在properties和namedProperties中,还将直接保存在ViewNode的成员变量中。
ViewNode是一个树,每个ViewNode节点中保存了它的父节点和子节点。文本解析的时候,是如何确定ViewNode父节点的呢?原来每行文本信息前面都有若干个空格,空格的数量决定了这个节点的深度,如5个空格表示这个节点在第6层,它的父节点就是最近收到的,有4个空格的节点。具体解析过程大家可以深入阅读loadWindowData函数。
TreeViewModel.getModel().setData(window, viewNode) -- 更新TreeViewModel的TreeView
让我们step into TreeViewModel.getModel().setData(window, viewNode)函数:
public
void
setData(Window window, ViewNode viewNode) {
synchronized
(
this
) {
if
(mTree !=
null
) {
mTree.viewNode.dispose();
}
this
.mWindow = window;
if
(viewNode ==
null
) {
mTree =
null
;
}
else
{
mTree =
new
DrawableViewNode(viewNode);
mTree.setLeft();
mTree.placeRoot();
}
mViewport =
null
;
mZoom =
1
;
mSelectedNode =
null
;
}
notifyTreeChanged();
}
以上函数中:
mTree = new DrawableViewNode(viewNode) –通过ViewNode树来构造DrawableViewNode树。为什么已经有了ViewNode结构还要再构造一个DrawableViewNode结构呢? 它们的功能是不同的,ViewNode是面向数据的,它对应的是Acitivity中每个控件节点的信息; 而DrawableViewNode面向的是图形绘制,它通过计算ViewNode中提供的数据,确定如何在Hierarchy view中进行绘制。读者深入阅读该构造函数,它的作用是根据ViewNode来递归地构造整个DrawableViewNode控件树,并根据每个子树的size确定每个子树在Hierarchy view绘制时中占据的高度。
mTree.setLeft() -- 计算树中每个节点在Hierarchy view绘制时的left值。
mTree.placeRoot() -- 计算树中每个节点在Hierarchy view绘制时的top值。
mViewport = null,mZoom = 1,mSelectedNode = null -- 初始化视见区,放大缩小比例和当前选中节点。
notifyTreeChanged() -- 触发treeChanged事件。
最后,TreeViewOverview.java, LayoutViewer, TreeViewer都是通过响应treeChanged事件,并最终调用PaintListener事件,根据TreeViewModel中的mTree,mViewport,mZoom,mSelectedNode的数据来绘制图形的(这3个类都是继承Canvas类)。
这3个类中的PaintListener事件中图形绘制的代码都很值得一读,但本文限于篇幅不能详细介绍了。
用户事件响应
当用户在一个View中进行操作,其他View也会响应这个操作。如在TreeView中滚动滚轮,TreeViewOverview也会跟着放大缩小;在LayoutViewer中选中某个节点,TreeView和TreeViewOverview中也会跟着选中,这一切是怎么发生的呢?
通过上一节,其实我们很容易理解HierarchyViewer是怎么做的了,这还是一个经典的MVC模式的例子:TreeViewModel提供了如下公开方法(加上上节中的setData方法,一共4个方法)来改变TreeViewModel中的数据:
public
void
setSelection(DrawableViewNode selectedNode)
public
void
setViewport(Rectangle viewport)
public
void
setZoom(
double
newZoom)
当在某View中选中节点时,移动视见区,放大缩小时,View将调用对应的方法来修改TreeViewModel中的数据,然后对应的事件 -- selectionChanged,viewportChanged和zoomChanged将被触发,Views通过响应这些事件,在PaintListener中重绘图形。这是一个用户操作View,View调用Model,Model触发事件,Views响应事件的过程。
Note:
1)不是所有的Views都关心所有的事件。如LayoutViewer不关心zoomChanged和viewportChanged事件;PropertyViewer只关心selectionChanged事件。
2)用户选中一个节点时,需要进行坐标转换,遍历所有的点才能找到选中的节点;在LayoutViewer中,需要找到的是符合条件的,层次低的节点。
本系列到此结束。我相信阅读HierarchyViewer和其他一些sdk工具的源代码,对于理解Android的机制是有帮助的。同时,对于学习MVC也会助益不少,google工程师的代码的确很简洁优秀。
本文由知平软件的刘斌华原创,转载请注明出处。
- Android工具HierarchyViewer代码导读(4) -- 前台代码
- Android工具HierarchyViewer代码导读(4) -- 前台代码
- Android工具HierarchyViewer 代码导读
- Android工具HierarchyViewer 代码导读
- Android工具HierarchyViewer 代码导读
- Android工具HierarchyViewer 代码导读
- Android工具HierarchyViewer 代码导读(3) -- 后台代码
- Android工具HierarchyViewer 代码导读(3) -- 后台代码
- Android工具HierarchyViewer 代码导读(1) -- 功能实现演示
- Android工具HierarchyViewer 代码导读(1) -- 功能实现演示
- Android工具HierarchyViewer 代码导读(2) -- 建立Eclipse调试环境
- Android工具HierarchyViewer 代码导读(1) -- 功能实现演示
- Android工具HierarchyViewer 代码导读(1) -- 功能实现演示
- Android工具HierarchyViewer 代码导读(2) -- 建立Eclipse调试环境
- Android 工具之hierarchyviewer
- Android工具HierarchyViewer
- Android 工具之hierarchyviewer
- android 布局查看工具 hierarchyViewer
- Android工具HierarchyViewer 代码导读(3) -- 后台代码
- 页面包含inc文件、用户控件、普通html/htm文件
- 5年内要看的计算机的书
- 一生的知识积累,自学的起码占90%
- IOS 使用自定义字体的方法 (待续)
- Android工具HierarchyViewer代码导读(4) -- 前台代码
- Linux下C语言实现查看进程是否存在
- mfc 给菜单添加相应的响应函数
- 在数据库里如何将毫秒转换成date格式
- LARGE_INTEGER类型和LONGLONG类型以及QueryPerformanceFrequency函数
- 规划篇
- 34.windbg-k*实例分析(查看调用栈分析)
- 程序员经典语录
- dropdownlist 启用了autopostback但是不执行selectedindexchanged事件