原标题:Android 自定义圆形进度条总结

安卓巴士全球开发者论坛【北京站】

安卓巴士全球开发者论坛邀请了在Android开发领域拥有多年经验的老司机,与你相约北京,带上你的开发好友,一起聊聊Android开发中,那些一线程序员技术干货。活动详情:

最近撸了一个圆形进度条的开源项目,算是第一次完完整整的使用自定义 View 。在此对项目开发思路做个小结。

该项目总共实现了三种圆形进度条效果

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;

}

接下来,在 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());

}

以上,已经基本完成了 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 实现的。

补充:同一个属性如何支持颜色和颜色数组

考虑到圆弧设置单色和渐变的区别,即单色只需要提供一种色值,而渐变至少需要提供两种色值。可以有以下几种解决方案:

定义两个属性,渐变的优先级高于单色的。

定义一个 format 为 string 属性,以 #FFFFFF|#000000 形式提供色值

定义一个 format 为 color|reference 的属性,其中 reference 属性指代渐变色的数组。

这里选用第三种方案,实现如下:

#00FF00

#EE9A00

#EE0000

@color/green

@color/blue

@color/red

android:id="@+id/dial_progress_bar"

android:layout_width="300dp"

android:layout_height="300dp"

app:arcColors="@array/gradient_arc_color" />

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();

}

水波纹效果的进度条

水波纹效果的进度条实现需要用到贝塞尔曲线,主要难点在于 绘制区域的计算 和 波浪效果 的实现,其余的逻辑跟上述两种进度条相似。

这里使用了 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;

}

至此,自定义圆形进度条相关的思路已全部讲述完成。

感谢 厉圣杰提供,源码地址:

https://github.com/MyLifeMyTravel/CircleProgress

代码已全部上传至 Git ,欢迎大家 Star 和 Fork。支持小编也可以在下方+1,投稿及有疑问或者问题的小伙伴可以在下方留言,小编会第一时间与您联系!

大家都在看

责任编辑:

android多个点的圆形进度条,Android 自定义圆形进度条总结相关推荐

  1. android绘制环形进度_Android动态自定义圆形进度条

    这篇文章主要介绍了Android动态自定义圆形进度条,需要的朋友可以参考下 效果图: A.绘制圆环,圆弧,文本 //1.画圆环 //原点坐标 float circleX = width / 2; fl ...

  2. Android 开发中原始音频的录播和和自定义音频控制条的讲解及实战(超详细 附源码)

    需要源码请点赞关注收藏后评论区留下QQ~~~ 一.原始音频的录播 语音通话功能要求实时传输,如果使用MediaRecorder与MediaPlayer组合,那么只能整句话都录完并编码好了才能传给对方去 ...

  3. android自定义圆形进度条,实现动态画圆效果

    自定义圆形进度条效果图如下:应用场景如动态显示分数等. view的自定义属性如下attr.xml <?xml version="1.0" encoding="UTF ...

  4. Java渐变进度条_Android ProgressBar自定义图片进度,自定义渐变色进度条

    java.lang.Object ↳android.view.View ↳android.widget.ProgressBar 直接子类 AbsSeekBar 间接子类 RatingBar, Seek ...

  5. 【Android 应用开发】 自定义 圆形进度条 组件

    转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://down ...

  6. Android自定义圆形进度条

    Android自定义圆形进度条 github地址:https://github.com/opq1289/CircleProgressView 效果图: 无动画: 有动画: 整圆: 切割圆: 具体步骤: ...

  7. Android 自定义View,自定义属性--自定义圆形进度条(整理)

    很多的时候,系统自带的View满足不了我们的功能需求,那么我们就需要自定义View来满足我们的需求 自定义View时要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为 ...

  8. Android自定义滑动进度条,Android自定义View实现圆形水波进度条

    每次听到某大牛谈论自定义View,顿时敬佩之心,如滔滔江水连绵不绝,心想我什么时候能有如此境界,好了,心动不如行动,于是我开始了自定义View之路,虽然过程有坎坷,但是结果我还是挺满意的.我知道大牛还 ...

  9. android 圆形渐变背景,android实现圆形渐变进度条

    最近项目中使用到了渐变效果的圆形进度条,网上找了很多渐变效果不够圆滑,两个渐变颜色之间有明显的过渡,或者有些代码画出来的效果过渡不美观,于是自己参照写了一个,喜欢的朋友可以参考或者直接使用. 先上一张 ...

  10. android自定义带进度条的圆形图片

    前言:在项目听新闻的改版中需要实现环绕圆形新闻图片的进度条功能,作为技术预备工作我就去看了一些网上的相关的原理,做了一个自定义带进度条的圆形图片的demo,并将这个实现写成文章发布出来,谁需要了可以进 ...

最新文章

  1. html微信窗口阻止滚动条,解决微信浏览器页面可以随意拉伸,导致页面内部滚动条滚动体验不好的问题...
  2. eclipse svn插件安装_Visual SVN和Tortoise SVN的安装简单使用汉化
  3. C语言面向对象编程(三):虚函数与多态
  4. 优朋普乐大数据_优朋普乐黑维炜:互动电视市场已进入成熟发展期
  5. java 相对路径 文件读取,Java相对路径读取文件
  6. c语言一维数组定义可用字母吗,C语言一维数组的定义和引用
  7. 前端npm install失败
  8. Google的YSlow——Page Speed(附插件下载)
  9. iOS自定义弹出视图、收音机APP、图片涂鸦、加载刷新、文件缓存等源码 1
  10. word文档怎么一分为二_微信文章怎么转成word文档,这个强大工具可以帮你搞定...
  11. java编码规范概述_Java 编码规范 (转)
  12. Python 面向对象一(转载)
  13. PCIe device tree range属性详解
  14. mysql建用户注册登录表_登录注册数据库建立
  15. python爬虫教程书籍-python入门书籍(爬虫方面)有哪些推荐?
  16. 利用Python scapy爬取起点小说网小说
  17. 2019 第十届蓝桥杯Java省赛B组个人题解
  18. 【组队学习】【32期】组队学习内容详情
  19. 江苏高考时间2021成绩查询,小高考时间2021具体时间江苏-江苏小高考成绩查询公布时间及网站...
  20. 1t硬盘怎么分区最好_1TB硬盘如何分区

热门文章

  1. 三菱伺服驱动器示例_三菱伺服驱动器 mr-j4-b 用户使用手册 中文高清版.pdf
  2. 最优化方法:六、约束最优化方法
  3. 普元EOS7.5生成RestFul接口服务
  4. 微信小程序--图片Image几种mode 设置图片方法
  5. 三端稳压管原理、作用、解决发热的问题
  6. TVS管、稳压管、肖特基二极管
  7. react的setState——读《react小书》
  8. 移动开发作业五 近场通信技术分析与未来应用场景预测
  9. 8uftp,8uftp连接不上怎么办
  10. QQ IDKey生成--一键加群