Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结
版权声明:本文为博主原创文章,未经博主允许不得转载。
微博:厉圣杰
微信公众号:牙锅子
源码:CircleProgress
文中如有纰漏,欢迎大家留言指出。
最近撸了一个圆形进度条的开源项目,算是第一次完完整整的使用自定义 View 。在此对项目开发思路做个小结,欢迎大家 Star 和 Fork
该项目总共实现了三种圆形进度条效果
- CircleProgress:圆形进度条,可以实现仿 QQ 健康计步器的效果,支持配置进度条背景色、宽度、起始角度,支持进度条渐变
- DialProgress:类似 CircleProgress,但是支持刻度
- WaveProgress:实现了水波纹效果的圆形进度条,不支持渐变和起始角度配置,如需此功能可参考 CircleProgress 自行实现。
先上效果图,有图才好说。
CircleProgress 效果图

DialProgress 和 WaveProgress 效果图

恩,那么接下来,就来讲讲怎么实现以上自定义进度条的效果。
圆形进度条
圆形进度条是第一个实现的进度条效果,用了我大半天的时间,实现起来并不复杂。
其思路主要可以分为以下几步:
- View 的测量
- 计算绘制 View 所需参数
- 圆弧的绘制及渐变的实现
- 文字的绘制
- 动画效果的实现
首先,我们要测量出所绘制 View 的大小,即重写 onMeasure()
方法,代码如下:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(MiscUtil.measure(widthMeasureSpec, mDefaultSize),
MiscUtil.measure(heightMeasureSpec, mDefaultSize));
}
由于其他两个进度条类都需要实现 View 的测量,这里对代码进行了封装:
/**
* 测量 View
*
* @param measureSpec
* @param defaultSize View 的默认大小
* @return 测量出来的 View 大小
*/
public static int measure(int measureSpec, int defaultSize) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
return result;
}
关于 View 测量可以看下这篇博客 Android 自定义View 中的onMeasure的用法
接下来,在 onSizeChanged()
中计算绘制圆及文字所需的参数,考虑到屏幕旋转的情况,故未直接在 onMeasure()
方法中直接计算。这里以下面草图来讲解绘制计算过程中的注意事项,图丑,勿怪~

