当前位置:网站首页>Drawing mechanism of view (3)
Drawing mechanism of view (3)
2022-07-02 07:27:00 【android_ Mr_ summer】
brief introduction
Last time we analyzed ViewRootImpl Objects and view Of measure() Methods to analyze , Let's continue to analyze layout() and draw() Method .
Catalog
- layout()
- draw()
layout()
We learned from the last blog ViewRootImpl Medium performTraversals() Method is called performLayout(), First look at the source code :
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");
try {
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
mInLayout = false;
}
In this method, we mainly look at host.layout(),host It's our root layout DecorView,DecorView Inherited from FrameLayout, The call layout() Methods , So we can continue to check view In the middle of layout Method :
public void layout(int l, int t, int r, int b) {
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
//layoutoutMode by Optical It will be transferred to setOpticalFrame()
//setOpticalFrame() The passed in parameters will be adjusted , But still call setFrame() Method
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
// If this is a ViewGroup, Also traverses the sub View Of layout() Method
// If it's ordinary View, Notice the specific implementation class layout change notice
onLayout(changed, l, t, r, b);
// eliminate PFLAG_LAYOUT_REQUIRED Mark
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
``````
// Layout monitoring notification
}
// eliminate PFLAG_FORCE_LAYOUT Mark
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
}
viewGroup Medium layout()
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) {
// If there is no animation , Or the animation is not running
super.layout(l, t, r, b);
} else {
// Wait for the animation to complete before calling requestLayout()
mLayoutCalledWhileSuppressed = true;
}
}
@Override
protected abstract void onLayout(boolean changed,
int l, int t, int r, int b);
Above we know view in layout The way is not to be final Embellished , Its subclasses can duplicate the method , in addition onLayout() Method is an empty method , So we need to View Subclasses of can rewrite the method according to their own needs to complete layout technological process .
and viewGroup Medium layout The way is to be final Embellished , We inherit viewGroup Method must override onLayout Abstract method of .
The first call setFrame() Method , The return value of the method indicates whether the layout has changed from the last time . The four parameters passed in represent , Layout left 、 Top 、 Right 、 The value at the bottom , These four values indicate a rectangular area .
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
mPrivateFlags |= PFLAG_HAS_BOUNDS;
// Callbacks onSizeChanged() Method
if (sizeChanged) {
sizeChange(newWidth, newHeight, oldWidth, oldHeight);
}
...
}
return changed;
}
This method is relatively simple , It mainly saves the area passed in by the parent class to View Of mLeft、mTop、mRight、mBottom. The execution of the setFrame() Then it will be executed to onLayout() Method .
draw()
draw The process is also in ViewRootImpl Of performTraversals() Internally dispatched , The calling sequence is measure() and layout() after ,ViewRootImpl The code in will create a Canvas object , And then call View Of draw() Method to perform a specific drawing process . So it's back to ViewGroup And View Tree recursion of draw The process .
public void draw(Canvas canvas) {
. . .
// Draw the background , Only dirtyOpaque by false And then draw , The same below
int saveCount;
if (!dirtyOpaque) {
drawBackground(canvas);
}
. . .
// Draw your own content
if (!dirtyOpaque) onDraw(canvas);
// Sketcher View
dispatchDraw(canvas);
. . .
// Draw scrollbars, etc
onDrawForeground(canvas);
}
First step , Yes View Draw against the background of .
private void drawBackground(Canvas canvas) {
// obtain xml Pass through android:background Property or code setBackgroundColor()、setBackgroundResource() Background of assignment by such methods Drawable
final Drawable background = mBackground;
......
// according to layout Determined by the process View Position to set the drawing area of the background
if (mBackgroundSizeChanged) {
background.setBounds(0, 0, mRight - mLeft, mBottom - mTop);
mBackgroundSizeChanged = false;
rebuildOutline();
}
......
// call Drawable Of draw() Method to complete the background rendering
background.draw(canvas);
......
}
The second step , Yes View Draw the contents of .
/** * Implement this to do your drawing. * * @param canvas the canvas on which the background will be drawn */
protected void onDraw(Canvas canvas) {
}
This is an empty method . Because of every View The content parts of are different , So you need subclasses to implement specific logic .
The third step , For the current View All son View Drawing , If the current View No children View You don't need to draw .
Let's see View Of draw Methods dispatchDraw(canvas); Method source code , You can see the following :
protected void dispatchDraw(Canvas canvas) {}
View Of dispatchDraw() Method is an empty method , We need to take a look ViewGroup Of dispatchDraw Method source code ( This is what I just said right now View All son View Drawing , If the current View No children View The reason why you don't need to draw , Because if it is View The method is empty , and ViewGroup Only then can we achieve ), as follows :
@Override
protected void dispatchDraw(Canvas canvas) {
......
final int childrenCount = mChildrenCount;
final View[] children = mChildren;
......
for (int i = 0; i < childrenCount; i++) {
......
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
more |= drawChild(canvas, child, drawingTime);
}
}
......
// Draw any disappearing views that have animations
if (mDisappearingChildren != null) {
......
for (int i = disappearingCount; i >= 0; i--) {
......
more |= drawChild(canvas, child, drawingTime);
}
}
......
}
Can see ,ViewGroup It did rewrite View Of dispatchDraw() Method , The method internally traverses each child View, And then call drawChild() Method , We can look at it ViewGroup Of drawChild Method , as follows :
protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
return child.draw(canvas, this, drawingTime);
}
Can see drawChild() Method to transfer the son View Of draw() Method . So ViewGroup Class has been overridden for us dispatchDraw() Function realization of , We generally don't need to rewrite this method , But you can overload the parent function to achieve specific functions .
Step four , Yes View Scroll bar to draw .
protected final void onDrawScrollBars(Canvas canvas) {
......
}
You can see that actually any one View All have ( Horizontal and vertical ) Scrollbar's , It's just that it's not displayed in general .
Here we are ,View Of draw Drawing part of the source code analysis is completed .
invalidate
We know invalidate()( In the main thread ) and postInvalidate()( Can be in the sub thread ) Are used to request View Redrawing method .
invalidate Method source code analysis :
public void invalidate(Rect dirty) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
// The essence is transportation invalidateInternal Method
invalidateInternal(dirty.left - scrollX, dirty.top - scrollY,
dirty.right - scrollX, dirty.bottom - scrollY, true, false);
}
public void invalidate(int l, int t, int r, int b) {
final int scrollX = mScrollX;
final int scrollY = mScrollY;
// The essence is transportation invalidateInternal Method
invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false);
}
public void invalidate() {
//invalidate The essence of it is transportation invalidateInternal Method
invalidate(true);
}
void invalidate(boolean invalidateCache) {
// The essence is transportation invalidateInternal Method
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
......
// 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;
// Set refresh area
damage.set(l, t, r, b);
// Delivery and transportation Parent ViewGroup Of invalidateChild Method
p.invalidateChild(this, damage);
}
......
}
public final void invalidateChild(View child, final Rect dirty) {
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
......
do {
......
// Circulate the superior transportation layer by layer , until ViewRootImpl Returns the null
parent = parent.invalidateChildInParent(location, dirty);
......
} while (parent != null);
}
This process is finally passed to ViewRootImpl Of invalidateChildInParent Method end , So let's take a look at ViewRootImpl Of invalidateChildInParent Method , as follows :
@Override
public ViewParent invalidateChildInParent(int[] location, Rect dirty) {
......
//View Dispatching invalidate Finally, it is uploaded to ViewRootImpl Finally triggered the method
scheduleTraversals();
......
}
The above code is finally uploaded to ViewRootImpl Finally triggered the method .
postInvalidate
public void postInvalidate() {
postInvalidateDelayed(0);
}
Continue to look at his transportation methods postInvalidateDelayed, as follows :
public void postInvalidateDelayed(long delayMilliseconds) {
// We try only with the AttachInfo because there's no point in invalidating
// if we are not attached to our window
final AttachInfo attachInfo = mAttachInfo;
// The core , The essence is to transfer ViewRootImpl.dispatchInvalidateDelayed Method
if (attachInfo != null) {
attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds);
}
}
We continue to watch his luck ViewRootImpl Class dispatchInvalidateDelayed Method , The following source code :
public void dispatchInvalidateDelayed(View view, long delayMilliseconds) {
Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view);
mHandler.sendMessageDelayed(msg, delayMilliseconds);
}
adopt ViewRootImpl Class Handler Sent a MSG_INVALIDATE news , Continuing to track the processing of this message can find :
public void handleMessage(Message msg) {
......
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
......
}
......
}
handleMessage Running in main thread , So the essence is again UI Thread It's been transferred View Of invalidate(); Method , then View Of invalidate().
requestLayout
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
From this, we can see that the method is changed and continues to call scheduleTraversals()
requestLayout and invalidate What's the difference? ?
We can simply think that mLayoutRequested by true Will trigger perfomMeasure( Internally it will call onMeasure) and performLayout( Internally it will call onLayout). And then in performDraw Inside onDraw In the process of discovery mDirty It's empty , therefore onDraw Will not be called , No redrawing .
So it seems requestLayout Will not lead to onDraw Called ?
Let's continue to recall the setFrame():
protected boolean setFrame(int left, int top, int right, int bottom) {
boolean changed = false;
if (DBG) {
Log.d("View", this + " View.setFrame(" + left + "," + top + ","
+ right + "," + bottom + ")");
}
if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
changed = true;
// Remember our drawn bit
int drawn = mPrivateFlags & PFLAG_DRAWN;
int oldWidth = mRight - mLeft;
int oldHeight = mBottom - mTop;
int newWidth = right - left;
int newHeight = bottom - top;
boolean sizeChanged = (newWidth != oldWidth) || (newHeight != oldHeight);
// Invalidate our old position
invalidate(sizeChanged);
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
mRenderNode.setLeftTopRightBottom(mLeft, mTop, mRight, mBottom);
...
}
We know requestLayout It can lead to perfomMeasure and performLayout, If in layout It was found in the process that l,t,r,b Different from before , Then it will trigger once invalidate. Code in View Of setFrame in , This will be in layout When called . therefore requestLayout Could lead to onDraw Called , It may not lead to onDraw Called , Depending on view Of l,t,r,b Whether to change .
边栏推荐
- 点云数据理解(PointNet实现第3步)
- Oracle EBS interface development - quick generation of JSON format data
- ORACLE 11.2.0.3 不停机处理SYSAUX表空间一直增长问题
- Oracle APEX 21.2 installation et déploiement en une seule touche
- PHP uses the method of collecting to insert a value into the specified position in the array
- oracle apex ajax process + dy 校验
- 第一个快应用(quickapp)demo
- Delete the contents under the specified folder in PHP
- 架构设计三原则
- view的绘制机制(一)
猜你喜欢
Pratique et réflexion sur l'entrepôt de données hors ligne et le développement Bi
TCP attack
User login function: simple but difficult
SSM laboratory equipment management
Network security -- intrusion detection of emergency response
Practice and thinking of offline data warehouse and Bi development
Check log4j problems using stain analysis
Agile development of software development pattern (scrum)
Take you to master the formatter of visual studio code
DNS攻击详解
随机推荐
使用Matlab实现:弦截法、二分法、CG法,求零点、解方程
MySQL组合索引加不加ID
【Torch】解决tensor参数有梯度,weight不更新的若干思路
Take you to master the formatter of visual studio code
SSM personnel management system
oracle apex ajax process + dy 校验
Spark的原理解析
【MEDICAL】Attend to Medical Ontologies: Content Selection for Clinical Abstractive Summarization
Write a thread pool by hand, and take you to learn the implementation principle of ThreadPoolExecutor thread pool
Module not found: Error: Can't resolve './$$_gendir/app/app.module.ngfactory'
Jordan decomposition example of matrix
华为机试题
離線數倉和bi開發的實踐和思考
[introduction to information retrieval] Chapter II vocabulary dictionary and inverted record table
Analysis of MapReduce and yarn principles
Module not found: Error: Can't resolve './$$_ gendir/app/app. module. ngfactory'
SSM garbage classification management system
PHP uses the method of collecting to insert a value into the specified position in the array
解决万恶的open failed: ENOENT (No such file or directory)/(Operation not permitted)
sparksql数据倾斜那些事儿