关于View的测量代码是如何执行到的以及performMeasure的具体流程可以参见上一篇笔记
之前忘了说明 view的绘制流程里面用到的设计模式是模板设计模式 可以参考这篇文章
https://blog.csdn.net/u011109881/article/details/82453771
模板设计模式就是父类制定了操作流程(空方法)等着子类去实现(类似hook的用法)
上一篇讲到ViewRootImpl的performTraversals 方法种执行了三个方法
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);//关键方法1
performLayout(lp, mWidth, mHeight);//关键方法2
performDraw();//关键方法3
并着重分析了performMeasure的后续流程 我们从这里开始接着讲述View的绘制流程

一.View的绘制整体流程

如前所述 View的绘制分成三步
1.performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
整个测量过程 由外至内传递MeasureSpec 有了父布局的MeasureSpec再计算子view 然后一级级由内至外执行measure方法
子布局的大小是由父布局和子布局一起决定的
2.performLayout(lp, mWidth, mHeight);//关键方法2
用于摆放布局 大致流程是for循环遍历子view 使用setChildFrame来摆放每一个布局
3.performDraw();//关键方法3
用于绘制自己还有子view(包括背景等)

二.继performMeasure之后执行的方法performLayout方法

ViewRootImpl performLayout(lp, mWidth, mHeight);–>View layout()–>onLayout()

        private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {mLayoutRequested = false;mScrollMayChange = true;mInLayout = true;final View host = mView;if (host == null) {return;}if (DEBUG_ORIENTATION || DEBUG_LAYOUT) {Log.v(mTag, "Laying out " + host + " to (" +host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")");}Trace.traceBegin(Trace.TRACE_TAG_VIEW, "layout");try {host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());//关键点mInLayout = false;int numViewsRequestingLayout = mLayoutRequesters.size();if (numViewsRequestingLayout > 0) {// requestLayout() was called during layout.// If no layout-request flags are set on the requesting views, there is no problem.// If some requests are still pending, then we need to clear those flags and do// a full request/measure/layout pass to handle this situation.ArrayList<View> validLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters,false);if (validLayoutRequesters != null) {// Set this flag to indicate that any further requests are happening during// the second pass, which may result in posting those requests to the next// frame insteadmHandlingLayoutInLayoutRequest = true;// Process fresh layout requests, then measure and layoutint numValidRequests = validLayoutRequesters.size();for (int i = 0; i < numValidRequests; ++i) {final View view = validLayoutRequesters.get(i);Log.w("View", "requestLayout() improperly called by " + view +" during layout: running second layout pass");view.requestLayout();}measureHierarchy(host, lp, mView.getContext().getResources(),desiredWindowWidth, desiredWindowHeight);mInLayout = true;host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());mHandlingLayoutInLayoutRequest = false;// Check the valid requests again, this time without checking/clearing the// layout flags, since requests happening during the second pass get noop'dvalidLayoutRequesters = getValidLayoutRequesters(mLayoutRequesters, true);if (validLayoutRequesters != null) {final ArrayList<View> finalRequesters = validLayoutRequesters;// Post second-pass requests to the next framegetRunQueue().post(new Runnable() {@Overridepublic void run() {int numValidRequests = finalRequesters.size();for (int i = 0; i < numValidRequests; ++i) {final View view = finalRequesters.get(i);Log.w("View", "requestLayout() improperly called by " + view +" during second layout pass: posting in next frame");view.requestLayout();}}});}}}} finally {Trace.traceEnd(Trace.TRACE_TAG_VIEW);}mInLayout = false;}

View

    public void layout(int l, int t, int r, int b) {if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;}int oldL = mLeft;int oldT = mTop;int oldB = mBottom;int oldR = mRight;boolean changed = isLayoutModeOptical(mParent) ?setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {onLayout(changed, l, t, r, b);//关键点if (shouldDrawRoundScrollbar()) {if(mRoundScrollbarRenderer == null) {mRoundScrollbarRenderer = new RoundScrollbarRenderer(this);}} else {mRoundScrollbarRenderer = null;}mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;ListenerInfo li = mListenerInfo;if (li != null && li.mOnLayoutChangeListeners != null) {ArrayList<OnLayoutChangeListener> listenersCopy =(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();int numListeners = listenersCopy.size();for (int i = 0; i < numListeners; ++i) {listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);}}}final boolean wasLayoutValid = isLayoutValid();mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;if (!wasLayoutValid && isFocused()) {mPrivateFlags &= ~PFLAG_WANTS_FOCUS;if (canTakeFocus()) {// We have a robust focus, so parents should no longer be wanting focus.clearParentsWantFocus();} else if (getViewRootImpl() == null || !getViewRootImpl().isInLayout()) {// This is a weird case. Most-likely the user, rather than ViewRootImpl, called// layout. In this case, there's no guarantee that parent layouts will be evaluated// and thus the safest action is to clear focus here.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);clearParentsWantFocus();} else if (!hasParentWantsFocus()) {// original requestFocus was likely on this view directly, so just clear focusclearFocusInternal(null, /* propagate */ true, /* refocus */ false);}// otherwise, we let parents handle re-assigning focus during their layout passes.} else if ((mPrivateFlags & PFLAG_WANTS_FOCUS) != 0) {mPrivateFlags &= ~PFLAG_WANTS_FOCUS;View focused = findFocus();if (focused != null) {// Try to restore focus as close as possible to our starting focus.if (!restoreDefaultFocus() && !hasParentWantsFocus()) {// Give up and clear focus once we've reached the top-most parent which wants// focus.focused.clearFocusInternal(null, /* propagate */ true, /* refocus */ false);}}}if ((mPrivateFlags3 & PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT) != 0) {mPrivateFlags3 &= ~PFLAG3_NOTIFY_AUTOFILL_ENTER_ON_LAYOUT;notifyEnterOrExitForAutoFillIfNeeded(true);}}

跟到最后我们发现View onLayout是个空实现 那么如果以我们上一篇举的例子

<?xml version="1.0" encoding="utf-8"?><!-- step2 在xml中使用自定义属性 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/outer"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"tools:context=".MainActivity"><LinearLayoutandroid:id="@+id/inner"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="text1" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="text2" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="text3" /></LinearLayout></LinearLayout>

对于第二层的LinearLayout inner而言 执行的就是LinearLayout 的onLayout方法

    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {if (mOrientation == VERTICAL) {layoutVertical(l, t, r, b);} else {layoutHorizontal(l, t, r, b);}}

因为我指定了属性排序方式为horizontal 那么我们看看子布局如何摆放

    void layoutHorizontal(int left, int top, int right, int bottom) {final boolean isLayoutRtl = isLayoutRtl();//默认falsefinal int paddingTop = mPaddingTop;int childTop;//子布局开始摆放的y坐标int childLeft;//子布局开始摆放的x坐标// Where bottom of child should gofinal int height = bottom - top;int childBottom = height - mPaddingBottom;// Space available for childint childSpace = height - paddingTop - mPaddingBottom;//可以摆放的高度(需要减去父容器的上下padding)final int count = getVirtualChildCount();final int majorGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;final int minorGravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;final boolean baselineAligned = mBaselineAligned;final int[] maxAscent = mMaxAscent;final int[] maxDescent = mMaxDescent;final int layoutDirection = getLayoutDirection();switch (Gravity.getAbsoluteGravity(majorGravity, layoutDirection)) {//根据LinearLayout不同的Gravity 计算childLeft(子节点的坐标起点x坐标)case Gravity.RIGHT:// mTotalLength contains the padding alreadychildLeft = mPaddingLeft + right - left - mTotalLength;break;case Gravity.CENTER_HORIZONTAL:// mTotalLength contains the padding alreadychildLeft = mPaddingLeft + (right - left - mTotalLength) / 2;break;case Gravity.LEFT:default:childLeft = mPaddingLeft;break;}int start = 0;int dir = 1;//In case of RTL, start drawing from the last child.if (isLayoutRtl) {start = count - 1;dir = -1;}for (int i = 0; i < count; i++) {final int childIndex = start + dir * i;final View child = getVirtualChildAt(childIndex);if (child == null) {//跳过childLeft += measureNullChild(childIndex);} else if (child.getVisibility() != GONE) {final int childWidth = child.getMeasuredWidth();final int childHeight = child.getMeasuredHeight();int childBaseline = -1;final LinearLayout.LayoutParams lp =(LinearLayout.LayoutParams) child.getLayoutParams();if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) {childBaseline = child.getBaseline();}int gravity = lp.gravity;if (gravity < 0) {gravity = minorGravity;}switch (gravity & Gravity.VERTICAL_GRAVITY_MASK) {//根据子view的gravity计算y方向的起始坐标(每一个子view都是从0开始计算)case Gravity.TOP:childTop = paddingTop + lp.topMargin;if (childBaseline != -1) {childTop += maxAscent[INDEX_TOP] - childBaseline;}break;case Gravity.CENTER_VERTICAL:// Removed support for baseline alignment when layout_gravity or// gravity == center_vertical. See bug #1038483.// Keep the code around if we need to re-enable this feature// if (childBaseline != -1) {//     // Align baselines vertically only if the child is smaller than us//     if (childSpace - childHeight > 0) {//         childTop = paddingTop + (childSpace / 2) - childBaseline;//     } else {//         childTop = paddingTop + (childSpace - childHeight) / 2;//     }// } else {childTop = paddingTop + ((childSpace - childHeight) / 2)+ lp.topMargin - lp.bottomMargin;break;case Gravity.BOTTOM:childTop = childBottom - childHeight - lp.bottomMargin;if (childBaseline != -1) {int descent = child.getMeasuredHeight() - childBaseline;childTop -= (maxDescent[INDEX_BOTTOM] - descent);}break;default:childTop = paddingTop;break;}if (hasDividerBeforeChildAt(childIndex)) {childLeft += mDividerWidth;}childLeft += lp.leftMargin;setChildFrame(child, childLeft + getLocationOffset(child), childTop,childWidth, childHeight);//摆放当前循环的child的位置childLeft += childWidth + lp.rightMargin +getNextLocationOffset(child);//累加 记录下一个摆放child的x起点位置 (注意对比y方向起始点的计算)i += getChildrenSkipCount(child, childIndex);}}}

最终是调用setChildFrame去摆放每一个子view

三.继performLayout之后执行的方法performDraw方法

ViewRootImpl performDraw()–>draw(fullRedrawNeeded);–>drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)
View–>mView.draw(canvas);
draw的几个绘制步骤中有三个比较重要:
1 drawBackground(canvas);画背景
2 onDraw(canvas);绘制自己(ViewGroup默认不会调用原因以及如何修改这一特点参见https://blog.csdn.net/u011109881/article/details/110729417)
3 dispatchDraw(canvas);绘制子view

    private void performDraw() {...try {draw(fullRedrawNeeded);//关键点} finally {mIsDrawing = false;Trace.traceEnd(Trace.TRACE_TAG_VIEW);}...}private void draw(boolean fullRedrawNeeded) {...if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset, scalingRequired, dirty)) {return;}...}private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty) {...try {canvas.translate(-xoff, -yoff);if (mTranslator != null) {mTranslator.translateCanvas(canvas);}canvas.setScreenDensity(scalingRequired ? mNoncompatDensity : 0);attachInfo.mSetIgnoreDirtyState = false;mView.draw(canvas);//关键点drawAccessibilityFocusedDrawableIfNeeded(canvas);} finally {if (!attachInfo.mSetIgnoreDirtyState) {// Only clear the flag if it was not set during the mView.draw() callattachInfo.mIgnoreDirtyState = false;}}...return true;}

View

    public void draw(Canvas canvas) {final int privateFlags = mPrivateFlags;mPrivateFlags = (privateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DRAWN;/** Draw traversal performs several drawing steps which must be executed* in the appropriate order:**      1. Draw the background*      2. If necessary, save the canvas' layers to prepare for fading*      3. Draw view's content*      4. Draw children*      5. If necessary, draw the fading edges and restore layers*      6. Draw decorations (scrollbars for instance)*/// Step 1, draw the background, if neededint saveCount;drawBackground(canvas);// skip step 2 & 5 if possible (common case)final int viewFlags = mViewFlags;boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;if (!verticalEdges && !horizontalEdges) {// Step 3, draw the contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);// Step 7, draw the default focus highlightdrawDefaultFocusHighlight(canvas);if (debugDraw()) {debugDrawFocus(canvas);}// we're done...return;}/** Here we do the full fledged routine...* (this is an uncommon case where speed matters less,* this is why we repeat some of the tests that have been* done above)*/boolean drawTop = false;boolean drawBottom = false;boolean drawLeft = false;boolean drawRight = false;float topFadeStrength = 0.0f;float bottomFadeStrength = 0.0f;float leftFadeStrength = 0.0f;float rightFadeStrength = 0.0f;// Step 2, save the canvas' layersint paddingLeft = mPaddingLeft;final boolean offsetRequired = isPaddingOffsetRequired();if (offsetRequired) {paddingLeft += getLeftPaddingOffset();}int left = mScrollX + paddingLeft;int right = left + mRight - mLeft - mPaddingRight - paddingLeft;int top = mScrollY + getFadeTop(offsetRequired);int bottom = top + getFadeHeight(offsetRequired);if (offsetRequired) {right += getRightPaddingOffset();bottom += getBottomPaddingOffset();}final ScrollabilityCache scrollabilityCache = mScrollCache;final float fadeHeight = scrollabilityCache.fadingEdgeLength;int length = (int) fadeHeight;// clip the fade length if top and bottom fades overlap// overlapping fades produce odd-looking artifactsif (verticalEdges && (top + length > bottom - length)) {length = (bottom - top) / 2;}// also clip horizontal fades if necessaryif (horizontalEdges && (left + length > right - length)) {length = (right - left) / 2;}if (verticalEdges) {topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));drawTop = topFadeStrength * fadeHeight > 1.0f;bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));drawBottom = bottomFadeStrength * fadeHeight > 1.0f;}if (horizontalEdges) {leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));drawLeft = leftFadeStrength * fadeHeight > 1.0f;rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));drawRight = rightFadeStrength * fadeHeight > 1.0f;}saveCount = canvas.getSaveCount();int topSaveCount = -1;int bottomSaveCount = -1;int leftSaveCount = -1;int rightSaveCount = -1;int solidColor = getSolidColor();if (solidColor == 0) {if (drawTop) {topSaveCount = canvas.saveUnclippedLayer(left, top, right, top + length);}if (drawBottom) {bottomSaveCount = canvas.saveUnclippedLayer(left, bottom - length, right, bottom);}if (drawLeft) {leftSaveCount = canvas.saveUnclippedLayer(left, top, left + length, bottom);}if (drawRight) {rightSaveCount = canvas.saveUnclippedLayer(right - length, top, right, bottom);}} else {scrollabilityCache.setFadeColor(solidColor);}// Step 3, draw the contentonDraw(canvas);// Step 4, draw the childrendispatchDraw(canvas);// Step 5, draw the fade effect and restore layersfinal Paint p = scrollabilityCache.paint;final Matrix matrix = scrollabilityCache.matrix;final Shader fade = scrollabilityCache.shader;// must be restored in the reverse order that they were savedif (drawRight) {matrix.setScale(1, fadeHeight * rightFadeStrength);matrix.postRotate(90);matrix.postTranslate(right, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(rightSaveCount, p);} else {canvas.drawRect(right - length, top, right, bottom, p);}}if (drawLeft) {matrix.setScale(1, fadeHeight * leftFadeStrength);matrix.postRotate(-90);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(leftSaveCount, p);} else {canvas.drawRect(left, top, left + length, bottom, p);}}if (drawBottom) {matrix.setScale(1, fadeHeight * bottomFadeStrength);matrix.postRotate(180);matrix.postTranslate(left, bottom);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(bottomSaveCount, p);} else {canvas.drawRect(left, bottom - length, right, bottom, p);}}if (drawTop) {matrix.setScale(1, fadeHeight * topFadeStrength);matrix.postTranslate(left, top);fade.setLocalMatrix(matrix);p.setShader(fade);if (solidColor == 0) {canvas.restoreUnclippedLayer(topSaveCount, p);} else {canvas.drawRect(left, top, right, top + length, p);}}canvas.restoreToCount(saveCount);drawAutofilledHighlight(canvas);// Overlay is part of the content and draws beneath Foregroundif (mOverlay != null && !mOverlay.isEmpty()) {mOverlay.getOverlayView().dispatchDraw(canvas);}// Step 6, draw decorations (foreground, scrollbars)onDrawForeground(canvas);if (debugDraw()) {debugDrawFocus(canvas);}}

再次注意draw里面使用了设计模式种的模板设计模式 即 规定了方法的顺序(模板) 但是可能没给出具体的实现,具体的实现由不同的子类根据各自的情况实现。
注意draw步骤
* 1. Draw the background
* 2. If necessary, save the canvas’ layers to prepare for fading
* 3. Draw view’s content
* 4. Draw children
* 5. If necessary, draw the fading edges and restore layers
* 6. Draw decorations (scrollbars for instance)

四 思考

比如 addView setVisibility invalidate setBackground等都会调用到performTraversals然后触发view的绘制流程 但是可能跳过某些步骤
代码优化:onDraw的调用由外到内 又由内至外 非常复杂 尽量不要嵌套 并且不要过度依赖性能优化工具,再厉害的工具往往也有检测不出的问题 了解源码的基本运行原理才是王道

红橙Darren视频笔记 view的绘制流程(下)基于API27相关推荐

  1. 红橙Darren视频笔记 view的绘制流程(上) onMeasure测量代码分析 基于API27

    一.准备工作Activity的onCreate和onResume调用过程 从ActivityThread的handleLaunchActivity开始进行代码跟踪 private void handl ...

  2. 红橙Darren视频笔记 view的invalidate调用draw方法的流程(源码分析基于api 29)

    问题描述 在界面上的一个控件(比如一个button)调用了invalidate 代码流程是什么样的? 首先一个控件基本要么是View 要么是ViewGroup 因为ViewGroup继承自View 他 ...

  3. 红橙Darren视频笔记 View事件分发源码分析 基于API29

    首先Android的事件分发是基于责任链设计模式的 如果不理解责任链设计 可以参考: https://blog.csdn.net/u011109881/article/details/59631314 ...

  4. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  5. 红橙Darren视频笔记 UML图简介

    整体架构复制自红橙原视频的课堂笔记 因为他这一课没有博客,所以没有转载链接,CSDN没有转载地址是无法作为转载类型的文章发表的,暂时标记为原创 参考链接 https://blog.csdn.net/r ...

  6. 红橙Darren视频笔记 代理模式 动态代理和静态代理

    红橙Darren视频笔记 代理模式 动态代理和静态代理(Android API 25) 关于代理模式我之前有过相关的介绍: https://blog.csdn.net/u011109881/artic ...

  7. 红橙Darren视频笔记 类加载机制(API28) 自己写个热修复 查看源码网站

    第一部分 类加载机制 一个Activity是如何被Android虚拟机找到的? 在之前的文章 红橙Darren视频笔记 自定义View总集篇(https://blog.csdn.net/u011109 ...

  8. 红橙Darren视频笔记 利用阿里巴巴AndFix进行热修复

    注意 由于AndFix在2017年左右就停止更新了,在最新版本的apk上遇到很多问题,我最终也没有成功进行热修复.本节主要是学习热修复的原理 在上一篇 红橙Darren视频笔记 自己捕获异常并保存到本 ...

  9. 红橙Darren视频笔记 Behavior的工作原理源码分析

    主要coordinatorlayout的代码来自coordinatorlayout-1.0.0-sources.jar 本文从源码介绍 CoordinatorLayout 的 behavior 怎么工 ...

最新文章

  1. CopyCat 代码克隆检测发布,剑指开源软件抄袭
  2. python 如何判断一个函数执行完成_Python核心编程的四大神兽迭代器、生成器 、闭包以及装饰器...
  3. 博主谈:聊聊我们说的网站优化
  4. 智能判断图片中是否存在某物体_智能家居组件漫谈——人体传感器
  5. Linux系统调用--getrusage函数详解
  6. solr4 mysql自动更新_(solr系列:五) solr定时实时重建索引和增量更新
  7. c语言内存分配与释放 不同类别变量的内存分配和释放的区别
  8. 漫话:如何给女朋友解释什么是删库跑路?
  9. python 声音强度检测_python – 从声音文件中检测频率
  10. (第六章)UI--PS 基础 图层蒙版与混合模式
  11. 创维E900V21E、融合机DT741、咪咕MGV2000-非高安版-通刷线刷固件
  12. 产品研发流程的四个里程碑
  13. 用最优控制视角看微分几何下的测地线
  14. 优先队列(priority_queue)总结
  15. Python-Django毕业设计钓鱼爱好者交流平台(程序+Lw)
  16. python opencv入门 鼠标绘图(4)
  17. hdu 1524 A Chess Game 博弈
  18. 数据挖掘著名案例——啤酒与尿布
  19. Spring 如何解决循环依赖的问题
  20. 玛雅预言2012的误区

热门文章

  1. react中的state、props、ref
  2. C# 调用命令行,参数有空格
  3. [原]sencha touch之表单(login demo)
  4. 动态规划——最长公共子串,没有比这更通俗易懂的了
  5. PAT乙级(1019 数字黑洞)
  6. 怎么实现抢票软件_怎么样在windows上实现文件预览功能?一个软件搞定,提高效率...
  7. pca主成分分析结果解释_主成分分析(PCA)原理精讲 | 统计学专题
  8. linux中exit和fatal区别,关于Linux系统命令中exit与exit的区别
  9. 【连载】如何掌握openGauss数据库核心技术?秘诀三:拿捏存储技术(6)
  10. MySQL 表空间加密插件 Keyring