【转载】FloatingActionButton源码解析
原文地址:https://github.com/Rowandjj/my_awesome_blog/blob/master/fab_anlysis/README.md
loatingActionButton源码解析
背景
FloatingActionButton(下文以fab代替)是android support design组件库中提供的一个视图控件,是material design设计中fab的官方实现。
此控件的官方介绍如下:
Floating action buttons are used for a promoted action. They are distinguished by a circled icon floating above the UI and have motion behaviors that include morphing, launching, and a transferring anchor point.
关于该控件的设计规范及使用场景请参考文档:
http://www.google.com/design/spec/components/buttons-floating-action-button.html#
如果你还不了解design组件库,请参考官方博客:
http://android-developers.blogspot.hk/2015/05/android-design-support-library.html
开始
源码版本:23.3.0

fab间接继承自ImageView(ImageButton是ImageView的子类),因而拥有ImageView的大部分特性。但是其内部还是做了很多定制,我们一一来看。
1. fab的自定义属性、背景着色相关
从构造器开始:
public FloatingActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//检查是否使用Theme.Appcompat主题
ThemeUtils.checkAppCompatTheme(context);
//拿到自定义属性并赋值
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.FloatingActionButton, defStyleAttr,
R.style.Widget_Design_FloatingActionButton);
...
a.recycle();
final int maxImageSize = (int) getResources().getDimension(R.dimen.design_fab_image_size);
mImagePadding = (getSizeDimension() - maxImageSize) / 2;
//背景着色
getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode,
mRippleColor, mBorderWidth);
//绘制阴影
getImpl().setElevation(elevation);
...
}
构造器中主要是拿到用户设置的自定义属性,比如着色、波纹颜色、大小等等,一共有以下几个属性可以定义。
<declare-styleable name="FloatingActionButton">
<attr name="backgroundTint"/>
<attr name="backgroundTintMode"/>
<attr format="color" name="rippleColor"/>
<attr name="fabSize">
<enum name="normal" value="0"/>
<enum name="mini" value="1"/>
</attr>
<attr name="elevation"/>
<attr format="dimension" name="pressedTranslationZ"/>
<attr format="dimension" name="borderWidth"/>
<attr format="boolean" name="useCompatPadding"/>
</declare-styleable>
属性的默认值定义如下:
<style name="Widget.Design.FloatingActionButton" parent="android:Widget">
<item name="android:background">@drawable/design_fab_background</item>
<item name="backgroundTint">?attr/colorAccent</item>
<item name="fabSize">normal</item>
<item name="elevation">@dimen/design_fab_elevation</item>
<item name="pressedTranslationZ">@dimen/design_fab_translation_z_pressed</item>
<item name="rippleColor">?attr/colorControlHighlight</item>
<item name="borderWidth">@dimen/design_fab_border_width</item>
</style>
需要注意的是android:background属性,这里指定了background为design_fab_background,并且不允许改变:
@Override
public void setBackgroundDrawable(Drawable background) {
Log.i(LOG_TAG, "Setting a custom background is not supported.");
}
那么我们来看下这个background长啥样:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/white" />
</shape>
很显然,fab的形状固定为圆形都是因为这个background。那么这里指定了背景色为白色,那是不是fab只能是白色背景呢?当然不是,还有我们牛逼的backgroundTint(即背景着色),tint是android 5.x引进的一个新特性,可以动态地给drawable资源着色,其原理就是通过给控件设置colorFilter:
drawable.java
public void setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
setColorFilter(new PorterDuffColorFilter(color, mode));
}
默认的着色模式为SRC_IN(取交集、显示上层,故底层白色会被忽略):
static final PorterDuff.Mode DEFAULT_TINT_MODE = PorterDuff.Mode.SRC_IN;
在fab构造的时候,会指定着色为?attr/colorAccent,即当前主题的colorAccent属性值。 然后执行如下代码,进行着色。
getImpl().setBackgroundDrawable(mBackgroundTint, mBackgroundTintMode,
mRippleColor, mBorderWidth);
因为不同版本间的实现略有不同,所以这里会根据不同版本创建不同的FloatingActionButtonImpl实现类:
private FloatingActionButtonImpl createImpl() {
final int sdk = Build.VERSION.SDK_INT;
if (sdk >= 21) {
return new FloatingActionButtonLollipop(this, new ShadowDelegateImpl());
} else if (sdk >= 14) {
return new FloatingActionButtonIcs(this, new ShadowDelegateImpl());
} else {
return new FloatingActionButtonEclairMr1(this, new ShadowDelegateImpl());
}
}
以5.x为例,其setBackgroundDrawable实现代码如下:
先创建着色的背景drawable。
GradientDrawable createShapeDrawable() {
GradientDrawable d = new GradientDrawable();
d.setShape(GradientDrawable.OVAL);
d.setColor(Color.WHITE);
return d;
}
再对此drawable设置tint:
@Override
void setBackgroundDrawable(ColorStateList backgroundTint,
PorterDuff.Mode backgroundTintMode, int rippleColor, int borderWidth) {
// Now we need to tint the shape background with the tint
mShapeDrawable = DrawableCompat.wrap(createShapeDrawable()); //着色,这里会其实就是设置了下colorFilter
DrawableCompat.setTintList(mShapeDrawable, backgroundTint); if (backgroundTintMode != null) {
DrawableCompat.setTintMode(mShapeDrawable, backgroundTintMode);
} final Drawable rippleContent;
if (borderWidth > 0) {
mBorderDrawable = createBorderDrawable(borderWidth, backgroundTint);
rippleContent = new LayerDrawable(new Drawable[]{mBorderDrawable, mShapeDrawable});
} else {
mBorderDrawable = null;
rippleContent = mShapeDrawable;
} mRippleDrawable = new RippleDrawable(ColorStateList.valueOf(rippleColor),
rippleContent, null); mContentBackground = mRippleDrawable; mShadowViewDelegate.setBackgroundDrawable(mRippleDrawable);
}
经过着色,fab就呈现出我们想要的颜色啦。
2. fab的大小
再来看fab的大小,fab有两种大小,一种是NORMAL,一种是MINI,实际大小分别是56dp和40dp,其定义可以在design库的values.xml中看到。
fab如何控制控件大小只有这两种规格呢(这样说不准确,事实上你可以通过设置fab的layout_width/layout_height指定为任意大小,但是我们最好按照MD规范来)?必然是通过复写onMeasure啦:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//我们希望的大小
final int preferredSize = getSizeDimension();
//最终测量的大小
final int w = resolveAdjustedSize(preferredSize, widthMeasureSpec);
final int h = resolveAdjustedSize(preferredSize, heightMeasureSpec); //取小值,保证最后绘制的是圆形
final int d = Math.min(w, h); // We add the shadow's padding to the measured dimension
setMeasuredDimension(
d + mShadowPadding.left + mShadowPadding.right,
d + mShadowPadding.top + mShadowPadding.bottom);
}
其中getSizeDimension方法计算出来的是我们期望的大小:
final int getSizeDimension() {
switch (mSize) {
case SIZE_MINI:
return getResources().getDimensionPixelSize(R.dimen.design_fab_size_mini);//40dp
case SIZE_NORMAL:
default:
return getResources().getDimensionPixelSize(R.dimen.design_fab_size_normal);//56dp
}
}
但是最终的值还是得看我们设置的LayoutParams。关于控件测量相关内容不在此文介绍范围内,大家可以自行google。
3.fab的动画
fab还支持fab以动画的方式显现/隐藏,通常和AppBarLayout一起使用,可以通过hide()/show()两个方法控制。
那么动画是如何实现的呢:
private void show(OnVisibilityChangedListener listener, boolean fromUser) {
getImpl().show(wrapOnVisibilityChangedListener(listener), fromUser);
}
private void hide(@Nullable OnVisibilityChangedListener listener, boolean fromUser) {
getImpl().hide(wrapOnVisibilityChangedListener(listener), fromUser);
}
这里因为要兼容不同版本,所以具体实现也交给了不同的fab实现类。3.x之后很好办,直接使用属性动画,如果是3.x之前的话,那么只能使用传统的Animation了
以hide()为例,使用属性动画较为简单,直接使用View#animate()即可链式调用。
@Override
void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
if (mIsHiding || mView.getVisibility() != View.VISIBLE) {
// A hide animation is in progress, or we're already hidden. Skip the call
if (listener != null) {
listener.onHidden();
}
return;
} if (!ViewCompat.isLaidOut(mView) || mView.isInEditMode()) {
// If the view isn't laid out, or we're in the editor, don't run the animation
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
listener.onHidden();
}
} else {
mView.animate().cancel();
mView.animate()
.scaleX(0f)
.scaleY(0f)
.alpha(0f)
.setDuration(SHOW_HIDE_ANIM_DURATION)
.setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR)
.setListener(new AnimatorListenerAdapter() {
private boolean mCancelled; @Override
public void onAnimationStart(Animator animation) {
mIsHiding = true;
mCancelled = false;
mView.internalSetVisibility(View.VISIBLE, fromUser);
} @Override
public void onAnimationCancel(Animator animation) {
mIsHiding = false;
mCancelled = true;
} @Override
public void onAnimationEnd(Animator animation) {
mIsHiding = false;
if (!mCancelled) {
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
listener.onHidden();
}
}
}
});
}
}
如果使用传统动画的话,则先在xml中定义好动画,然后构造Animation实例,启动动画。
@Override
void hide(@Nullable final InternalVisibilityChangedListener listener, final boolean fromUser) {
if (mIsHiding || mView.getVisibility() != View.VISIBLE) {
// A hide animation is in progress, or we're already hidden. Skip the call
if (listener != null) {
listener.onHidden();
}
return;
} Animation anim = android.view.animation.AnimationUtils.loadAnimation(
mView.getContext(), R.anim.design_fab_out);
anim.setInterpolator(AnimationUtils.FAST_OUT_LINEAR_IN_INTERPOLATOR);
anim.setDuration(SHOW_HIDE_ANIM_DURATION);
anim.setAnimationListener(new AnimationUtils.AnimationListenerAdapter() {
@Override
public void onAnimationStart(Animation animation) {
mIsHiding = true;
} @Override
public void onAnimationEnd(Animation animation) {
mIsHiding = false;
mView.internalSetVisibility(View.GONE, fromUser);
if (listener != null) {
listener.onHidden();
}
}
});
mView.startAnimation(anim);
}
4. fab与CoordinatorLayout的交互
这块内容因为与
CoordinatorLayout/CoordinatorLayout#Behavior有很大关联,如果不熟悉,请先google相关资料。本文假设读者对这块内容已经有一定理解。
fab并不直接与CoordinatorLayout联系,而是通过CoordinatorLayout#Behavior作为桥梁。CoordinatorLayout类通过CoordinatorLayout#Behavior可以间接控制其直系子View的行为,能控制什么行为?View测量、布局、touch事件拦截、监听、NestedScroll等等。是不是很屌。
fab内部实现了CoordinatorLayout#Behavior抽象类。该抽象类有如下接口:
public static abstract class Behavior<V extends View> {
...
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
...
/**
* Determine whether the supplied child view has another specific sibling view as a
* layout dependency.
*
* <p>This method will be called at least once in response to a layout request. If it
* returns true for a given child and dependency view pair, the parent CoordinatorLayout
* will:</p>
* <ol>
* <li>Always lay out this child after the dependent child is laid out, regardless
* of child order.</li>
* <li>Call {@link #onDependentViewChanged} when the dependency view's layout or
* position changes.</li>
* </ol>
*/
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
return false;
}
/**
* Respond to a change in a child's dependent view
*
* <p>This method is called whenever a dependent view changes in size or position outside
* of the standard layout flow. A Behavior may use this method to appropriately update
* the child view in response.</p>
*
* <p>A view's dependency is determined by
* {@link #layoutDependsOn(CoordinatorLayout, android.view.View, android.view.View)} or
* if {@code child} has set another view as it's anchor.</p>
*
* <p>Note that if a Behavior changes the layout of a child via this method, it should
* also be able to reconstruct the correct position in
* {@link #onLayoutChild(CoordinatorLayout, android.view.View, int) onLayoutChild}.
* <code>onDependentViewChanged</code> will not be called during normal layout since
* the layout of each child view will always happen in dependency order.</p>
*
* <p>If the Behavior changes the child view's size or position, it should return true.
* The default implementation returns false.</p>
*
*/
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
return false;
}
...
/**
* Called when the parent CoordinatorLayout is about the lay out the given child view.
*
* <p>This method can be used to perform custom or modified layout of a child view
* in place of the default child layout behavior. The Behavior's implementation can
* delegate to the standard CoordinatorLayout measurement behavior by calling
* {@link CoordinatorLayout#onLayoutChild(android.view.View, int)
* parent.onLayoutChild}.</p>
*
* <p>If a Behavior implements
* {@link #onDependentViewChanged(CoordinatorLayout, android.view.View, android.view.View)}
* to change the position of a view in response to a dependent view changing, it
* should also implement <code>onLayoutChild</code> in such a way that respects those
* dependent views. <code>onLayoutChild</code> will always be called for a dependent view
* <em>after</em> its dependency has been laid out.</p>
*
*/
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
return false;
}
...
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
// Do nothing
}
}
看到这个抽象类,有两点需要注意:
- 此抽象类并无抽象方法,也即子类可选择任何想复写的方法进行复写。
- 此抽象类接受一个泛型。该泛型需要是View的子类。
fab实现此抽象类:
public static class Behavior extends CoordinatorLayout.Behavior<FloatingActionButton> {}
有选择性地实现了三个方法:
public boolean layoutDependsOn(CoordinatorLayout parent,
FloatingActionButton child, View dependency); public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
View dependency); public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
int layoutDirection);
fab为啥要实现Behavior?主要是为了配合其他控件完成一些复杂的交互,比较经典的像这个:
fab需要在snackBar弹出的时候自动向上平移,这就得知道SnackBar的状态了,实现Behavior让fab有机会监听到其他CoordinatorLayout子View的状态,并根据状态更新自己。
复写layoutDependsOn方法可以告诉CoordinatorLayout我对哪个View感兴趣,
这里当然是SnackBar了。(注意哦,SnackBar最终展现的是SnackbarLayout,SnackBar本身并不是View)
private static final boolean SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11; @Override
public boolean layoutDependsOn(CoordinatorLayout parent,
FloatingActionButton child, View dependency) {
// We're dependent on all SnackbarLayouts (if enabled)
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
为什么API LEVEL要大于11呢?因为google偷懒想直接使用属性动画。
前面告诉了CoordinatorLayoutfab对SnackBar比较感兴趣,那么当SnackBar状态改变的时候,CoordinatorLayout就会通过onDependentViewChanged回调通知fab:
fab就可以更新自己的UI拉(这里当然是平移喽):
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child,
View dependency) {
if (dependency instanceof Snackbar.SnackbarLayout) {
updateFabTranslationForSnackbar(parent, child, dependency);
} else if (dependency instanceof AppBarLayout) {
// If we're depending on an AppBarLayout we will show/hide it automatically
// if the FAB is anchored to the AppBarLayout
updateFabVisibility(parent, (AppBarLayout) dependency, child);
}
return false;
}
如果是SnackBar状态变化了,那么fab就会根据情况进行平移:
private void updateFabTranslationForSnackbar(CoordinatorLayout parent,
final FloatingActionButton fab, View snackbar) {
final float targetTransY = getFabTranslationYForSnackbar(parent, fab);
if (mFabTranslationY == targetTransY) {
// We're already at (or currently animating to) the target value, return...
return;
} final float currentTransY = ViewCompat.getTranslationY(fab); // Make sure that any current animation is cancelled
if (mFabTranslationYAnimator != null && mFabTranslationYAnimator.isRunning()) {
mFabTranslationYAnimator.cancel();
} if (fab.isShown()
&& Math.abs(currentTransY - targetTransY) > (fab.getHeight() * 0.667f)) {
// If the FAB will be travelling by more than 2/3 of it's height, let's animate
// it instead
if (mFabTranslationYAnimator == null) {
mFabTranslationYAnimator = ViewUtils.createAnimator();
mFabTranslationYAnimator.setInterpolator(
AnimationUtils.FAST_OUT_SLOW_IN_INTERPOLATOR);
mFabTranslationYAnimator.setUpdateListener(
new ValueAnimatorCompat.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimatorCompat animator) {
ViewCompat.setTranslationY(fab,
animator.getAnimatedFloatValue());
}
});
}
mFabTranslationYAnimator.setFloatValues(currentTransY, targetTransY);
mFabTranslationYAnimator.start();
} else {
// Now update the translation Y
ViewCompat.setTranslationY(fab, targetTransY);
} mFabTranslationY = targetTransY;
}
代码里的注释很多,我就不解释了。
前面说到AppBarLayout和fab一起使用可以完成另一个效果,即AppBarLayout伸缩时,fab也可以以动画的形式显现、隐藏,其实现如下:
private boolean updateFabVisibility(CoordinatorLayout parent,
AppBarLayout appBarLayout, FloatingActionButton child) {
final CoordinatorLayout.LayoutParams lp =
(CoordinatorLayout.LayoutParams) child.getLayoutParams();
//注意到我们必须为fab指定layout_anchor为appBarLayout
if (lp.getAnchorId() != appBarLayout.getId()) {
// The anchor ID doesn't match the dependency, so we won't automatically
// show/hide the FAB
return false;
} if (child.getUserSetVisibility() != VISIBLE) {
// The view isn't set to be visible so skip changing it's visibility
return false;
} if (mTmpRect == null) {
mTmpRect = new Rect();
} // First, let's get the visible rect of the dependency
final Rect rect = mTmpRect;
ViewGroupUtils.getDescendantRect(parent, appBarLayout, rect); if (rect.bottom <= appBarLayout.getMinimumHeightForVisibleOverlappingContent()) {
// If the anchor's bottom is below the seam, we'll animate our FAB out
child.hide(null, false);
} else {
// Else, we'll animate our FAB back in
child.show(null, false);
}
return true;
}
除此之外,fab#Behavior还实现了onLayoutChild,主要是为了根据AppBarLayout的当前状态来判断自己是否需要隐藏。
@Override
public boolean onLayoutChild(CoordinatorLayout parent, FloatingActionButton child,
int layoutDirection) {
// First, lets make sure that the visibility of the FAB is consistent
final List<View> dependencies = parent.getDependencies(child);
for (int i = 0, count = dependencies.size(); i < count; i++) {
final View dependency = dependencies.get(i);
if (dependency instanceof AppBarLayout
&& updateFabVisibility(parent, (AppBarLayout) dependency, child)) {
break;
}
}
// Now let the CoordinatorLayout lay out the FAB
parent.onLayoutChild(child, layoutDirection);
// Now offset it if needed
offsetIfNeeded(parent, child);
return true;
}
此方法会在CoordinatorLayout对孩子布局的时候进行调用(即CoordinatorLayout#onLayout),CoordinatorLayout会检查所有的直系孩子,是否设置了Behavior,如果设置了,那么就执行其onLayoutChild方法:
CoordinatorLayout#onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
final int layoutDirection = ViewCompat.getLayoutDirection(this);
final int childCount = mDependencySortedChildren.size();
for (int i = 0; i < childCount; i++) {
final View child = mDependencySortedChildren.get(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Behavior behavior = lp.getBehavior(); if (behavior == null || !behavior.onLayoutChild(this, child, layoutDirection)) {
onLayoutChild(child, layoutDirection);
}
}
}
如果该Behavior实现了OnLayoutChild,并且返回了true,那么将不会执行CoordinatorLayout #onLayoutChild,否则执行默认的布局方案。 最后一点,这里的Behavior如何生效的呢?通过注解:
@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends VisibilityAwareImageButton {
CoordinatorLayout在解析孩子的LayoutParams时,会check有无注解:
LayoutParams getResolvedLayoutParams(View child) {
final LayoutParams result = (LayoutParams) child.getLayoutParams();
if (!result.mBehaviorResolved) {
Class<?> childClass = child.getClass();
DefaultBehavior defaultBehavior = null;
while (childClass != null &&
(defaultBehavior = childClass.getAnnotation(DefaultBehavior.class)) == null) {
childClass = childClass.getSuperclass();
}
if (defaultBehavior != null) {
try {
result.setBehavior(defaultBehavior.value().newInstance());
} catch (Exception e) {
Log.e(TAG, "Default behavior class " + defaultBehavior.value().getName() +
" could not be instantiated. Did you forget a default constructor?", e);
}
}
result.mBehaviorResolved = true;
}
return result;
}
至此fab解析完毕,谢谢观看!
【转载】FloatingActionButton源码解析的更多相关文章
- jQuery整体架构源码解析(转载)
jQuery整体架构源码解析 最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性, ...
- 转载:Bootstrap 源码解析
Bootstrap 源码解析 前言 Bootstrap 是个CSS库,简单,高效.很多都可以忘记了再去网站查.但是有一些核心的东西需要弄懂.个人认为弄懂了这些应该就算是会了.源码看一波. 栅格系统 所 ...
- 【转载】Xutils3源码解析
Github源码地址:https://github.com/wyouflf/xUtils3 原文地址 :http://www.codekk.com/blogs/detail/54cfab086c476 ...
- 【转载】okhttp源码解析
转自:http://www.open-open.com/lib/view/open1472216742720.html https://blog.piasy.com/2016/07/11/Unders ...
- 【转载】Scroller源码解析
原文地址:https://github.com/Skykai521/AndroidSdkSourceAnalysis/blob/master/article/Scroller%E6%BA%90%E7% ...
- 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新
本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新
上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...
- dcraw源码解析
dcraw源码解析 Author:Maddock Date:2015-04-22 转载请注明出处: 首先吐槽一点: 程序中使用了相当多的全局变量, 看的人头大.全局变量的坏处参看 http://wen ...
随机推荐
- svn稀疏目录--通过设置工作目录的深度(depth)实现目录树的部分签出
对于一个大的版本库来说,本地工作目录签出整个目录树是即费时又占地儿的.虽然可以只签出某个子目录树,但有时候还是需要从根目录签出.那么,怎么才能只把自己感兴趣的子目录签出来呢? 从svn1.5版开始,提 ...
- Hadoop 介绍
1.Hadoop简介 Hadoop[hædu:p]实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS.HDFS有高容错性的特点,并且设计用来部署在低 ...
- Spring Web 项目Junit测试报错问题
测试对象是Web项目的Service类,参照网上查到的资料,按如下方式执行时报错, //使用junit4进行单元测试 @RunWith(SpringJUnit4ClassRunner.class) / ...
- SQL Server Compact/SQLite Toolbox
如果你的vs2013 无法打开 .sdf 数据库文件. 那么 SQL Server Compact/SQLite Toolbox,可以帮助你. 下载安装后, vs2013->tools-> ...
- 【BZOJ】2196: [Usaco2011 Mar]Brownie Slicing
[题意]给定n*m的数字矩阵,要求横着切A-1刀,对每块再分别竖着切B-1刀,是最小子矩阵最大. [算法]二分+贪心 [题解]还记得提高组2015跳石头吗?这道题做法一致,只不过拓展到二维而已. 二分 ...
- 20155335俞昆《java程序设计》第10周总结
学号 2016-2017-2 <Java程序设计>第十周学习总结 ## 事实上网络编程,我们可以简单的理解为两台计算机相互通讯数据而已,对于程序员而言,掌握一种编程接口并使用一种编程模型相 ...
- 【CF558E】 A Simple Task (权值线段树)
题目链接 用权值线段树维护每个字母在\([l,r]\)出现的次数,每次修改把每个字母在区间的出现次数记下来,然后清空这段区间,再按顺序插进去就好了. 时间复杂度\(O(n\log n*26)\) (好 ...
- Can you answer these queries?(HDU4027+势能线段树)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 题目: 题意:n个数,每次区间更新将其数值变成它的根号倍(向下取整),区间查询数值和. 思路:易 ...
- MSSQL数据库 事务隔离级别
数据库事务的隔离级别有4个,由低到高依次为Read uncommitted .Read committed .Repeatable read .Serializable ,这四个级别可以逐个解 脏读 ...
- 一个python拖库字段的小脚本
import requests import re all_column = dict() all_db = "db_zf,dg_activity,dg_activity_log,dg_ad ...