图中,外面蓝色矩形为 View,里面黑色矩形为圆的外接矩形,蓝色矩形和黑色矩形中间空白的地方为 View 的内边距(padding)。两个蓝色的圆其实是一个圆,代表圆的粗细,这是因为 Android 在绘制圆或者圆弧的时候是圆的边宽的中心与外接矩形相交,所以在计算的时候要考虑到内边距(padding) 和 圆与外接矩形的相交。
默认不考虑圆弧的宽度,绘制出来的效果如下:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; oldw = " + oldw + "; oldh = " + oldh);
//求圆弧和背景圆弧的最大宽度
float maxArcWidth = Math.max(mArcWidth, mBgArcWidth);
//求最小值作为实际值
int minSize = Math.min(w - getPaddingLeft() - getPaddingRight() - 2 * (int) maxArcWidth,
h - getPaddingTop() - getPaddingBottom() - 2 * (int) maxArcWidth);
//减去圆弧的宽度,否则会造成部分圆弧绘制在外围
mRadius = minSize / 2;
//获取圆的相关参数
mCenterPoint.x = w / 2;
mCenterPoint.y = h / 2;
//绘制圆弧的边界
mRectF.left = mCenterPoint.x - mRadius - maxArcWidth / 2;
mRectF.top = mCenterPoint.y - mRadius - maxArcWidth / 2;
mRectF.right = mCenterPoint.x + mRadius + maxArcWidth / 2;
mRectF.bottom = mCenterPoint.y + mRadius + maxArcWidth / 2;
//计算文字绘制时的 baseline
//由于文字的baseline、descent、ascent等属性只与textSize和typeface有关,所以此时可以直接计算
//若value、hint、unit由同一个画笔绘制或者需要动态设置文字的大小,则需要在每次更新后再次计算
mValueOffset = mCenterPoint.y - (mValuePaint.descent() + mValuePaint.ascent()) / 2;
mHintOffset = mCenterPoint.y * 2 / 3 - (mHintPaint.descent() + mHintPaint.ascent()) / 2;
mUnitOffset = mCenterPoint.y * 4 / 3 - (mUnitPaint.descent() + mUnitPaint.ascent()) / 2;
updateArcPaint();
Log.d(TAG, "onSizeChanged: 控件大小 = " + "(" + w + ", " + h + ")"
+ "圆心坐标 = " + mCenterPoint.toString()
+ ";圆半径 = " + mRadius
+ ";圆的外接矩形 = " + mRectF.toString());
}
关于 Android 中文字绘制可以参考以下两篇文章:
以上,已经基本完成了 View 绘制所需全部参数的计算。接下来就是绘制圆弧及文字了。
绘制圆弧需要用到 Canvas 的
// oval 为 RectF 类型,即圆弧显示区域
// startAngle 和 sweepAngle 均为 float 类型,分别表示圆弧起始角度和圆弧度数。3点钟方向为0度,顺时针递增
// 如果 startAngle < 0 或者 > 360,则相当于 startAngle % 360
// useCenter:如果为 true 时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形
// 绘制圆弧的画笔
drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint);
为了方便计算,绘制圆弧的时候使用了 Canvas 的 rotate()
方法,对坐标系进行了旋转
private void drawArc(Canvas canvas) {
// 绘制背景圆弧
// 从进度圆弧结束的地方开始重新绘制,优化性能
canvas.save();
float currentAngle = mSweepAngle * mPercent;
canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
// +2 是因为绘制的时候出现了圆弧起点有尾巴的问题
canvas.drawArc(mRectF, currentAngle, mSweepAngle - currentAngle + 2, false, mBgArcPaint);
canvas.drawArc(mRectF, 2, currentAngle, false, mArcPaint);
canvas.restore();
}
恩,圆环已经绘制完成,那么接下来就是实现圆环的渐变,这里使用 SweepGradient
类。SweepGradient
可以实现从中心放射性渐变的效果,如下图:

