技术标签: android 源码解读
首先调用
public void invalidate() {
invalidate(true);
}
调用invalidete 带参数的方法
void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
调用invalidateInternal方法
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent view.
final AttachInfo ai = mAttachInfo;
final ViewParent p = mParent;
if (p != null && ai != null && l < r && t < b) {
final Rect damage = ai.mTmpInvalRect;
damage.set(l, t, r, b);
p.invalidateChild(this, damage);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
// Damage the entire IsolatedZVolume receiving this view's shadow.
if (isHardwareAccelerated() && getZ() != 0) {
damageShadowReceiver();
}
}
}
invalidateInternal里面调用了ViewParent的invaludateChild方法,而ViewParent就是当前View的外层ViewGroup
查看ViewGroup的invaludateChild方法.这个方法里面有个while循环,一直向外层ViewGroup回调invaludateChildInParent方法。我们都知道最终顶层的View就是DecoView,而DecoView里面的ViewParent又是哪个呢?
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
//省略部分代码
//while循环,一直向上回调ViewGroup 的invalidateChildInParent
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
parent = parent.invalidateChildInParent(location, dirty);
if (view != null) {
// Account for transform on current parent
Matrix m = view.getMatrix();
if (!m.isIdentity()) {
RectF boundingRect = attachInfo.mTmpTransformRect;
boundingRect.set(dirty);
m.mapRect(boundingRect);
dirty.set((int) (boundingRect.left - 0.5f),
(int) (boundingRect.top - 0.5f),
(int) (boundingRect.right + 0.5f),
(int) (boundingRect.bottom + 0.5f));
}
}
} while (parent != null);
}
}
WindowManagerGlobal.java view为DecorView
当我们setContent方法后,在Activity 的onResume方法前,会把当前的顶层DecoView添加到addView 到WindowManager里面,而WindowManager的实现类WindowManagerImpl里面的调用的是WindowManagerGlobal的addView方法,如下。看注释的关系代码,这里实例化了一个ViewRootImpl,调用了setView方法,传入的view参数就是DecorView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow)
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//关键代码
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
synchronized (mLock) {
final int index = findViewLocked(view, false);
if (index >= 0) {
removeViewLocked(index, true);
}
}
throw e;
}
}
ViewRootImpl 的setView方法,其中view为DecorView,在该方法里面调用了view.assigenParent(this),把ViewRootImpl设置为DecorView的ViewParent.
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
CompatibilityInfo compatibilityInfo = mDisplayAdjustments.getCompatibilityInfo();
mTranslator = compatibilityInfo.getTranslator();
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
//关键代码.设置DecorView的viewParent为ViewRootImpl
view.assignParent(this);
mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;
if (mAccessibilityManager.isEnabled()) {
mAccessibilityInteractionConnectionManager.ensureConnection();
}
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
}
}
}
ViewGroup invalidateChild方法里面的while循环完最终调用ViewRootImpl里面的invaludateChild方法,查看ViewRootImpl里面的invalidateChild方法
ViewRootImpl
public void invalidateChild(View child, Rect dirty) {
invalidateChildInParent(null, dirty);
}
调用了invalidateChildInParent方法
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
checkThread();
if (DEBUG_DRAW) Log.v(TAG, "Invalidate child: " + dirty);
if (dirty == null) {
invalidate();
return null;
} else if (dirty.isEmpty() && !mIsAnimating) {
return null;
}
if (mCurScrollY != 0 || mTranslator != null) {
mTempRect.set(dirty);
dirty = mTempRect;
if (mCurScrollY != 0) {
dirty.offset(0, -mCurScrollY);
}
if (mTranslator != null) {
mTranslator.translateRectInAppWindowToScreen(dirty);
}
if (mAttachInfo.mScalingRequired) {
dirty.inset(-1, -1);
}
}
invalidateRectOnScreen(dirty);
return null;
}
调用了invalidateRectOnScreen方法,由方法名可猜测该方法是刷新屏幕上面的一个Rect区域,而Rect区域就是调用invalidate方法的那个View大小
private void invalidateRectOnScreen(Rect dirty) {
final Rect localDirty = mDirty;
if (!localDirty.isEmpty() && !localDirty.contains(dirty)) {
mAttachInfo.mSetIgnoreDirtyState = true;
mAttachInfo.mIgnoreDirtyState = true;
}
// Add the new dirty rect to the current one
localDirty.union(dirty.left, dirty.top, dirty.right, dirty.bottom);
// Intersect with the bounds of the window to skip
// updates that lie outside of the visible region
final float appScale = mAttachInfo.mApplicationScale;
final boolean intersected = localDirty.intersect(0, 0,
(int) (mWidth * appScale + 0.5f), (int) (mHeight * appScale + 0.5f));
if (!intersected) {
localDirty.setEmpty();
}
if (!mWillDrawSoon && (intersected || mIsAnimating)) {
scheduleTraversals();
}
}
里面调用了scheduleTraversals方法,
scheduleTraversals方法里面发送了一个消息,将执行TraversalRunnable 任务方法
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
TraversalRunnable 任务方法
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
这个方法又调用了performTraversals()
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
if (mProfile) {
Debug.startMethodTracing("ViewAncestor");
}
performTraversals();
if (mProfile) {
Debug.stopMethodTracing();
mProfile = false;
}
}
}
performTraversals这个方法挺长的,里面执行的方法也多。继续看
里面依次可能会调用了performMeasure,performLayout,performDraw。这三个和我们常用的onMeasure,onLayout,onDraw方法就很像了,因为这三个方法最终也会调用我们常用的onXXX方法。在这里这三个方法不一定都会调用,当我们调用invalidate的时候,也就是说我们只想调用绘制我们的View的方法,这个时候只会调用到performDraw方法,当我们的view如果位置发生改变了,则也会调用到performLayout方法,如果大小也改变了,则也会调用perforMeasure方法。这三个方法就会回调我们自己View里面的mesure,layout,draw方法,measure内部会回调onMeasure,layout内部会回调onLayout,draw内部会回调onDraw。追踪到这里就差不多了,因为最终会回调到那个invalidate方法的view的onDraw方法里去。
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
mIsInTraversal = true;
mWillDrawSoon = true;
boolean windowSizeMayChange = false;
boolean newSurface = false;
boolean surfaceChanged = false;
WindowManager.LayoutParams lp = mWindowAttributes;
int desiredWindowWidth;
int desiredWindowHeight;
final int viewVisibility = getHostVisibility();
boolean viewVisibilityChanged = mViewVisibility != viewVisibility
|| mNewSurfaceNeeded;
WindowManager.LayoutParams params = null;
if (mFirst) {
mFullRedrawNeeded = true;
mLayoutRequested = true;
if (lp.type == WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL
|| lp.type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
// NOTE -- system code, won't try to do compat mode.
Point size = new Point();
mDisplay.getRealSize(size);
desiredWindowWidth = size.x;
desiredWindowHeight = size.y;
} else {
DisplayMetrics packageMetrics =
mView.getContext().getResources().getDisplayMetrics();
desiredWindowWidth = packageMetrics.widthPixels;
desiredWindowHeight = packageMetrics.heightPixels;
}
} else {
desiredWindowWidth = frame.width();
desiredWindowHeight = frame.height();
if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
if (DEBUG_ORIENTATION) Log.v(TAG,
"View " + host + " resized to: " + frame);
mFullRedrawNeeded = true;
mLayoutRequested = true;
windowSizeMayChange = true;
}
}
boolean insetsChanged = false;
boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
if (layoutRequested) {
final Resources res = mView.getContext().getResources();
if (mFirst) {
// make sure touch mode code executes by setting cached value
// to opposite of the added touch mode.
mAttachInfo.mInTouchMode = !mAddedTouchMode;
ensureTouchModeLocally(mAddedTouchMode);
} else {
if (!mPendingOverscanInsets.equals(mAttachInfo.mOverscanInsets)) {
insetsChanged = true;
}
if (!mPendingContentInsets.equals(mAttachInfo.mContentInsets)) {
insetsChanged = true;
}
if (!mPendingStableInsets.equals(mAttachInfo.mStableInsets)) {
insetsChanged = true;
}
if (!mPendingVisibleInsets.equals(mAttachInfo.mVisibleInsets)) {
mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+ mAttachInfo.mVisibleInsets);
}
if (!mPendingOutsets.equals(mAttachInfo.mOutsets)) {
insetsChanged = true;
}
}
// Determine whether to compute insets.
// If there are no inset listeners remaining then we may still need to compute
// insets in case the old insets were non-empty and must be reset.
final boolean computesInternalInsets =
mAttachInfo.mTreeObserver.hasComputeInternalInsetsListeners()
|| mAttachInfo.mHasNonEmptyGivenInternalInsets;
boolean insetsPending = false;
int relayoutResult = 0;
if (mSurfaceHolder != null) {
// The app owns the surface; tell it about what is going on.
if (mSurface.isValid()) {
// XXX .copyFrom() doesn't work!
//mSurfaceHolder.mSurface.copyFrom(mSurface);
mSurfaceHolder.mSurface = mSurface;
}
mSurfaceHolder.setSurfaceFrameSize(mWidth, mHeight);
mSurfaceHolder.mSurfaceLock.unlock();
if (mSurface.isValid()) {
if (!hadSurface) {
mSurfaceHolder.ungetCallbacks();
mIsCreating = true;
mSurfaceHolderCallback.surfaceCreated(mSurfaceHolder);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
}
surfaceChanged = true;
}
if (surfaceChanged) {
mSurfaceHolderCallback.surfaceChanged(mSurfaceHolder,
lp.format, mWidth, mHeight);
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceChanged(mSurfaceHolder, lp.format,
mWidth, mHeight);
}
}
}
mIsCreating = false;
} else if (hadSurface) {
mSurfaceHolder.ungetCallbacks();
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
mSurfaceHolderCallback.surfaceDestroyed(mSurfaceHolder);
if (callbacks != null) {
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceDestroyed(mSurfaceHolder);
}
}
mSurfaceHolder.mSurfaceLock.lock();
try {
mSurfaceHolder.mSurface = new Surface();
} finally {
mSurfaceHolder.mSurfaceLock.unlock();
}
}
}
// Ask host how big it wants to be
//会回调onMeasure方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
// Implementation of weights from WindowManager.LayoutParams
// We just grow the dimensions as needed and re-measure if
// needs be
int width = host.getMeasuredWidth();
int height = host.getMeasuredHeight();
boolean measureAgain = false;
if (lp.horizontalWeight > 0.0f) {
width += (int) ((mWidth - width) * lp.horizontalWeight);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (lp.verticalWeight > 0.0f) {
height += (int) ((mHeight - height) * lp.verticalWeight);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
MeasureSpec.EXACTLY);
measureAgain = true;
}
if (measureAgain) {
if (DEBUG_LAYOUT) Log.v(TAG,
"And hey let's measure once more: width=" + width
+ " height=" + height);
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
}
layoutRequested = true;
}
}
} else {
final boolean windowMoved = (mAttachInfo.mWindowLeft != frame.left
|| mAttachInfo.mWindowTop != frame.top);
if (windowMoved) {
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(frame);
}
mAttachInfo.mWindowLeft = frame.left;
mAttachInfo.mWindowTop = frame.top;
// Update the light position for the new window offsets.
if (mAttachInfo.mHardwareRenderer != null) {
mAttachInfo.mHardwareRenderer.setLightCenter(mAttachInfo);
}
}
}
final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
//会回调onLayout方法
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
// By this point all views have been sized and positioned
// We can compute the transparent area
if ((host.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) != 0) {
// start out transparent
// TODO: AVOID THAT CALL BY CACHING THE RESULT?
host.getLocationInWindow(mTmpLocation);
mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
mTmpLocation[0] + host.mRight - host.mLeft,
mTmpLocation[1] + host.mBottom - host.mTop);
host.gatherTransparentRegion(mTransparentRegion);
if (mTranslator != null) {
mTranslator.translateRegionInWindowToScreen(mTransparentRegion);
}
if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
mPreviousTransparentRegion.set(mTransparentRegion);
mFullRedrawNeeded = true;
// reconfigure window manager
try {
mWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
} catch (RemoteException e) {
}
}
}
}
boolean skipDraw = false;
mFirst = false;
mWillDrawSoon = false;
mNewSurfaceNeeded = false;
mViewVisibility = viewVisibility;
// Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
mReportNextDraw = true;
}
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() ||
viewVisibility != View.VISIBLE;
if (!cancelDraw && !newSurface) {
if (!skipDraw || mReportNextDraw) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
//会回调onDraw()方法
performDraw();
}
} else {
if (viewVisibility == View.VISIBLE) {
// Try again
scheduleTraversals();
} else if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).endChangingAnimations();
}
mPendingTransitions.clear();
}
}
mIsInTraversal = false;
}
至此流程梳理完毕
总结一下,调用View invalidate方法->View invalidateInternal 方法->ViewGroup 的invalidateChild方法->ViewRootImpl的invalidateChild---->performTraversals->View onDraw,大概主要调用流程如此,
文章浏览阅读7.6k次,点赞15次,收藏164次。城市公交网络城市公交网络分析与可视化数据爬取与处理公交站点信息爬取公交线路轨迹爬取坐标转换城市公交网络可视化利用ArcMap实现地图的可视化利用plotly实现地图可视化公交路线基本特征分析公交线路的平均长度公交线路的平均站点数公交线路的平均站距公交线路的平均直线系数公交换乘网络搭建公交换乘网络分析节点数和边数节点的邻居数(度)节点度的分布(直方图)网络的平均路径长度参考资料城市公交网络分析与可..._城市公交网络分析与可视化
文章浏览阅读1.8k次。pga_aggregate_target 通常缩写为P_A_T,该参数同时限制全局pga分配和私有工作区内存分配在oracle9i以及10gr1中,单个sql操作内存使用存在如下限制:对于串行操作,单个sql操作能够使用的pga内存按照一下原则分配:MIN(5%*PGA_AGGREGATE_TARGET,100MB)此处的5%*PGA_AGGREGATE_TARGET实际上是由_smm_nax_s..._19c pga分配原则
文章浏览阅读272次。参考:https://blog..net/chengdong1314/article/details/53463183KEIL编译器C语言编译选项优化等级说明摘录于:http://blog..net/conquerwave/article/details/10450721原文0Minimum optimization. Turns off most optimizations.It gives t..._c语言代码请将优化级别设置为默认
文章浏览阅读997次。在数学建模中主流的编程语言是MATLAB,但随着python/R中数学软件包的不断完善,熟悉这两种编程语言的同学也可以快速数学建模的编程环节。后面我们将介绍几种常见数学建模算法的python实现,旨在展示python在本领域的强大威力。1问题描述你希望通过几种常见算法的实现,了解python在数学建模中的能力。2解决方案python除了丰富的原生数据结构外,拥有强大的第三方软件包支持,例如矩阵运算..._数模源码
文章浏览阅读7.2k次,点赞8次,收藏77次。一、实验内容1、在GNS中创建如下图所示的网络拓扑结构。2、给路由器和防火墙按照拓扑图中的规划,配置好IP地址和路由表。给R1、R2、R4、R6开启远程连接。3、验证防火墙默认安全规则,高安全级别接口(inside)可主动访问低安全级别接口(outside);低安全级别接口(outside)不能主动访问高安全级别接口(inside)。4、在防火墙上配置ACL,使R1和R2可以ping通..._配置防火墙最后一步
文章浏览阅读369次。关于linux下的程序运行很简单,将源码编译成二进制(假设为proram)文件后直接在命令行运行即可,root#./program如果需要后台运行,即不占用当前终端,这在嵌入式linux显得十分有必要,因为一般嵌入式linux只有一个终端,通过rs232连接到电脑上,使用SercureCRT或者其他软件来与linux交互,如果当前终端被程序占用,那么就不能进行其他操作(此处不考虑可以通过..._正点原子linux的出厂程序为什么后台可以正常使用
文章浏览阅读286次,点赞2次,收藏2次。Xilinx开发,ARM开发,嵌入式,Linux开发_mmcargs
文章浏览阅读433次,点赞4次,收藏7次。ubuntu 22.04, (减少鼠标 多用键盘, 集中思路), sxhkd(按键)+xdotool(X11窗口工具)
文章浏览阅读6.7k次。不要官方的骗人数据每年贬值10%-12%左右 (我补充下把,楼下那个所谓的官方数据是CPI,这个指标现在有点失真了,它没有考虑房价等因素,每年10%通货膨胀率结论只是一个概括性的估.我去吃快餐的,由15元升到17元,通胀率13.33%中国的通货膨胀率是多少按经济学理论和国际通行惯例,一般可将居民消费价格视为通货膨胀率。以下是1996年以来中国的居民消费价格。 可能你会觉得数据没有人们心里的感觉高。..._中国m2历年走势
文章浏览阅读1.8k次,点赞3次,收藏8次。EBS R12.2新特性:弹性域值集安全策略弹性域值集安全策略允许系统管理员限制用户访问、添加或者更新特定的值集里的值。值集安全策略使基于角色的分类职责分离键弹性域,描述性弹性域和report参数。举例来说,你可以设置值集安全性策略,比如特定用户可以查看、插入值到任何值集,这些值集被用来做账户弹性域。一些其他用户可以访问、更新Oracle Human Capital Management applications模块下的所有弹性域值集的值。你也可以通过OU、角色、职责等来实现隔离访问。值集安全策略使用一种_ebs 值集 安全性
文章浏览阅读5.7k次,点赞2次,收藏12次。以上就是uniapp 使用scroll-view上拉加载跟下拉刷新功能的实现方法。_scroll-view 下拉刷新
文章浏览阅读843次,点赞14次,收藏25次。读写分离,基本的原理是让主数据库处理事务性增、改、删操作(INSERT、UPDATE、DELETE),而从数据库处理SELECT查询操作。数据库复制被用来把事务性操作导致的变更同步到集群中的从数据库。