我知道android的view布局是树形布局的。子View需要刷新的自身界面的时候,把需求告诉父亲,则父View负责刷新、布局显示子View,从上到下。
public void invalidate() {
final ViewParent p = mParent;
final AttachInfo ai = mAttachInfo;
if (p != null && ai != null) {
final Rect r = ai.mTmpInvalRect;
// 设置刷新区域为自己的尺寸
r.set(0, 0, mRight - mLeft, mBottom - mTop);
p.invalidateChild(this, r);
}
}
从上面的代码我们可以看出:子View调用invalidate时,首先找到自己父View(View的成员变量mParent记录自己的父View),然后将AttachInfo中保存的信息告诉父View刷新自己。
View的父子关系的建立分为两种情况:
(1) View加入ViewGroup中
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {
...
// tell our children
if (preventRequestLayout) {
child.assignParent(this);
} else {
child.mParent = this;
}
...
}
2)DecorView注册给WindowManagerImpl时,产生一个ViewRoot作为其父View。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView){
.....
view.assignParent(this);
....
}
AttachInfo是在View第一次attach到Window时,ViewRoot传给自己的子View的。这个AttachInfo之后,会顺着布局体系一直传递到最底层的View。
View.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
.....
}
ViewGroup.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
super.dispatchAttachedToWindow(info, visibility);
for (int i = 0; i < count; i++) {
children[i].dispatchAttachedToWindow(info, visibility);
}
}
并且在新的View被加入ViewGroup时,也会将该AttachInfo传给加入的View
ViewGroup.java
private void addViewInner(View child, int index, LayoutParams params, boolean preventRequestLayout) {
child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
}
到这里明白了mParent与AttachInfo代表的意义,可以继续刷新过程的分析。
在invalidate中,调用父View的invalidateChild,这是一个从第向上回溯的过程,每一层的父View都将自己的显示区域与传入的刷新Rect做交集。
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
final int[] location = attachInfo.mInvalidateChildLocation;
// 需要刷新的子View的位置
location[CHILD_LEFT_INDEX] = child.mLeft;
location[CHILD_TOP_INDEX] = child.mTop;
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes through
final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
// Check whether the child that requests the invalidate is fully opaque
final boolean isOpaque = child.isOpaque() && !drawAnimation && child.getAnimation() != null;
// Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
} else if (parent instanceof ViewRoot) {
((ViewRoot) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
}
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
}
}
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
if ((mPrivateFlags & DRAWN) == DRAWN) {
if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
FLAG_OPTIMIZE_INVALIDATE) {
// 根据父View的位置,偏移刷新区域
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX, location[CHILD_TOP_INDEX] - mScrollY);
final int left = mLeft;
final int top = mTop;
//计算实际可刷新区域
if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
(mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
mPrivateFlags &= ~DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
return mParent;
}
} else {
mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = mLeft;
location[CHILD_TOP_INDEX] = mTop;
dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
mBottom - location[CHILD_TOP_INDEX]);
return mParent;
}
}
return null;
}
这个向上回溯的过程直到ViewRoot那里结束,由ViewRoot对这个最终的刷新区域做刷新。
ViewRoot.java
public void invalidateChild(View child, Rect dirty) {
scheduleTraversals();
}
分享到:
相关推荐
基于SYSTEMVIEW通信原理实验报告北邮通信原理实验
用MATLAB 通信系统仿真,包括模拟调幅,调频,调相,数字编码,数字调制技术等等。内含源码及注释。
View复用原理是ListView能够流畅滚动并节省系统资源的关键,特别是当列表包含复杂视图时。 首先,我们来理解什么是View复用。在ListView中,屏幕可见的item数量是有限的,当用户滚动时,已滑出屏幕的item会被重新...
**View工作原理详解** 在Android开发中,View是构建用户界面的基本元素,它与用户进行交互,接收并处理用户的触摸事件。理解View的工作原理对于优化应用性能和开发自定义视图至关重要。本文将深入探讨View的创建、...
通原实验仿真,共十个,全部可用,基本的模拟和数字调制方法都有,希望对大家有所帮助。
事务原理MVCC、Readview和行锁原理分析直播中(1).zip事务原理MVCC、Readview和行锁原理分析直播中(1).zip事务原理MVCC、Readview和行锁原理分析直播中(1).zip事务原理MVCC、Readview和行锁原理分析直播中(1).zip事务...
根据提供的信息,我们可以了解到这是一份关于 Beaglebone Black 的 LCD Cape(扩展板)BB View 的原理图文档。为了更好地理解这份文档所涉及的关键技术点,我们先来逐一解析其中包含的信息。 ### 1. 标题:BB VIEW ...
通信原理System view 仿真实例,用System view软件将通信原理教程的各个章节进行了仿真,可以复制通信技术学习
本文将深入探讨自定义View的基础与原理,包括它的概念、使用场景以及如何进行自定义。 首先,我们要理解什么是自定义View。在Android系统中,View是构成UI的基本元素,如按钮、文本框等。自定义View则是指开发者...
绪论 2 SystemView及其操作简介 3 ...3.1 16QAM系统基本原理 27 3.2 16QAM产生的方法 27 3.3 16QAM信号的解调方法 28 3.4 16QAM的信号空间 29 3.5 16QAM调制系统仿真设计 30 小结 33 附录 34 谢辞 34 参考文献 34
北邮通信原理实验基于SYSTEMVIEW通信原理实验报告
通信原理SystemView仿真实验指导书
通过SystemView,我们可以直观地理解2ASK调幅法的工作原理,并进行详细的仿真分析。 2ASK调幅的基本过程是将二进制数字序列转换为模拟信号。这个转换过程中,数字信号被映射到两个不同的幅度级别上,例如,0通常...
`<router-view>` 的实现原理** `<router-view>` 是 Vue Router 中的一个特殊组件,它作为路由的占位符,用于展示匹配当前路由的组件。当路由变化时,`<router-view>` 将被替换为与当前激活路由配置对应的组件实例...
学习Systemview仿真软件,培养学生基本掌握电路设计的基本思路和方法,对需要仿真的通信系统各功能模块的工作原理进行分析。 a.二进制振幅键控(2ASK)系统的设计 b.二进制移频键控(2FSK)系统的设计 c.二进制移...
通信原理实验五--二进制差分相移键控 DPSK--Systemview仿真.rar 资源包括:DPSK的调制、解调和相比较法解调DPSK 实验目的 1、掌握 DPSK 调制和解调的基本原理; 2、掌握 DPSK 数据传输过程,熟悉典型电路; 3、...
1. **2PSK调制原理**:2PSK采用两种不同的相位,通常为0°和180°,代表二进制的“0”和“1”。通过改变载波信号的相位,我们可以将二进制数据编码到模拟信号中。 2. **数字信号处理**:在2PSK仿真中,首先要进行...
PowerPoint演示文稿AndroidView的工作原理WWW1PPTCOMContents一前言二工作流程三measure过程四layout过程五draw过程一前言11ViewView类是用户界