SweepGradient
类有两个构造方法,
/**
* @param cx 渲染中心点x坐标
* @param cy 渲染中心点y坐标
* @param colors 围绕中心渲染的颜色数组,至少要有两种颜色值
* @param positions 相对位置的颜色数组,可为null, 若为null,可为null,颜色沿渐变线均匀分布。一般不需要设置该参数
/
public SweepGradient(float cx, float cy, int[] colors, float[] positions)
/**
* @param cx 渲染中心点x坐标
* @param cy 渲染中心点y坐标
* @param color0 起始渲染颜色
* @param color1 结束渲染颜色
/
public SweepGradient(float cx, float cy, int color0, int color1)
这里我们选择第一个构造方法。由于设置渐变需要每次都创建一个新的 SweepGradient
对象,所以最好不要放到 onDraw
方法中去更新,最好在初始化的时候就设置好,避免频繁创建导致内存抖动。
private void updateArcPaint() {
// 设置渐变
int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED};
mSweepGradient = new SweepGradient(mCenterPoint.x, mCenterPoint.y, mGradientColors, null);
mArcPaint.setShader(mSweepGradient);
}
这里还有一个值得注意的地方,草图如下

假设,渐变颜色如下:
int[] mGradientColors = {Color.GREEN, Color.YELLOW, Color.RED, Color.BLUE};
因为 SweepGradient
渐变是 360 度的,所以如果你绘制的圆弧只有 270度,则蓝色部分(图中黑色阴影部分)的渐变就会不可见。
接下来,就是文字的绘制了。文字绘制在上述提到的文章中已经进行了详细的讲解,这里就不再赘述。代码如下:
private void drawText(Canvas canvas) {
canvas.drawText(String.format(mPrecisionFormat, mValue), mCenterPoint.x, mValueOffset, mValuePaint);
if (mHint != null) {
canvas.drawText(mHint.toString(), mCenterPoint.x, mHintOffset, mHintPaint);
}
if (mUnit != null) {
canvas.drawText(mUnit.toString(), mCenterPoint.x, mUnitOffset, mUnitPaint);
}
}
最后,我们来实现进度条的动画效果。这里我们使用 Android 的属性动画来实现进度更新。
private void startAnimator(float start, float end, long animTime) {
mAnimator = ValueAnimator.ofFloat(start, end);
mAnimator.setDuration(animTime);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mPercent = (float) animation.getAnimatedValue();
mValue = mPercent * mMaxValue;
if (BuildConfig.DEBUG) {
Log.d(TAG, "onAnimationUpdate: percent = " + mPercent
+ ";currentAngle = " + (mSweepAngle * mPercent)
+ ";value = " + mValue);
}
invalidate();
}
});
mAnimator.start();
}
这里有两个注意点:
- 不要在
ValueAnimator.AnimatorUpdateListener
中输出 Log,特别是动画调用频繁的情况下,因为输出 Log 频繁会生成大量 String 对象造成内存抖动,当然也可以使用StringBuilder
来优化。 - 关于
invalidate()
和postInvalidate()
两者最本质的前者只能在 UI 线程中使用,而后者可以在非 UI 线程中使用,其实postInvalidate()
内部也是使用 Handler 实现的。
关于 Android 属性动画可以参考:
补充:同一个属性如何支持颜色和颜色数组
考虑到圆弧设置单色和渐变的区别,即单色只需要提供一种色值,而渐变至少需要提供两种色值。可以有以下几种解决方案:
- 定义两个属性,渐变的优先级高于单色的。
- 定义一个 format 为
string
属性,以#FFFFFF|#000000
形式提供色值 - 定义一个 format 为
color|reference
的属性,其中reference
属性指代渐变色的数组。
这里选用第三种方案,实现如下:
<!-- 圆形进度条 -->
<declare-styleable name="CircleProgressBar">
<!-- 圆弧颜色, -->
<attr name="arcColors" format="color|reference" />
</declare-styleable>
<!-- colors.xml -->
<color name="green">#00FF00</color>
<color name="blue">#EE9A00</color>
<color name="red">#EE0000</color>
<!-- 渐变颜色数组 -->
<integer-array name="gradient_arc_color">
<item>@color/green</item>
<item>@color/blue</item>
<item>@color/red</item>
</integer-array>
<!-- 布局文件中使用 -->
<!-- 使用渐变 -->
<com.littlejie.circleprogress.DialProgress
android:id="@+id/dial_progress_bar"
android:layout_width="300dp"
android:layout_height="300dp"
app:arcColors="@array/gradient_arc_color" />
<!-- 使用单色 -->
<com.littlejie.circleprogress.DialProgress
android:id="@+id/dial_progress_bar"
android:layout_width="300dp"
android:layout_height="300dp"
app:arcColors="@color/green" />
代码中读取 xml 中配置:
int gradientArcColors = typedArray.getResourceId(R.styleable.CircleProgressBar_arcColors, 0);
if (gradientArcColors != 0) {
try {
int[] gradientColors = getResources().getIntArray(gradientArcColors);
if (gradientColors.length == 0) {//如果渐变色为数组为0,则尝试以单色读取色值
int color = getResources().getColor(gradientArcColors);
mGradientColors = new int[2];
mGradientColors[0] = color;
mGradientColors[1] = color;
} else if (gradientColors.length == 1) {//如果渐变数组只有一种颜色,默认设为两种相同颜色
mGradientColors = new int[2];
mGradientColors[0] = gradientColors[0];
mGradientColors[1] = gradientColors[0];
} else {
mGradientColors = gradientColors;
}
} catch (Resources.NotFoundException e) {
throw new Resources.NotFoundException("the give resource not found.");
}
}
带刻度进度条
前面,详细讲了 CircleProgress 的绘制思路,接下来讲 DialProgress。
实话说,DialProgress 与 CircleProgress 的实现极其相似,因为两者之间其实就差了一个刻度,但考虑到扩展以及类职责的单一,所以将两者分开。
这里主要讲一下刻度的绘制。刻度绘制主要使用 Canvas 类的 save()
、rotate()
和restore()
方法,当然你也可以使用 translate()
方法对坐标系进行平移,方便计算。
/**
* 用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作。
*/
public void save()
/**
* 旋转一定的角度绘制图像
* @param degrees 旋转角度
* @param x 旋转中心点x轴坐标
* @param y 旋转中心点y轴坐标
*/
public void rotate(float degrees, float x, float y)
/**
* 在当前的坐标上平移(x,y)个像素单位
* 若dx <0 ,沿x轴向上平移; dx >0 沿x轴向下平移
* 若dy <0 ,沿y轴向上平移; dy >0 沿y轴向下平移
*/
public void translate(float dx, float dy)
/**
* 用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响。
*/
public void restore()
private void drawDial(Canvas canvas) {
int total = (int) (mSweepAngle / mDialIntervalDegree);
canvas.save();
canvas.rotate(mStartAngle, mCenterPoint.x, mCenterPoint.y);
for (int i = 0; i <= total; i++) {
canvas.drawLine(mCenterPoint.x + mRadius, mCenterPoint.y, mCenterPoint.x + mRadius + mArcWidth, mCenterPoint.y, mDialPaint);
canvas.rotate(mDialIntervalDegree, mCenterPoint.x, mCenterPoint.y);
}
canvas.restore();
}
关于 Canvas 的画布操作可以参考这篇文章:安卓自定义View进阶-Canvas之画布操作
水波纹效果的进度条
水波纹效果的进度条实现需要用到贝塞尔曲线,主要难点在于 绘制区域的计算 和 波浪效果 的实现,其余的逻辑跟上述两种进度条相似。
这里使用了 Path 类,该类在 Android 2D 绘图中是非常重要的,Path 不仅能够绘制简单图形,也可以绘制这些比较复杂的图形。也可以对多个路径进行布尔操作,类似设置 Paint 的 setXfermode()
,具体使用可以参考这篇博客:安卓自定义View进阶-Path基本操作。这里就不再赘述,有机会自己也会对 Android 自定义 View 的知识进行总结,不过,感觉应该了了无期。
继续上示意图,请叫我灵魂画手~

