Android View绘制13问13答
来源:互联网 发布:卖家淘宝客 编辑:程序博客网 时间:2024/05/13 18:51
来源:http://www.cnblogs.com/punkisnotdead/p/5181821.html?ref=myread
Android View绘制13问13答
1.View的绘制流程分几步,从哪开始?哪个过程结束以后能看到view?
答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程。draw流程结束以后就可以在屏幕上看到view了。
2.view的测量宽高和实际宽高有区别吗?
答:基本上百分之99的情况下都是可以认为没有区别的。有两种情况,有区别。第一种 就是有的时候会因为某些原因 view会多次测量,那第一次测量的宽高 肯定和最后实际的宽高 是不一定相等的,但是在这种情况下
最后一次测量的宽高和实际宽高是一致的。此外,实际宽高是在layout流程里确定的,我们可以在layout流程里 将实际宽高写死 写成硬编码,这样测量的宽高和实际宽高就肯定不一样了,虽然这么做没有意义 而且也不好。
3.view的measureSpec 由谁决定?顶级view呢?
答:由view自己的layoutparams和父容器 一起决定自己的measureSpec。一旦确定了spec,onMeasure中就可以确定view的宽高了。
顶级view就稍微特殊一点,对于decorView的测量在ViewRootImpl的源码里。
1 2 3 //desire的这2个参数就代表屏幕的宽高, 4 childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width); 5 childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height); 6 performMeasure(childWidthMeasureSpec, childHeightMeasureSpec); 7 8 9 10 //decorView的measureSpec就是在这里确定的,其实比普通view的measurespec要简单的多11 //代码就不分析了 一目了然的东西12 private static int getRootMeasureSpec(int windowSize, int rootDimension) {13 int measureSpec;14 switch (rootDimension) {15 16 case ViewGroup.LayoutParams.MATCH_PARENT:17 // Window can't resize. Force root view to be windowSize.18 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);19 break;20 case ViewGroup.LayoutParams.WRAP_CONTENT:21 // Window can resize. Set max size for root view.22 measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);23 break;24 default:25 // Window wants to be an exact size. Force root view to be that size.26 measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);27 break;28 }29 return measureSpec;30 }
4.对于普通view来说,他的measure过程中,与父view有关吗?如果有关,这个父view也就是viewgroup扮演了什么角色?
答:看源码:
1 //对于普通view的measure来说 是由这个view的 父view ,也就是viewgroup来触发的。 2 //也就是下面这个measureChildWithMargins方法 3 4 protected void measureChildWithMargins(View child, 5 int parentWidthMeasureSpec, int widthUsed, 6 int parentHeightMeasureSpec, int heightUsed) { 7 //第一步 先取得子view的 layoutParams 参数值 8 final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); 9 10 //然后开始计算子view的spec的值,注意这里看到 计算的时候除了要用子view的 layoutparams参数以外 11 //还用到了父view 也就是viewgroup自己的spec的值 12 final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, 13 mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin 14 + widthUsed, lp.width); 15 final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, 16 mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin 17 + heightUsed, lp.height); 18 19 child.measure(childWidthMeasureSpec, childHeightMeasureSpec); 20 } 21 22 23 24 25 26 27 //这个算view的spec的方法 看上去一大串 但是真的逻辑非常简单 就是根据父亲viewgroup 28 //的meaurespec 同时还有view自己的params来确定 view自己的measureSpec。 29 //注意这里的参数是padding,这个值的含义是 父容器已占用的控件的大小 所以view的Specsize 30 //的值 你们可以看到 是要减去这个padding的值的。总大小-已经用的 =可用的。 很好理解。 31 32 //然后就是下面的switch逻辑 要自己梳理清楚。其实也不难,主要是下面几条原则 33 //如果view采用固定宽高,也就是写死的数值那种。那就不管父亲的spec的值了,view的spec 就肯定是exactly 并且大小遵循layout参数里设置的大小。 34 35 //如果view的宽高是match_parent ,那么就要看父容器viewgroup的 spec的值了,如果父view的spec是exactly模式, 36 //那view也肯定是exactly,并且大小就是父容器剩下的空间。如果父容器是at_most模式,那view也是at_most 并且不会超过剩余空间大小 37 38 //如果view的宽高是wrap_content, 那就不管父容器的spec了,view的spec一定是at_most 并且不会超过父view 剩余空间的大小。 39 40 41 public static int getChildMeasureSpec(int spec, int padding, int childDimension) { 42 int specMode = MeasureSpec.getMode(spec); 43 int specSize = MeasureSpec.getSize(spec); 44 45 int size = Math.max(0, specSize - padding); 46 47 int resultSize = 0; 48 int resultMode = 0; 49 50 switch (specMode) { 51 // Parent has imposed an exact size on us 52 case MeasureSpec.EXACTLY: 53 if (childDimension >= 0) { 54 resultSize = childDimension; 55 resultMode = MeasureSpec.EXACTLY; 56 } else if (childDimension == LayoutParams.MATCH_PARENT) { 57 // Child wants to be our size. So be it. 58 resultSize = size; 59 resultMode = MeasureSpec.EXACTLY; 60 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 61 // Child wants to determine its own size. It can't be 62 // bigger than us. 63 resultSize = size; 64 resultMode = MeasureSpec.AT_MOST; 65 } 66 break; 67 68 // Parent has imposed a maximum size on us 69 case MeasureSpec.AT_MOST: 70 if (childDimension >= 0) { 71 // Child wants a specific size... so be it 72 resultSize = childDimension; 73 resultMode = MeasureSpec.EXACTLY; 74 } else if (childDimension == LayoutParams.MATCH_PARENT) { 75 // Child wants to be our size, but our size is not fixed. 76 // Constrain child to not be bigger than us. 77 resultSize = size; 78 resultMode = MeasureSpec.AT_MOST; 79 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 80 // Child wants to determine its own size. It can't be 81 // bigger than us. 82 resultSize = size; 83 resultMode = MeasureSpec.AT_MOST; 84 } 85 break; 86 87 // Parent asked to see how big we want to be 88 case MeasureSpec.UNSPECIFIED: 89 if (childDimension >= 0) { 90 // Child wants a specific size... let him have it 91 resultSize = childDimension; 92 resultMode = MeasureSpec.EXACTLY; 93 } else if (childDimension == LayoutParams.MATCH_PARENT) { 94 // Child wants to be our size... find out how big it should 95 // be 96 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size; 97 resultMode = MeasureSpec.UNSPECIFIED; 98 } else if (childDimension == LayoutParams.WRAP_CONTENT) { 99 // Child wants to determine its own size.... find out how100 // big it should be101 resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;102 resultMode = MeasureSpec.UNSPECIFIED;103 }104 break;105 }106 return MeasureSpec.makeMeasureSpec(resultSize, resultMode);107 }
5.view的meaure和onMeasure有什么关系?
答:看源码:
1 //view的measure是final 方法 我们子类无法修改的。 2 public final void measure(int widthMeasureSpec, int heightMeasureSpec) { 3 boolean optical = isLayoutModeOptical(this); 4 if (optical != isLayoutModeOptical(mParent)) { 5 Insets insets = getOpticalInsets(); 6 int oWidth = insets.left + insets.right; 7 int oHeight = insets.top + insets.bottom; 8 widthMeasureSpec = MeasureSpec.adjust(widthMeasureSpec, optical ? -oWidth : oWidth); 9 heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);10 }11 12 // Suppress sign extension for the low bytes13 long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;14 if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);15 16 if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||17 widthMeasureSpec != mOldWidthMeasureSpec ||18 heightMeasureSpec != mOldHeightMeasureSpec) {19 20 // first clears the measured dimension flag21 mPrivateFlags &= ~PFLAG_MEASURED_DIMENSION_SET;22 23 resolveRtlPropertiesIfNeeded();24 25 int cacheIndex = (mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ? -1 :26 mMeasureCache.indexOfKey(key);27 if (cacheIndex < 0 || sIgnoreMeasureCache) {28 // measure ourselves, this should set the measured dimension flag back29 onMeasure(widthMeasureSpec, heightMeasureSpec);30 mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;31 } else {32 long value = mMeasureCache.valueAt(cacheIndex);33 // Casting a long to int drops the high 32 bits, no mask needed34 setMeasuredDimensionRaw((int) (value >> 32), (int) value);35 mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;36 }37 38 // flag not set, setMeasuredDimension() was not invoked, we raise39 // an exception to warn the developer40 if ((mPrivateFlags & PFLAG_MEASURED_DIMENSION_SET) != PFLAG_MEASURED_DIMENSION_SET) {41 throw new IllegalStateException("View with id " + getId() + ": "42 + getClass().getName() + "#onMeasure() did not set the"43 + " measured dimension by calling"44 + " setMeasuredDimension()");45 }46 47 mPrivateFlags |= PFLAG_LAYOUT_REQUIRED;48 }49 50 mOldWidthMeasureSpec = widthMeasureSpec;51 mOldHeightMeasureSpec = heightMeasureSpec;52 53 mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |54 (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension55 }56 57 //不过可以看到的是在measure方法里调用了onMeasure方法58 //所以就能知道 我们在自定义view的时候一定是重写这个方法!59 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {60 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),61 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));62 }
6.简要分析view的measure流程?
答:先回顾问题4,viewgroup 算出子view的spec以后 会调用子view的measure方法,而子view的measure方法 我们问题5也看过了实际上是调用的onMeasure方法
所以我们只要分析好onMeasure方法即可,注意onMeasure方法的参数 正是他的父view算出来的那2个spec的值(这里view的measure方法会把这个spec里的specSize值做略微的修改 这个部分 不做分析 因为measure方法修改specSize的部分很简单)。
1 //可以看出来这个就是setMeasuredDimension方法的调用 这个方法看名字就知道就是确定view的测量宽高的 2 //所以我们分析的重点就是看这个getDefaultSize 方法 是怎么确定view的测量宽高的 3 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 4 setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), 5 getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); 6 } 7 8 9 //这个方法特别简单 基本可以认为就是近似的返回spec中的specSize,除非你的specMode是UNSPECIFIED10 //UNSPECIFIED 这个一般都是系统内部测量才用的到,这种时候返回size 也就是getSuggestedMinimumWidth的返回值11 public static int getDefaultSize(int size, int measureSpec) {12 int result = size;13 int specMode = MeasureSpec.getMode(measureSpec);14 int specSize = MeasureSpec.getSize(measureSpec);15 16 switch (specMode) {17 case MeasureSpec.UNSPECIFIED:18 result = size;19 break;20 case MeasureSpec.AT_MOST:21 case MeasureSpec.EXACTLY:22 result = specSize;23 break;24 }25 return result;26 }27 28 //跟view的背景相关 这里不多做分析了29 protected int getSuggestedMinimumWidth() {30 return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth());31 }
7.自定义view中 如果onMeasure方法 没有对wrap_content 做处理 会发生什么?为什么?怎么解决?
答:如果没有对wrap_content做处理 ,那即使你在xml里设置为wrap_content.其效果也和match_parent相同。看问题4的分析。我们可以知道view自己的layout为wrap,那mode就是at_most(不管父亲view是什么specmode).
这种模式下宽高就是等于specSize(getDefaultSize函数分析可知),而这里的specSize显然就是parentSize的大小。也就是父容器剩余的大小。那不就和我们直接设置成match_parent是一样的效果了么?
解决方式就是在onMeasure里 针对wrap 来做特殊处理 比如指定一个默认的宽高,当发现是wrap_content 就设置这个默认宽高即可。
8.ViewGroup有onMeasure方法吗?为什么?
答:没有,这个方法是交给子类自己实现的。不同的viewgroup子类 肯定布局都不一样,那onMeasure索性就全部交给他们自己实现好了。
9.为什么在activity的生命周期里无法获得测量宽高?有什么方法可以解决这个问题吗?
答:因为measure的过程和activity的生命周期 没有任何关系。你无法确定在哪个生命周期执行完毕以后 view的measure过程一定走完。可以尝试如下几种方法 获取view的测量宽高。
1 //重写activity的这个方法 2 public void onWindowFocusChanged(boolean hasFocus) { 3 super.onWindowFocusChanged(hasFocus); 4 if (hasFocus) { 5 int width = tv.getMeasuredWidth(); 6 int height = tv.getMeasuredHeight(); 7 Log.v("burning", "width==" + width); 8 Log.v("burning", "height==" + height); 9 10 }11 }
或者重写这个方法
1 @Override 2 protected void onStart() { 3 super.onStart(); 4 tv.post(new Runnable() { 5 @Override 6 public void run() { 7 int width = tv.getMeasuredWidth(); 8 int height = tv.getMeasuredHeight(); 9 }10 });11 }
再或者:
1 @Override 2 protected void onStart() { 3 super.onStart(); 4 ViewTreeObserver observer = tv.getViewTreeObserver(); 5 observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { 6 @Override 7 public void onGlobalLayout() { 8 int width = tv.getMeasuredWidth(); 9 int height = tv.getMeasuredHeight();10 tv.getViewTreeObserver().removeOnGlobalLayoutListener(this);11 }12 });13 }
10.layout和onLayout方法有什么区别?
答:layout是确定本身view的位置 而onLayout是确定所有子元素的位置。layout里面 就是通过serFrame方法设设定本身view的 四个顶点的位置。这4个位置以确定 自己view的位置就固定了
然后就调用onLayout来确定子元素的位置。view和viewgroup的onlayout方法都没有写。都留给我们自己给子元素布局
11.draw方法 大概有几个步骤?
答: 一共是4个步骤, 绘制背景---------绘制自己--------绘制chrildren----绘制装饰。
12.setWillNotDraw方法有什么用?
答:这个方法在view里。
1 /** 2 * If this view doesn't do any drawing on its own, set this flag to 3 * allow further optimizations. By default, this flag is not set on 4 * View, but could be set on some View subclasses such as ViewGroup. 5 * 6 * Typically, if you override {@link #onDraw(android.graphics.Canvas)} 7 * you should clear this flag. 8 * 9 * @param willNotDraw whether or not this View draw on its own10 */11 public void setWillNotDraw(boolean willNotDraw) {12 setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);13 }
用于设置标志位的 也就是说 如果你的自定义view 不需要draw的话,就可以设置这个方法为true。这样系统知道你这个view 不需要draw 可以优化执行速度。viewgroup 一般都默认设置这个为true,因为viewgroup多数都是只负责布局
不负责draw的。而view 这个标志位 默认一般都是关闭的。
13.自定义view 有哪些需要注意的点?
答:主要是要处理wrap_content 和padding。否则xml 那边设置这2个属性就根本没用了。还有不要在view中使用handler 因为人家已经提供了post方法。如果是继承自viewGroup,那在onMeasure和onLayout里面 也要考虑
padding和layout的影响。也就是说specSize 要算一下 。最后就是如果view的动画或者线程需要停止,可以考虑在onDetachedFromWindow里面来做。
针对上述的几点,给出几个简单的自定义view 供大家理解。
给出一个圆形的view 范例:
1 package com.example.administrator.motioneventtest; 2 3 import android.content.Context; 4 import android.graphics.Canvas; 5 import android.graphics.Color; 6 import android.graphics.Paint; 7 import android.util.AttributeSet; 8 import android.view.View; 9 10 /**11 * Created by Administrator on 2016/2/4.12 */13 public class CircleView extends View {14 15 private int mColor = Color.RED;16 private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);17 18 private void init() {19 mPaint.setColor(mColor);20 }21 22 @Override23 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {24 super.onMeasure(widthMeasureSpec, heightMeasureSpec);25 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);26 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);27 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);28 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);29 30 //处理为wrap_content时的情况31 if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {32 setMeasuredDimension(200, 200);33 } else if (widthSpecMode == MeasureSpec.AT_MOST) {34 setMeasuredDimension(200, heightSpecSize);35 } else if (heightSpecMode == MeasureSpec.AT_MOST) {36 setMeasuredDimension(widthSpecSize, 200);37 }38 39 }40 41 @Override42 protected void onDraw(Canvas canvas) {43 super.onDraw(canvas);44 //处理padding的情况45 final int paddingLeft = getPaddingLeft();46 final int paddingRight = getPaddingRight();47 final int paddingTop = getPaddingTop();48 final int paddingBottom = getPaddingBottom();49 50 51 int width = getWidth() - paddingLeft - paddingRight;52 int height = getHeight() - paddingTop - paddingBottom;53 int radius = Math.min(width, height) / 2;54 canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint);55 }56 57 public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {58 super(context, attrs, defStyleAttr);59 init();60 }61 62 public CircleView(Context context) {63 super(context);64 init();65 66 }67 68 public CircleView(Context context, AttributeSet attrs) {69 super(context, attrs);70 init();71 }72 }
然后下面再给出一个范例,稍微复杂一点是自定义viewgroup了(主要是加强对onMeasure和onLayout的理解), 需求如下:
一个水平的viewgroup,内部的子元素 为了简单 我们假定他们的宽高都是一样的。来写一个这样的简单的viewgroup。
1 package com.example.administrator.motioneventtest; 2 3 import android.content.Context; 4 import android.util.AttributeSet; 5 import android.util.Log; 6 import android.view.View; 7 import android.view.ViewGroup; 8 9 /**10 * Created by Administrator on 2016/2/4.11 */12 //这里我们只处理了padding的状态 没有处理margin的状态,子view的margin 对measure和layout的影响13 //就留给读者自己完成了14 public class CustomHorizontalLayout extends ViewGroup {15 16 //设置默认的控件最小是多少 这里不提供自定义属性了 写死在代码里 你们可以自行拓展17 final int minHeight = 0;18 final int minWidth = 0;19 20 21 public CustomHorizontalLayout(Context context) {22 super(context);23 }24 25 public CustomHorizontalLayout(Context context, AttributeSet attrs) {26 super(context, attrs);27 }28 29 public CustomHorizontalLayout(Context context, AttributeSet attrs, int defStyleAttr) {30 super(context, attrs, defStyleAttr);31 }32 33 @Override34 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {35 super.onMeasure(widthMeasureSpec, heightMeasureSpec);36 int measureWidth = 0;37 int measureHeight = 0;38 final int childCount = getChildCount();39 measureChildren(widthMeasureSpec, heightMeasureSpec);40 int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);41 int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);42 int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);43 int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);44 final View childView = getChildAt(0);45 final int paddingLeft = getPaddingLeft();46 final int paddingRight = getPaddingRight();47 final int paddingTop = getPaddingTop();48 final int paddingBottom = getPaddingBottom();49 //没有子控件 时 我们的宽高要作特殊处理50 if (childCount == 0) {51 //当没有子控件时,如果长宽有一个为wrap 那么就让这个控件以最小的形式展现52 //这里我们最小设置为053 if (widthSpecMode == MeasureSpec.AT_MOST || heightSpecMode == MeasureSpec.AT_MOST) {54 setMeasuredDimension(minWidth, minHeight);55 } else {56 //否则根据我们的layout属性来57 setMeasuredDimension(getLayoutParams().width, getLayoutParams().height);58 }59 60 } else if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {61 measureWidth = childView.getMeasuredWidth() * childCount;62 measureHeight = childView.getMeasuredHeight();63 setMeasuredDimension(paddingLeft + measureWidth + paddingRight, paddingTop + measureHeight + paddingBottom);64 } else if (heightSpecMode == MeasureSpec.AT_MOST) {65 measureHeight = childView.getMeasuredHeight();66 setMeasuredDimension(paddingLeft + paddingRight + widthSpecSize, paddingTop + paddingBottom + measureHeight);67 } else if (widthSpecMode == MeasureSpec.AT_MOST) {68 measureWidth = childView.getMeasuredWidth() * childCount;69 setMeasuredDimension(paddingLeft + paddingRight + measureWidth, paddingTop + paddingBottom + heightSpecSize);70 }71 }72 73 @Override74 protected void onLayout(boolean changed, int l, int t, int r, int b) {75 final int paddingLeft = getPaddingLeft();76 final int paddingRight = getPaddingRight();77 final int paddingTop = getPaddingTop();78 final int paddingBottom = getPaddingBottom();79 //左边初始位置为080 int childLeft = 0 + paddingLeft;81 final int childCount = getChildCount();82 for (int i = 0; i < childCount; i++) {83 final View childView = getChildAt(i);84 if (childView.getVisibility() != View.GONE) {85 final int childWidth = childView.getMeasuredWidth();86 childView.layout(childLeft, 0 + paddingTop, childLeft + childWidth, paddingTop + childView.getMeasuredHeight());87 childLeft += childWidth;88 }89 }90 }91 }
- Android View绘制13问13答
- Android View绘制13问13答
- Android View绘制13问13答
- Android View绘制13问13答
- Android View 绘制 13 问 13 答
- [Android][转]Android View绘制13问13答
- Android View绘制的13问13答。
- Android View绘制机制 13问13答
- Android View绘制的13问13答。
- 【View】Android View绘制机制
- Android 如何 绘制View
- android如何绘制view
- Android View绘制流程
- Android View绘制流程
- Android如何绘制View
- Android View绘制流程
- Android View绘制流程
- Android View绘制流程
- JQuery实现多文件上传
- Sqlite全面学习(一)
- HTML使用图像
- iOS对象的序列化
- java回调
- Android View绘制13问13答
- 编程10问 自己的问题整理
- 2015.08.17 Ubuntu 14.04+cuda 7.0+caffe安装配置
- SharePoint2013 IT Professional - Check Out
- Frida-跨平台注入工具基础篇
- 26 string
- Nutch2.3.1源码开发环境搭建
- 常用的正则表达式(RegExp)
- Inventor API: 隐藏工程图视图中的某个surface body对应的图线