自定义控件之下载控件1(DownloadView1)
来源:互联网 发布:数码宝贝第三部知乎 编辑:程序博客网 时间:2024/04/29 03:06
前段时间在干货集中营看到了两个炫酷的下载按钮:
可惜是隔壁 iOS 的孩子,怎么办,我也好喜欢,emmm,某该,只能自己模仿着实现一下了。先从第一个入手(第二个波浪效果暂时还不会)。
1 准备动作
写过几次自定义控件后,我也掌握了基本姿势,首先我们将第一个控件肢解了,我大致将它分为五步,第一步,把冰箱门打开,第二步,把大象。。。去去去,要啥自行车自行车。第一步:先画出一个圆,圆环颜色有点像浅青色,由深青色填充,圆中间有一个常见的下载的箭头;第二步:下载箭头的竖线收缩变成一个点;第三步;下载箭头的下半部分往上变成了一条横线,同时刚才竖线收缩成的点往上移动到圆环正上方;第四步;下载箭头的横线收缩变成一个点,同时移动到圆环正上方的点向从圆的两边分别画半圆直到画完整个圆环;第五步:下载横线收缩成的点变成绿色的对勾。
OK,了解了分解动作后,我们先新建一个控件 DownloadView1,然后初始化背景色和画笔,确定一个半径:
public class DownloadView1 extends View { private Paint mPaint; private float radius = 150; //圆半径 public DownloadView1(Context context) { this(context, null); } public DownloadView1(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public DownloadView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setStrokeWidth(radius / 10); mPaint.setStrokeCap(Paint.Cap.ROUND); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setBackgroundColor(Color.rgb(10, 39, 46)); }}
2 画圆和箭头
下载箭头的竖线可以直接用 drawLine() 方法画,箭头可以用 drawPath() 方法画,在构造方法中新建一个 Path 对象,然后定义一个 lineLength 为半径的一半。
public class DownloadView1 extends View { private Paint mPaint; private Path mPath; private float radius = 150; //圆半径 private float lineLength = radius / 2; //下载的标志中间的竖线的长度 private float arrowTurningPointY = radius / 2; //下载的标志下面箭头的转折点的 Y 轴坐标 private float arrowLength = radius / 2; //下载的标志下面箭头变成水平线后的长度 public DownloadView1(Context context) { this(context, null); } public DownloadView1(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public DownloadView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setStrokeWidth(radius / 10); mPaint.setStrokeCap(Paint.Cap.ROUND); mPath = new Path(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setBackgroundColor(Color.rgb(10, 39, 46)); } @Override protected void onDraw(Canvas canvas) { mPaint.setColor(Color.rgb(10, 39, 46)); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, mPaint); mPaint.setColor(Color.rgb(37, 66, 73)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, mPaint); mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawLine(getMeasuredWidth() / 2, getMeasuredHeight() / 2 - lineLength , getMeasuredWidth() / 2, getMeasuredHeight() / 2 + lineLength , mPaint); mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); mPath.reset(); mPath.moveTo(getMeasuredWidth() / 2 - arrowLength, getMeasuredHeight() / 2); mPath.lineTo(getMeasuredWidth() / 2, getMeasuredHeight() / 2 + arrowTurningPointY); mPath.lineTo(getMeasuredWidth() / 2 + arrowLength, getMeasuredHeight() / 2); canvas.drawPath(mPath, mPaint); }}
然后我们就可以在这个基础上去实现动画了,后面为了节省篇幅,就只贴关键代码了,主要看思路,最终的完整代码在最后贴出来。
3 下载箭头竖线收缩成一个点
我们需要一个变量来控制这个竖线的长度,通过属性动画来让这个长度越来越短就行了:
//画下载箭头 mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); if (lineLength == 0) { //下载箭头的竖线收缩成点后应该画点 canvas.drawPoint(getMeasuredWidth() / 2, linePointY, mPaint); } else { //否则应该画线 canvas.drawLine(getMeasuredWidth() / 2, getMeasuredHeight() / 2 - lineLength , getMeasuredWidth() / 2, getMeasuredHeight() / 2 + lineLength , mPaint); }
然后写一个 startAnimation() 方法,点击后调用这个方法:
private void startAnimation() { //每个动画设置不同的时长 lineLengthAnimator.setDuration(500); //添加动画监听,实际上就是一直重绘 lineLengthAnimator.addUpdateListener(mAnimatorUpdateListener); //设置动画的播放顺序 AnimatorSet mAnimatorSet = new AnimatorSet(); mAnimatorSet.play(lineLengthAnimator);mAnimatorSet.start();}
private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } };
4 下载箭头的箭头部分变成横线,同时竖线的圆点上移
下面箭头变成横线,我们则需要修改 path 中路径中的转折点,我定义的是 arrowTurningPointY,先让它移到圆心,然后往下移一点再移回来形成抖一下的效果,同时下载箭头竖线变成的点在 Y 轴的坐标也要变,从圆心移到圆环正上方,定义了 linePointY 来控制,所以增加几个动画,修改一个 startAnimation() 方法:
private void startAnimation() { //下载箭头竖线收缩成点的动画 ObjectAnimator lineLengthAnimator = ObjectAnimator.ofFloat(this, "lineLength", radius / 2, 0); //下载箭头竖线收缩成点后的动画1(下载箭头的箭头变成横线) ObjectAnimator arrowTurningPointYAnimator1 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", radius / 2, 0); //下载箭头竖线收缩成点后的动画2(下载箭头的箭头抖一下的效果) ObjectAnimator arrowTurningPointYAnimator2 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", 0, radius / 4, 0); //下载箭头竖线收缩成点后的在 Y 轴的坐标变化的动画 ObjectAnimator linePointYAnimator = ObjectAnimator.ofFloat(this, "linePointY", getMeasuredHeight() / 2, getMeasuredHeight() / 2 - radius); //每个动画设置不同的时长 lineLengthAnimator.setDuration(500); arrowTurningPointYAnimator1.setDuration(500); arrowTurningPointYAnimator2.setDuration(500); linePointYAnimator.setDuration(500); //添加动画监听,实际上就是一直重绘 lineLengthAnimator.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator1.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator2.addUpdateListener(mAnimatorUpdateListener); linePointYAnimator.addUpdateListener(mAnimatorUpdateListener); //设置动画的播放顺序 AnimatorSet mAnimatorSet = new AnimatorSet(); mAnimatorSet.play(arrowTurningPointYAnimator1).after(lineLengthAnimator); mAnimatorSet.play(arrowTurningPointYAnimator2).after(arrowTurningPointYAnimator1); mAnimatorSet.play(linePointYAnimator).after(arrowTurningPointYAnimator1); mAnimatorSet.start(); }
5 画两个半圆,下载箭头变成的横线收缩成一个点
这个动画也比较简单,要画两段圆弧,圆弧扫过的角度不断增加,直到形成两个半圆,即最后形成一个圆环,定义一个 sweepAngle 来控制,还有下载箭头箭头部分收缩成点,定义 arrowLength 来控制这个长度:
画半圆:
//画两个半圆 mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawArc(mRectF, -90, sweepAngle, false, mPaint); canvas.drawArc(mRectF, -90, 0 - sweepAngle, false, mPaint);
箭头部分:
mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); if (arrowLength == 0) { //下载箭头的箭头部分收缩成点后应该画点 canvas.drawPoint(getMeasuredWidth() / 2, tickTurningPointY, mPaint); drawOk = true; } else { //否则应该画箭头 mPath.reset(); mPath.moveTo(getMeasuredWidth() / 2 - arrowLength, getMeasuredHeight() / 2); mPath.lineTo(getMeasuredWidth() / 2, getMeasuredHeight() / 2 + arrowTurningPointY); mPath.lineTo(getMeasuredWidth() / 2 + arrowLength, getMeasuredHeight() / 2); canvas.drawPath(mPath, mPaint); }
修改 startAnimation() 方法:
private void startAnimation() { //下载箭头竖线收缩成点的动画 ObjectAnimator lineLengthAnimator = ObjectAnimator.ofFloat(this, "lineLength", radius / 2, 0); //下载箭头竖线收缩成点后的动画1(下载箭头的箭头变成横线) ObjectAnimator arrowTurningPointYAnimator1 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", radius / 2, 0); //下载箭头竖线收缩成点后的动画2(下载箭头的箭头抖一下的效果) ObjectAnimator arrowTurningPointYAnimator2 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", 0, radius / 4, 0); //下载箭头竖线收缩成点后的在 Y 轴的坐标变化的动画 ObjectAnimator linePointYAnimator = ObjectAnimator.ofFloat(this, "linePointY", getMeasuredHeight() / 2, getMeasuredHeight() / 2 - radius); //画半圆的动画 ObjectAnimator sweepAngleAnimator = ObjectAnimator.ofFloat(this, "sweepAngle", 0, 180); //下载箭头的箭头变成横线后收缩成点的动画 ObjectAnimator arrowLengthAnimator = ObjectAnimator.ofFloat(this, "arrowLength", radius / 2, 0); //每个动画设置不同的时长 lineLengthAnimator.setDuration(500); arrowTurningPointYAnimator1.setDuration(500); arrowTurningPointYAnimator2.setDuration(500); linePointYAnimator.setDuration(500); sweepAngleAnimator.setDuration(3000); arrowLengthAnimator.setDuration(3000); //添加动画监听,实际上就是一直重绘 lineLengthAnimator.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator1.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator2.addUpdateListener(mAnimatorUpdateListener); linePointYAnimator.addUpdateListener(mAnimatorUpdateListener); sweepAngleAnimator.addUpdateListener(mAnimatorUpdateListener); arrowLengthAnimator.addUpdateListener(mAnimatorUpdateListener); //设置动画的播放顺序 AnimatorSet mAnimatorSet = new AnimatorSet(); mAnimatorSet.play(arrowTurningPointYAnimator1).after(lineLengthAnimator); mAnimatorSet.play(arrowTurningPointYAnimator2).after(arrowTurningPointYAnimator1); mAnimatorSet.play(linePointYAnimator).after(arrowTurningPointYAnimator1); mAnimatorSet.play(arrowLengthAnimator).after(arrowTurningPointYAnimator2); mAnimatorSet.play(sweepAngleAnimator).after(arrowTurningPointYAnimator2); mAnimatorSet.start(); }
6 画对勾
这里要说一下,我们在画对勾的时候就不要画下载箭头的那部分了,所以刚才代码中有一个 boolean 类型的 drawOk 的标志位,通过它来判断是画对勾还是下载箭头,在下载箭头的箭头部分也收缩成一个点后将它置为 true,即 arrowLength = 0 时,画对勾我们这里需要动态计算它的转折点的位置还有对勾的总长度
看这张图:
这样一来,point1 的 x 坐标就是圆心的 x 坐标减去斜边1的长度乘以 sin45°,point2 的 x 坐标就是圆心的 x 坐标,point3 的x 坐标是圆心的 x 坐标加上斜边2的长度乘以 sin45°。point2 的 y 坐标是我们已知的变化,所以通过 point2 的 y 坐标可以算出 point1 和 point3 的 y 坐标,point1 的 y 坐标等于 point2 的 y 坐标减去斜边1乘以 cos45°,point3 的 y 坐标等于 point2 的 y 坐标减去斜边2乘以 cos45°。虽然理论上是这样,但是实际效果并不好看。
mPath.moveTo((float) (getMeasuredWidth() / 2 - (Math.sin(45) * tickLength / 3)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3))); mPath.lineTo(getMeasuredWidth() / 2, tickTurningPointY); mPath.lineTo((float) (getMeasuredWidth() / 2 + (Math.sin(45) * tickLength / 3*2)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3*2)));
mPath.moveTo((float) (getMeasuredWidth() / 2 - (Math.sin(45) * tickLength / 3)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3)-radius/4)); mPath.lineTo(getMeasuredWidth() / 2, tickTurningPointY-radius/4); mPath.lineTo((float) (getMeasuredWidth() / 2 + (Math.sin(45) * tickLength / 3)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3*2)-radius/4));
然后添加动画,代码就不贴了,看最后的整体代码吧。最终的整体效果如下:
7 总结
这次这个自定义控件的动画算是比较复杂的了,步骤有点多,也涉及了一点我以前学习属性动画疏漏的地方,这次赶紧补上。而且尤其是最后一个步骤的时候,感觉实现得真的不好,果然自己在数学和算法方面差距有点大。不过这次又更加让我明白了,就算是再复杂的动画,只要分解好了,一步一步的实现,都没有什么难度。
8 源码
/** * Description: * Created by 禽兽先生 * Created on 2017/9/4 */public class DownloadView1 extends View { private Paint mPaint; private Path mPath; private float radius = 150; //圆半径 private float lineLength = radius / 2; //下载的标志中间的竖线的长度 private float arrowTurningPointY = radius / 2; //下载的标志下面箭头的转折点的 Y 轴坐标 private float linePointY; //下载的标志中间的竖线收缩成点之后点的 Y 坐标,onMeasured() 方法中初始化 private RectF mRectF; private float sweepAngle = 0; //两边弧线扫过的角度 private float arrowLength = radius / 2; //下载的标志下面箭头变成水平线后的长度 private float tickTurningPointY; //对钩 Y 坐标,onMeasured() 方法中初始化 private float tickLength = 0; //对钩的总长度; private boolean drawOk; //是否应该画最后的对钩的标志位 public DownloadView1(Context context) { this(context, null); } public DownloadView1(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public DownloadView1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mPaint.setStrokeWidth(radius / 10); mPaint.setStrokeCap(Paint.Cap.ROUND); mPath = new Path(); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { startAnimation(); } }); } public void setLineLength(float lineLength) { this.lineLength = lineLength; } public void setArrowTurningPointY(float arrowTurningPointY) { this.arrowTurningPointY = arrowTurningPointY; } public void setLinePointY(float linePointY) { this.linePointY = linePointY; } public void setSweepAngle(float sweepAngle) { this.sweepAngle = sweepAngle; } public void setArrowLength(float arrowLength) { this.arrowLength = arrowLength; } public void setTickTurningPointY(float tickTurningPointY) { this.tickTurningPointY = tickTurningPointY; } public void setTickLength(float tickLength) { this.tickLength = tickLength; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setBackgroundColor(Color.rgb(10, 39, 46)); linePointY = getMeasuredHeight() / 2; tickTurningPointY = getMeasuredHeight() / 2; mRectF = new RectF(getMeasuredWidth() / 2 - radius , getMeasuredHeight() / 2 - radius , getMeasuredWidth() / 2 + radius , getMeasuredHeight() / 2 + radius); } @Override protected void onDraw(Canvas canvas) { //画整个圆 mPaint.setColor(Color.rgb(10, 39, 46)); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, mPaint); //画圆环 mPaint.setColor(Color.rgb(37, 66, 73)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, radius, mPaint); //画两个半圆 mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); canvas.drawArc(mRectF, -90, sweepAngle, false, mPaint); canvas.drawArc(mRectF, -90, 0 - sweepAngle, false, mPaint); //根据是否应该画对勾的标志位来决定画什么 if (drawOk) { //画对勾 mPaint.setColor(Color.rgb(98, 178, 117)); mPaint.setStyle(Paint.Style.STROKE); mPath.reset(); mPath.moveTo((float) (getMeasuredWidth() / 2 - (Math.sin(45) * tickLength / 3)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3)-radius/4)); mPath.lineTo(getMeasuredWidth() / 2, tickTurningPointY-radius/4); mPath.lineTo((float) (getMeasuredWidth() / 2 + (Math.sin(45) * tickLength / 3)) , (float) (tickTurningPointY - (Math.cos(45) * tickLength / 3*2)-radius/4)); canvas.drawPath(mPath, mPaint); } else { //画下载箭头 mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); if (lineLength == 0) { //下载箭头的竖线收缩成点后应该画点 canvas.drawPoint(getMeasuredWidth() / 2, linePointY, mPaint); } else { //否则应该画线 canvas.drawLine(getMeasuredWidth() / 2, getMeasuredHeight() / 2 - lineLength , getMeasuredWidth() / 2, getMeasuredHeight() / 2 + lineLength , mPaint); } mPaint.setColor(Color.rgb(255, 255, 255)); mPaint.setStyle(Paint.Style.STROKE); if (arrowLength == 0) { //下载箭头的箭头部分收缩成点后应该画点 canvas.drawPoint(getMeasuredWidth() / 2, tickTurningPointY, mPaint); drawOk = true; } else { //否则应该画箭头 mPath.reset(); mPath.moveTo(getMeasuredWidth() / 2 - arrowLength, getMeasuredHeight() / 2); mPath.lineTo(getMeasuredWidth() / 2, getMeasuredHeight() / 2 + arrowTurningPointY); mPath.lineTo(getMeasuredWidth() / 2 + arrowLength, getMeasuredHeight() / 2); canvas.drawPath(mPath, mPaint); } } } private void startAnimation() { //下载箭头竖线收缩成点的动画 ObjectAnimator lineLengthAnimator = ObjectAnimator.ofFloat(this, "lineLength", radius / 2, 0); //下载箭头竖线收缩成点后的动画1(下载箭头的箭头变成横线) ObjectAnimator arrowTurningPointYAnimator1 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", radius / 2, 0); //下载箭头竖线收缩成点后的动画2(下载箭头的箭头抖一下的效果) ObjectAnimator arrowTurningPointYAnimator2 = ObjectAnimator.ofFloat(this, "arrowTurningPointY", 0, radius / 4, 0); //下载箭头竖线收缩成点后的在 Y 轴的坐标变化的动画 ObjectAnimator linePointYAnimator = ObjectAnimator.ofFloat(this, "linePointY", getMeasuredHeight() / 2, getMeasuredHeight() / 2 - radius); //画半圆的动画 ObjectAnimator sweepAngleAnimator = ObjectAnimator.ofFloat(this, "sweepAngle", 0, 180); //下载箭头的箭头变成横线后收缩成点的动画 ObjectAnimator arrowLengthAnimator = ObjectAnimator.ofFloat(this, "arrowLength", radius / 2, 0); //对勾的转折点在 Y 轴坐标变化的动画 ObjectAnimator tickTurningPointYAnimator = ObjectAnimator.ofFloat(this, "tickTurningPointY", getMeasuredHeight() / 2, getMeasuredHeight() / 2 + radius / 2); //对勾的总长度的变化动画 ObjectAnimator tickLengthAnimator = ObjectAnimator.ofFloat(this, "tickLength", 0, radius); //每个动画设置不同的时长 lineLengthAnimator.setDuration(500); arrowTurningPointYAnimator1.setDuration(500); arrowTurningPointYAnimator2.setDuration(500); linePointYAnimator.setDuration(500); sweepAngleAnimator.setDuration(3000); arrowLengthAnimator.setDuration(3000); tickTurningPointYAnimator.setDuration(1000); tickLengthAnimator.setDuration(1000); //添加动画监听,实际上就是一直重绘 lineLengthAnimator.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator1.addUpdateListener(mAnimatorUpdateListener); arrowTurningPointYAnimator2.addUpdateListener(mAnimatorUpdateListener); linePointYAnimator.addUpdateListener(mAnimatorUpdateListener); sweepAngleAnimator.addUpdateListener(mAnimatorUpdateListener); arrowLengthAnimator.addUpdateListener(mAnimatorUpdateListener); tickTurningPointYAnimator.addUpdateListener(mAnimatorUpdateListener); tickLengthAnimator.addUpdateListener(mAnimatorUpdateListener); //设置动画的播放顺序 AnimatorSet mAnimatorSet = new AnimatorSet(); mAnimatorSet.play(arrowTurningPointYAnimator1).after(lineLengthAnimator); mAnimatorSet.play(arrowTurningPointYAnimator2).after(arrowTurningPointYAnimator1); mAnimatorSet.play(linePointYAnimator).after(arrowTurningPointYAnimator1); mAnimatorSet.play(arrowLengthAnimator).after(arrowTurningPointYAnimator2); mAnimatorSet.play(sweepAngleAnimator).after(arrowTurningPointYAnimator2); mAnimatorSet.play(tickTurningPointYAnimator).after(sweepAngleAnimator); mAnimatorSet.play(tickLengthAnimator).after(sweepAngleAnimator); //添加动画集监听,结束后重置各个变量 mAnimatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) {// lineLength = radius / 2;// arrowTurningPointY = radius / 2;// linePointY = getMeasuredHeight() / 2;// sweepAngle = 0;// arrowLength = radius / 2;// tickTurningPointY = getMeasuredHeight() / 2;// tickLength = 0;// drawOk = false;// invalidate(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mAnimatorSet.start(); } private ValueAnimator.AnimatorUpdateListener mAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } };}
阅读全文
0 0
- 自定义控件之下载控件1(DownloadView1)
- Android自定义控件之自定义下载progress
- Android自定义view之下载控件,ProgressBar
- 自定义控件(25)---自定义控件之组合控件
- WPF控件开发之自定义控件(1)
- WPF控件开发之自定义控件(1)
- WPF控件开发之自定义控件(1)
- 自定义控件(1)
- 自定义控件之翻页控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- 自定义控件之组合控件
- Android-自定义控件之组装控件(自定义导航)
- Android-自定义控件之重写控件(自定义TextView)
- 自定义控件(18)---自定义控件之面板思想---addRule
- Android自定义控件之自定义组合控件(三)
- Android自定义控件之自定义组合控件(一)
- 开发自定义控件 __Textbox控件(1)
- java调用qq聊天,调用分享
- Spring Cloud与Consul服务发现
- 块级元素和行内元素
- TypeError: 'zip' object is not callable
- spring 事务传播行为实例分析
- 自定义控件之下载控件1(DownloadView1)
- CCF-201609-1-最大波动
- RedisDesktopManager连接远程Linux系统的Redis服务(图文)
- git-----初始化配置添加用户名和密码
- [编程题] 地下迷宫 滴滴编程题
- Linux文件系统概述-基于0.12内核
- C++面试
- 虚拟机在线迁移Vmotion
- 线程thread的使用