图中黑色的圆为我们要绘制的进度条圆,黑色的曲线为初始状态的的波浪,该波浪使用贝塞尔曲线绘制,其中奇数的点为贝塞尔曲线的起始点,偶数的点为贝塞尔曲线的控制点。例如:1——>2——>3就为一条贝塞尔曲线,1 是起点,2 是控制点,3 是终点。从图中可以看到波浪在园内圆外各一个(1—>5 和 5->9),通过对波浪在 x 轴上做平移,即图中蓝色实线,来实现波浪的动态效果,所以一个波浪的完整动画效果需要有两个波浪来实现。同理,通过控制 y 轴的偏移量,即图中蓝色虚线,可以实现波浪随进度的上涨下降。
贝塞尔曲线上起始点和控制点的计算如下:
/**
* 计算贝塞尔曲线上的起始点和控制点
* @param waveWidth 一个完整波浪的宽度
*/
private Point[] getPoint(float waveWidth) {
Point[] points = new Point[mAllPointCount];
//第1个点特殊处理,即数组的中心
points[mHalfPointCount] = new Point((int) (mCenterPoint.x - mRadius), mCenterPoint.y);
//屏幕内的贝塞尔曲线点
for (int i = mHalfPointCount + 1; i < mAllPointCount; i += 4) {
float width = points[mHalfPointCount].x + waveWidth * (i / 4 - mWaveNum);
points[i] = new Point((int) (waveWidth / 4 + width), (int) (mCenterPoint.y - mWaveHeight));
points[i + 1] = new Point((int) (waveWidth / 2 + width), mCenterPoint.y);
points[i + 2] = new Point((int) (waveWidth * 3 / 4 + width), (int) (mCenterPoint.y + mWaveHeight));
points[i + 3] = new Point((int) (waveWidth + width), mCenterPoint.y);
}
//屏幕外的贝塞尔曲线点
for (int i = 0; i < mHalfPointCount; i++) {
int reverse = mAllPointCount - i - 1;
points[i] = new Point(points[mHalfPointCount].x - points[reverse].x,
points[mHalfPointCount].y * 2 - points[reverse].y);
}
return points;
}
以上,我们已经获取到绘制贝塞尔曲线所需的路径点。接下来,我们就需要来计算出绘制区域,即使用 Path 类。

