- DecorView将要显示的具体内容呈现在PhoneWindow上
- 而所有View的监听事件,都通过WindowManagerService来接收,并通过Activity对象来回调相应的onClickListener
private void performTraversals() {
......
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... }
|
- 一部分必须做的事情写在final的这个被调用的measure方法中
- 另一部分可以让子类*重写的事情写在另一个姊妹方法onMeasure()中,这个方法被measure方法调用,我们所有关心的测量工作都是在onMeasure()中做的,下面的描述中某些地方会略去measure方法
- 首先最外层的ViewGroup的onMeasure方法被调用,然后做这几步
- 遍历每一个子View,做这些事:
- 调用#measureChildWithMargins#方法,这个方法中:
- 使用onMeasure方法传入的MeasureSpec【2】和子View的LayoutParams得到要传给子View的MeasureSpec【3】【4】
- 调用子View的measure方法,传入上面计算出的MeasureSpec
- 如果子View还是ViewGroup,那么回到第一层进行迭代
- 如果子View是View,那么就根据这个MeasureSpec以及自己的特性进行计算,最后调用#setMeasuredDimension#方法,完成自己最后的测量
- 经过上面的迭代,里边所有层的View和ViewGroup应该都测量结束了,取出当前这个子View的测量结果到记录器(对于FrameLayout 可能用最大的字View的大小,对于LinearLayout,可能是高度的累加,所以可能是累加器或是比较器或是别的,取决于具体的ViewGroup)
- 调用#measureChildWithMargins#方法,这个方法中:
- 所有的孩子测量之后,根据记录器中所有子View的测量结果进行计算,调用#setMeasuredDimension#方法传入计算结果,完成自己最后的测量
- 遍历每一个子View,做这些事:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
;
; ; ; i < count; i++) { final View child = getChildAt(i);
// 遍历自己的子View,只要不是GONE的都会参与测量
if (mMeasureAllChildren || child.getVisibility() != GONE) {
//....
measureChildWithMargins(child, widthMeasureSpec, , heightMeasureSpec, );
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth, child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin); //....
}
} //....
setMeasuredDimension(
resolveSizeAndState(maxWidth, widthMeasureSpec, childState), resolveSizeAndState(maxHeight, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT) );
//....
}
|
protected void measureChildWithMargins(View child,
int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) child.getLayoutParams(); // 根据父View的测量规格、父 View自己的Padding、子 View的Margin和已经用掉的空间大小( widthUsed),就能算出子View的 MeasureSpec,具体计算过程要看getChildMeasureSpec方法 int paddingWitdth = mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + widthUsed; int paddingHeight = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + heightUsed; final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,paddingWitdth, lp. width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,paddingHeight, lp. height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); }
|
- spec参数 表示父View的MeasureSpec
- padding参数 父View的Padding+子View的Margin,父View的大小减去这些边距,才能精确算出子View的MeasureSpec的size
- childDimension参数 表示该子View内部LayoutParams属性的值(lp.width或者lp.height)可以是wrap_content、match_parent、一个精确值(an exactly size)
public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
int specMode = MeasureSpec.getMode(spec); //获得父 View给的mode int specSize = MeasureSpec.getSize(spec); //获得父 View给的大小 // 父View给的大小 -自己的Padding+子 View的Margin,得到值才是子 View的大小。 , specSize - padding); ; //初始化值,最后通过这个两个值生成子 View的MeasureSpec ; //初始化值,最后通过这个两个值生成子 View的MeasureSpec
switch (specMode) {}//这里具体的switch case省略,逻辑与表[1]是一样的
//根据上面逻辑条件获取的 mode和size构建 MeasureSpec对象。
return MeasureSpec.makeMeasureSpec(resultSize, resultMode); }
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int measuredWidth = getDefaultSize (getSuggestedMinimumWidth() , widthMeasureSpec ); int measuredHeight = getDefaultSize (getSuggestedMinimumHeight(), heightMeasureSpec); setMeasuredDimension(measuredWidth,measuredHeight); }
|
- 计算测量结束后的宽高(就是计算测量结果)
- 这里计算宽高用的是一个默认方法:getDefaultSize,这个方法接收一个默认长度和一个measureSpec
- 这个方法的计算逻辑是:(见[1])
- 只要measureSpec的mode不是UNSPECIFIED(未确定的),那么就会用这个measureSpec的数值当做View的高度
- 如果measureSpec的mode是UNSPECIFIED,那么就用前面那个默认长度
- 而这个默认长度默认是用getSuggestedMinimumXXX计算方法计算的,它的计算逻辑是:(见[2])
- 由View的Background尺寸和xml中设置的View的minXXX属性(比如android:minHeight)共同决定的
- 而这个默认长度默认是用getSuggestedMinimumXXX计算方法计算的,它的计算逻辑是:(见[2])
- 调用setMeasuredDimension(),传入测量结果
- setMeasuredDimension()可以简单理解就是给mMeasuredWidth和mMeasuredHeight设值,如果这两个值一旦设置了,那么意味着对于这个View的测量结束了
- 另外setMeasuredDimension方法必须在onMeasure方法中调用,不然会抛异常
- (View类默认的onMeasure方法只支持EXACTLY模式,所以如果)在自定义控件的时候不重写onMeasure方法,你就不能使用wrap_content,只能用matchparent和具体的size
- 它们的onMeasure方法系统了都做了重写
- 会先去测量字符或者图片的高度等,然后拿到View本身content这个高度(字符高度等)
- 如果MeasureSpec是AT_MOST,而且View本身content的高度不超出MeasureSpec的size,那么可以直接用View本身content的高度(字符高度等)
[1]
public static int getDefaultSize(int size, int measureSpec) {
int result = size; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (specMode) { case MeasureSpec.UNSPECIFIED: //表示该View的大小父视图未定,设置为默认值 result = size; break; case MeasureSpec.AT_MOST: case MeasureSpec.EXACTLY: result = specSize; break; } return result; }
|
[2]
protected int getSuggestedMinimumWidth() {
return (mBackground == null) ? mMinWidth : max(mMinWidth, mBackground.getMinimumWidth()); }
|
- DecorView可以理解成整个页面的根View
- DecorView是一个FrameLayout,其中包含两个子View,一个是id=statusBarBackground的View、一个是LineaLayout
- id=statusBarBackground的View可以先不管(我也不是特别懂这个View,应该就是statusBar的设置背景的一个控件,方便设置statusBar的背景
- 这个LinearLayout比较重要,它包含一个title和一个content
- title是TitleBar或者ActionBar,如果我们没有设置NoActionBar的Theme,就会有这个东西
- content 就更简单了,setContentView()方法你应该用过吧,android.R.id.content 你应该听过吧,没错就是它
- content是一个FrameLayout,你写的页面布局通过setContentView加进来就成了content的直接子View,是我们打交道最多的View
- 简单来说,Window是一个抽象类,是所有视图的最顶层容器,视图的外观和行为都归他管,不论是背景显示,标题栏还是事件处理都是他管理的范畴,它其实就像是View界的太上皇(虽然能管的事情看似很多,但是没实权,因为抽象类不能直接使用)。
- 而 PhoneWindow 作为 Window 的唯一亲儿子(唯一实现类),自然就是 View 界的皇帝了,PhoneWindow 的权利可是非常大大,不过对于我们来说用处并不大,因为皇帝平时都是躲在深宫里面的,虽然偶尔用特殊方法能见上一面,但想要完全指挥 PhoneWindow 为你工作是很困难的。
- 而上面说的 DecorView 是 PhoneWindow 的一个内部类,其职位相当于小太监,就是跟在 PhoneWindow 身边专业为 PhoneWindow 服务的,除了自己要干活之外,也负责消息的传递,PhoneWindow 的指示通过 DecorView 传递给下面的 View,而下面 View 的信息也通过 DecorView 回传给 PhoneWindow。
- 含义
- MeasureSpec是两个单词组成,翻译过来“测量规格”或者“测量参数”
- MeasureSpec封装了从父容器传递给子容器的布局要求,“传递” 两个字很重要,因为它不是父容器对子容器的布局要求
- 换种说法是:MeasureSpec是由父View的MeasureSpec和子View的LayoutParams通过简单的计算得出一个针对子View的测量要求,这个测量要求就是MeasureSpec
- 表示方法
- 一个MeasureSpec是一个大小跟模式的组合值
- MeasureSpec是一个32位 整型,其中高两位是mode,后面30位存的是size
- mode一共有三种:
- UNSPECIFIED(未确定的) : 父容器对于子容器没有任何限制,子容器想要多大就多大
- EXACTLY: 父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间。
- AT_MOST:子容器可以是声明大小内的任意大小
- 与view尺寸(就是LayoutParams,你在xml的layout_width和layout_height,layout_xxx的值最后都会封装到这个LayoutParams里)的对比
- 一个view可以这么描述自己的尺寸(不用另外配合SIZE使用)
- wrap_content:内容有多少,就定多少
- match_parent:爸妈给多少,就定多少
- size:一个固定的尺寸
- 父view可以这么告诉自己的子view的MODE(需要另外配合SIZE使用)
- UPSPECIFIED:你想要多少,就定多少(这时候SIZE是0)
- AT_MOST:我给你一个尺寸,不要超过它就好
- EXACTLY:就这么大,固定的
- 一个view可以这么描述自己的尺寸(不用另外配合SIZE使用)
- 计算表格,见表[1](不知道是不是固定不变的)
- 这句话为什么这么怪
- 这句话容易引起误会,严格说是这样:爷爷A,父亲B,儿子C,B用A给的东西a和C的东西c生成了一个b,然后把这个b给C,这个过程不断迭代
- 但这样又容易让人感觉怪异,其实换种说法是这样:爷爷A,父亲B,儿子C,B用自己的b和C的c生成了一个新c,把这个新c给C,这样就感觉好理解多了,其实是一样的,因为B的这个b其实就是A给的
(下)父View给的MeasureSpec
(右)子View的LayoutParams
(内容)要给子View的结果MeasureSpec
|
size | match_parent | wrap_content |
EXACTLY |
LayoutParams.size(居然是听子View的!但是可能无法全部显示)
EXACTLY
|
MeasureSpec.size
EXACTLY
|
MeasureSpec.size
AT_MOST(也是不确定,需要到子View那儿去之后再根据具体情况计算)
|
AT_MOST |
LayoutParams.size
EXACTLY
|
MeasureSpec.size
AT_MOST
|
MeasureSpec.size
AT_MOST
|
UNSPECIFIED |
LayoutParams.size
EXACTLY
|
0(一旦没有任何要求和约束,size的值就没有任何意义了,所以一般都直接设置成0)
UNSPECIFIED
|
UNSPECIFIED
|
private void performTraversals() {
......
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width); int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height); ......
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
......
}
|
private static int getRootMeasureSpec(int windowSize, int rootDimension) {
int measureSpec; switch (rootDimension) { case ViewGroup.LayoutParams.MATCH_PARENT: measureSpec = MeasureSpec.makeMeasureSpec(windowSize,MeasureSpec.EXACTLY); break; ...... } return measureSpec; }
|
- 这个方法第一个参数会传入屏幕的宽度和高度:mWith和mHeight
- 而第二个参数传入的lp是WindowManager.LayoutParams,它的lp.width和lp.height的默认值是MATCH_PARENT
- 所以通过getRootMeasureSpec 生成的测量规格MeasureSpec 的mode是MATCH_PARENT ,size是屏幕的高宽
private void performTraversals() {
......
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec); ...... mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); ...... mView.draw(canvas); ...... }
|
public final void layout(int l, int t, int r, int b) {
if (!mSuppressLayout && (mTransition == null || !mTransition.isChangingLayout())) { if (mTransition != null) { mTransition.layoutChange(this); } super.layout(l, t, r, b); } else { mLayoutCalledWhileSuppressed = true; } }
|
- 如果当前ViewGroup未添加LayoutTransition动画,或者LayoutTransition动画此刻并未运行,那么调用super.layout(l, t, r, b)也就是View的layout方法,继而调用到View中的onLayout
- 否则将mLayoutSuppressed设置为true,等待动画完成时再调用requestLayout()
public final void layout(int l, int t, int r, int b) {
.....
boolean changed = setFrame(l, t, r, b);
// 判断View的位置是否发生过变化,看有必要进行重新 layout吗 if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) { if (ViewDebug.TRACE_HIERARCHY) { ViewDebug.trace(this, ViewDebug.HierarchyTraceType. ON_LAYOUT); }
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~LAYOUT_REQUIRED; } mPrivateFlags &= ~FORCE_LAYOUT; ..... }
|
- setFrame(l, t, r, b) 可以理解为给mLeft 、mTop、mRight、mBottom赋值,然后基本就能确定View自己在父视图的位置了,这几个值构成的矩形区域就是该View显示的位置,这里的具体位置都是相对与父视图的位置
- 回调onLayout
- 对于View来说,onLayout只是一个空实现,一般情况下我们也不需要重载该函数
- 对于ViewGroup 来说,它的onLayout 方法多了关键字abstract的修饰,要求其子类必须重载
- 而重载onLayout的目的就是安排其children在父视图的具体位置,那么如何安排子View的具体位置呢?
;i< childCount ;i ++){
View child = getChildAt(i) ; child.layout(l, t, r, b) ; }
|
- 代码很简单,就是遍历自己的孩子,然后调用 child.layout(l, t, r, b) ,给子view 通过setFrame(l, t, r, b) 确定位置
- 而重点是(l, t, r, b) 怎么计算出来的呢
- 还记得我们之前测量过程,测量出来的MeasuredWidth和MeasuredHeight吗?还记得你在xml 设置的Gravity吗?还有RelativeLayout 的其他参数吗,没错,就是这些参数和MeasuredHeight、MeasuredWidth 一起来确定子View在父视图的具体位置的。具体的计算过程大家可以看下最简单FrameLayout 的onLayout 函数的源码,每个不同的ViewGroup 的实现都不一样
- 提一下写在LayoutParams中的布局参数是怎么作用的:
- DecorView在确定给每个View分配的屏幕区域大小时,是允许View参与进来,与它一起商量的,但是每个View在屏幕区域中的位置就不能让View自己来决定了,而是由DecorView一手操办
- 虽然View无法决定自己在ViewGroup中的位置,但是开发者在使用View时,可以向ViewGroup表达自己所用的View要放在哪里,也就是layout_*之类的配置
- layout_*之类的配置虽然在书写上与View的属性在一起,但它们并不是View的属性,它们只是使用该View的使用者用来细化调整该View在ViewGroup中的位置的,同时,这些值在Inflate时,是由ViewGroup读取,然后生成一个ViewGroup特定的LayoutParams对象,再把这个对象存入子View中
- 这样,ViewGroup在为该子View安排位置时,就可以参考这个LayoutParams中的信息了
- 我们发现,调用inflate时,除了输入布局文件的id外,一般要求传入parent ViewGroup,传入这个参数的目的,就是为了读取布局文件中的layout配置信息,如果没有传入,这些信息将会丢失
- 不同的ViewGroup拥有不同的LayoutParams内部类,这是因为,它们所允许的子View微微调整自己的位置的方式是不一样的,具体讲就是配置子View时,允许使用的layout_*是不一样的
- 比如,RelativeLayout就允许layout_toRightOf等配置,其他的ViewGroup没有这些配置
- 这些确定View的位置的过程,被包装在View 的layout方法中,这样我们也很容易理解,对于基本View而言,这个方法是没有用的,所以都是空的,你可以查看下ImageView、TextView等的源代码,验证下这一点。对于ViewGroup而言,它们会用该方法为自己的子View安排位置
- 另外:因为View的最终的布局位置和大小完全由(l, t, r, b) 这4个参数决定,而measure过程产生的MeasuredWidth和MeasuredHeight这两个参数为计算这四个值提供了一个很重要的依据,但是这两个参数也不是必须的,所以measure过程并不是必须的,也就是我们完全可以不使用这两个值,这4个参数完全可以由我们任意指,而如果这样,getMeasuredWidth() 和getWidth() 就很有可能不是同一个值,它们的计算是不一样的:
- public final int getMeasuredWidth() { return mMeasuredWidth & MEASURED_SIZE_MASK; }
- public final int getWidth() { return mRight - mLeft; }
- 还记得我们之前测量过程,测量出来的MeasuredWidth和MeasuredHeight吗?还记得你在xml 设置的Gravity吗?还有RelativeLayout 的其他参数吗,没错,就是这些参数和MeasuredHeight、MeasuredWidth 一起来确定子View在父视图的具体位置的。具体的计算过程大家可以看下最简单FrameLayout 的onLayout 函数的源码,每个不同的ViewGroup 的实现都不一样
- View的draw 方法虽然不是final的,但一般不去重写,官网文档也建议不要去重写draw 方法,一般是重写其中第三步draw内容时回调的onDraw方法
- View 的onDraw(canvas) 是空实现,ViewGroup 也没有实现,每个View的内容是各不相同的,所以需要由子类去实现具体逻辑
- onDraw()函数将会传给你一个 Canvas 对象,通过它你可以在二维图形上做任何事情,包括其他的一些标准和通用的组件、文本的格式,任何你可以想到的东西都可以通过它实现
- Canvas就像是一个画板,使用Paint就可以在上面作画了
- 另外:ViewGroup通常情况下不需要绘制,因为它本身就没有需要绘制的东西,如果不是指定了ViewGroup的背景颜色,那么它的onDraw方法都不会被调用,但是它会使用dispatchDraw方法来绘制自己的子View,过程是遍历所有子View,调用子View的绘制方法
public void draw(Canvas canvas) {
... /* * 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 needed ... background.draw(canvas); ... // skip step 2 & 5 if possible (common case) ... // Step 2, save the canvas' layers ... if (solidColor == 0) { final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; if (drawTop) { canvas.saveLayer(left, top, right, top + length, null, flags); } ... // Step 3, draw the content if (!dirtyOpaque) onDraw(canvas); // Step 4, draw the children dispatchDraw(canvas); // Step 5, draw the fade effect and restore layers if (drawTop) { matrix.setScale(1, fadeHeight * topFadeStrength); matrix.postTranslate(left, top); fade.setLocalMatrix(matrix); canvas.drawRect(left, top, right, top + length, p); } ... // Step 6, draw decorations (scrollbars) onDrawScrollBars(canvas); } } |
private void drawBackground(Canvas canvas) {
Drawable final Drawable background = mBackground; ...... //mRight - mLeft, mBottom - mTop layout确定的四个点来设置背景的绘制区域 if (mBackgroundSizeChanged) { background.setBounds(0, 0, mRight - mLeft, mBottom - mTop); mBackgroundSizeChanged = false; rebuildOutline(); } ...... //调用Drawable的draw() 把背景图片画到画布上 background.draw(canvas); ...... } |
@Override
protected void dispatchDraw(Canvas canvas) { ... if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) { for (int i = 0; i < count; i++) { final View child = children[i]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } } else { for (int i = 0; i < count; i++) { final View child = children[getChildDrawingOrder(count, i)]; if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) { more |= drawChild(canvas, child, drawingTime); } } } ...... } |
- 就是遍历子View然后drawChild()
- drawChild()方法实际调用的是子View.draw()方法
- ViewGroup类已经为我们实现绘制子View的默认过程,这个实现基本能满足大部分需求,所以ViewGroup类的子类(LinearLayout,FrameLayout)也基本没有去重写dispatchDraw方法
- 我们在实现自定义控件,除非比较特别,不然一般也不需要去重写它
- drawChild()的核心过程就是为子视图分配合适的cavas剪切区
- 剪切区的大小正是由layout过程决定的
- 而剪切区的位置取决于滚动值以及子视图当前的动画
- 设置完剪切区后就会调用子视图的draw()函数进行具体的绘制了。
4、第6步 对View的滚动条进行绘制
不是重点,知道有这东西就行,onDrawScrollBars 的一句注释 :Request the drawing of the horizontal and the vertical scrollbar. The scrollbars are painted only if they have been awakened first.
View绘制过程理解的更多相关文章
-
Android View绘制过程
Android的View绘制是从根节点(Activity是DecorView)开始,他是一个自上而下的过程.View的绘制经历三个过程:Measure.Layout.Draw.基本流程如下图: per ...
-
Android View 绘制过程
Android的View绘制是从根节点(Activity是DecorView)开始,他是一个自上而下的过程.View的绘制经历三个过程:Measure.Layout.Draw.基本流程如下图: per ...
-
学习笔记-----Android的View绘制过程
边看源码边参考别人的博客等,做一下学习笔记. 要了解View的绘制,首先得知道View树的结构:(可以参考http://blog.csdn.net/qinjuning/article/details/ ...
-
View绘制详解(四),谝一谝layout过程
上篇博客我们介绍了View的测量过程,这只是View显示过程的第一步,第二步就是layout了,这个我们一般译作布局,其实就是在View测量完成之后根据View的大小,将其一个一个摆放在ViewGro ...
-
Android中View绘制流程以及invalidate()等相关方法分析
[原文]http://blog.csdn.net/qinjuning 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简 ...
-
Android UI 绘制过程浅析(一)LayoutInflater简介
前言 这篇blog是我在阅读过csdn大牛郭霖的<带你一步步深入了解View>一系列文章后,亲身实践并做出的小结.作为有志向的前端开发工程师,怎么可以不搞懂View绘制的基本原理——简直就 ...
-
View绘制详解(五),draw方法细节详解之View的滚动/滑动问题
关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架.View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程.View的绘制涉及 ...
-
Android中View绘制流程以及invalidate()等相关方法分析(转)
转自:http://blog.csdn.net/qinjuning 前言: 本文是我读<Android内核剖析>第13章----View工作原理总结而成的,在此膜拜下作者 .同时真挚地向渴 ...
-
Android UI 绘制过程浅析(三)layout过程
前言 上一篇blog中,了解到measure过程对View进行了测量,得到measuredWidth/measuredHeight.对于ViewGroup,则计算出全部children的宽高进行求和. ...
随机推荐
-
深入理解IoC/DI
------------------------------------------------------------------------ 理解IoC/DI 1.控制反转 --> 谁控制谁 ...
-
关于 mobile sui a外链 老是出现加载失败的解决办法
mobile sui 框架里面的a本身都绑了了一个ajax方法,ajax只能处理同域,跨域就会出现问题 ,所以mobile sui 中的a如果是外链的话就会出现加载失败的提示,这种明显的bug,让用户 ...
-
Java [Leetcode 169]Majority Element
题目描述: Given an array of size n, find the majority element. The majority element is the element that ...
-
给一个非常长的字符串str 另一个字符集比方{a,b,c} 找出str 里包括{a,b,c}的最短子串。要求O(n)
给一个非常长的字符串str 另一个字符集比方{a,b,c} 找出str 里包括{a,b,c}的最短子串.要求O(n). 比方,字符集是a,b,c,字符串是abdcaabcx,则最短子串为abc. 设置 ...
-
泛泰A850 (高通8064+720p)刷4.4专用中文recovery TWRP2.7.1.3版
欢迎关注泛泰非盈利专业第三方开发团队 VegaDevTeam (本team 由 syhost suky zhaochengw(z大) xuefy(大星星) tenfar(R大师) loogeo cr ...
-
[.NET] 《C# 高效编程》(一) - C# 语言习惯
C# 语言习惯 目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 is 或 as 操作符而不是强制类型转换 四.使用 Con ...
-
KFold,StratifiedKFold k折交叉切分
python风控评分卡建模和风控常识(博客主亲自录制视频教程) https://study.163.com/course/introduction.htm?courseId=1005214003&am ...
-
Windows网络发现无法启动
解决方法: 运行services.msc命令,打开服务界面.分别将Function Discovery Resource Publication.SSDP Discovery.UPnP Device ...
-
linux下编译protobuf(可以编译成pb.go)
编译前需要安装gtest $ cd googletest $ cmake -DBUILD_SHARED_LIBS=ON . $ make $ sudo cp -a include/gtest /hom ...
-
HR_Counting Valleys
把字符串数字化之后应该从 i>0开始判断而不是 i>1 因此错了4个testcases. #!/bin/python3 import math import os import rando ...