紫色区域为贝塞尔曲线需要绘制的整体区域。

红色区域为上图紫色区域与圆的交集,也就是波浪要显示的区域
代码如下:
//该方法必须在 Android 19以上的版本才能使用(Path.op())
@TargetApi(Build.VERSION_CODES.KITKAT)
private void drawWave(Canvas canvas, Paint paint, Point[] points, float waveOffset) {
mWaveLimitPath.reset();
mWavePath.reset();
//lockWave 用于判断波浪是否随进度条上涨下降
float height = lockWave ? 0 : mRadius - 2 * mRadius * mPercent;
//moveTo和lineTo绘制出水波区域矩形
mWavePath.moveTo(points[0].x + waveOffset, points[0].y + height);
for (int i = 1; i < mAllPointCount; i += 2) {
mWavePath.quadTo(points[i].x + waveOffset, points[i].y + height,
points[i + 1].x + waveOffset, points[i + 1].y + height);
}
mWavePath.lineTo(points[mAllPointCount - 1].x, points[mAllPointCount - 1].y + height);
//不管如何移动,波浪与圆路径的交集底部永远固定,否则会造成上移的时候底部为空的情况
mWavePath.lineTo(points[mAllPointCount - 1].x, mCenterPoint.y + mRadius);
mWavePath.lineTo(points[0].x, mCenterPoint.y + mRadius);
mWavePath.close();
mWaveLimitPath.addCircle(mCenterPoint.x, mCenterPoint.y, mRadius, Path.Direction.CW);
//取该圆与波浪路径的交集,形成波浪在圆内的效果
mWaveLimitPath.op(mWavePath, Path.Op.INTERSECT);
canvas.drawPath(mWaveLimitPath, paint);
}
以上,就实现了水波动态的效果,当然,你也可以通过配置,来设定水波是否随进度上涨下降。为了实现更好的效果,可以设置一个浅色的水波并支持设置水波的走向(R2L 和 L2R),通过设置浅色波浪和深色波浪动画的时间,从而实现长江后浪推前浪的效果,恩,效果很自然的~自己脑补从右至左波浪的实现和贝塞尔点的计算。
对获取坐标点的代码进行优化:
/**
* 从左往右或者从右往左获取贝塞尔点
*
* @return
*/
private Point[] getPoint(boolean isR2L, float waveWidth) {
Point[] points = new Point[mAllPointCount];
//第1个点特殊处理,即数组的中点
points[mHalfPointCount] = new Point((int) (mCenterPoint.x + (isR2L ? mRadius : -mRadius)), mCenterPoint.y);
//屏幕内的贝塞尔曲线点
for (int i = mHalfPointCount + 1; i < mAllPointCount; i += 4) {
float width = points[mHalfPointCount].x + waveWidth * (i / 4 - mWaveNum);
points[i] = new Point((int) (waveWidth / 4 + width), (int) (mCenterPoint.y - mWaveHeight));
points[i + 1] = new Point((int) (waveWidth / 2 + width), mCenterPoint.y);
points[i + 2] = new Point((int) (waveWidth * 3 / 4 + width), (int) (mCenterPoint.y + mWaveHeight));
points[i + 3] = new Point((int) (waveWidth + width), mCenterPoint.y);
}
//屏幕外的贝塞尔曲线点
for (int i = 0; i < mHalfPointCount; i++) {
int reverse = mAllPointCount - i - 1;
points[i] = new Point((isR2L ? 2 : 1) * points[mHalfPointCount].x - points[reverse].x,
points[mHalfPointCount].y * 2 - points[reverse].y);
}
//对从右向左的贝塞尔点数组反序,方便后续处理
return isR2L ? MiscUtil.reverse(points) : points;
}
至此,自定义圆形进度条相关的思路已全部讲述完成。代码已全部上传至 Git ,欢迎大家 Star 和 Fork,传送门:CircleProgress。
如有不清楚或者错误的地方,欢迎指出~
Android 自定义 View 圆形进度条总结的更多相关文章
- android 自定义图片圆形进度条
感觉话一个圆形进度条挺简单的 ,但是却偏偏给了几张图片让你话,说实话我没接触过,感觉好难,还好百度有大把的资源,一番努力下终于画出来了. 代码如下. package com.etong.cpms.wi ...
- Android 自定义view --圆形百分比(进度条)
转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50334595 注:本文由于是在学习过程中写的,存在大量问题(overdraw onDr ...
- Android自定义圆角矩形进度条2
效果图: 或 方法讲解: (1)invalidate()方法 invalidate()是用来刷新View的,必须是在UI线程中进行工作.比如在修改某个view的显示时, 调用invalidate()才 ...
- Android自定义控件之圆形进度条ImageView
From:http://blog.csdn.net/xiadik/article/details/41648181package com.wangran.beautiful_girl_show.vie ...
- Android 自定义View合集
自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...
- 简单说说Android自定义view学习推荐的方式
这几天比较受关注,挺开心的,嘿嘿. 这里给大家总结一下学习自定义view的一些技巧. 以后写自定义view可能不会写博客了,但是可以开源的我会把源码丢到github上我的地址:https://git ...
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
Android 高手进阶(21) 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明地址:http://blog.csdn.net/xiaanming/article/detail ...
- 自定义VIew——漂亮的圆形进度条
package com.example.firstapp; import java.text.DecimalFormat; import android.annotation.SuppressLint ...
- 【Android 应用开发】 自定义 圆形进度条 组件
转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://down ...
随机推荐
- 9.TCP:传输控制协议
1.TCP功能 TCP向应用层提供面向连接的.可靠的字节流服务.TCP可以认为是一个没有选择确认或否认的滑动窗口协议. TCP将用户数据打包构成报文段:它发送数据后启动一个定时器:另一 ...
- VS生成时复制文件到指定目录
1.右键项目属性,选择生成事件,再点击"编辑后生成事件",可以直接在编辑框内填写命令行,如图: 2.也可以在弹出的编辑框内,写命令,$(ProjectDir)这种是系统的宏路径,具 ...
- Web项目或WCF发布IIS后,如何通过VS2010调试
在做项目的时候,例如WCF服务一般都会将WCF服务承载于控制台应用程序,或者WinForm窗体应用程序,因为这样可以直接在服务代码上打断点,然后就可以调试了.但是项目已经发布了,当然这里我用的本机进行 ...
- FineUI表格模板列Undefined问题
一般是配置文件未添加ClientID="AutoID"引起
- Java自然语言处理NLP工具包
1. Java自然语言处理 LingPipe LingPipe是一个自然语言处理的Java开源工具包.LingPipe目前已有很丰富的功能,包括主题分类(Top Classification).命名实 ...
- Kettle(Pentaho)实现web方式远程执行job或transformation
一.背景 公司在用kettle做数据etl,每做完一个job或transformation发布上线想要立即执行看数据效果的话每次都是找运维同学登陆服务器打开kettle找到对应的文件点击执行,整个过程 ...
- SQL Server事务、隔离级别详解(二十九)
前言 事务一直以来是我最薄弱的环节,也是我打算重新学习SQL Server的出发点,关于SQL Server中事务将分为几节来进行阐述,Always to review the basics. 事务简 ...
- MEAN教程1-MongoDB安装和使用
MEAN是MongoDB.Express.AngularJS和Node.js的缩写.其理念是仅使用JavaScript一种语言来驱动整个应用.其最鲜明的特点有以下几个:1整个应用只使用一种语言:2整个 ...
- [2017.02.04] C++学习记录(1)
编编程语言的目的是帮助程序员以代码的形式表述ideas.编程语言一方面为程序员提供一组关于可以做什么的抽象,另一方面为程序员提供可以被机器执行的轮子.C++编程语言,支持4种编程范式:过程式(Proc ...
- Linux 菜鸟学习笔记--系统分区
硬盘分区 常识 主分区:最多只能有4个 扩展分区:用于突破主分区最多4个的限制 *最多只能有1个 *主分区+扩展分区最多有4个 *不能写入数据,只能包含逻辑分区 逻辑分区 格式化:实际是写入文件系统, ...