当前位置:网站首页>自定义控件之下载控件1(DownloadView1)
自定义控件之下载控件1(DownloadView1)
2022-06-29 09:16:00 【禽兽先生不禽兽】
前段时间在干货集中营看到了两个炫酷的下载按钮:

可惜是隔壁 iOS 的孩子,怎么办,我也好喜欢,emmm,某该,只能自己模仿着实现一下了。先从第一个入手(第二个波浪效果暂时还不会)。
1 准备动作
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 画圆和箭头
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);
}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 下载箭头的箭头部分变成横线,同时竖线的圆点上移
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 画两个半圆,下载箭头变成的横线收缩成一个点
//画两个半圆
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);
} 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 画对勾

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();
}
};
}边栏推荐
- JVM之虚拟机栈之动态链接
- Cloud management platform: 9 open source cloud management platforms (CMP)
- leetcode MYSQL数据库题目180
- 指针函数和函数指针
- Sublime Text3 set to run your own makefile
- Leetcode skimming -- teponacci sequence
- CROSSFORMER: A VERSATILE VISION TRANSFORMER BASED ON CROSS-SCALE ATTENTION
- gSoap例子——calc
- Lc236. nearest common ancestor of binary tree
- The 23 most useful elasticsearch search techniques you must know
猜你喜欢

2020-09-29 非商品模板化代码层次 rapidjson库

Segmentation of Head and Neck Tumours Using Modified U-net

KDevelop new project

linux环境下安装配置redis,并设置开机自启动

FreeRTOS(九)——队列

Flutter 基础组件之 Container

RecyclerView 通用适配器封装

User level threads and kernel level threads

Automatic Multi-Organ SegmVentation on Abdominal CT With Dense V-Networks

JVM之方法返回地址
随机推荐
Chang'an chain go language smart contract writing and compilation
JVM四种调用方法的指令
动态规划总结
Es error nonodeavailableexception[none of the configured nodes are available:[.127.0.0.1}{127.0.0.1:9300]
FreeRTOS (IX) - queue
阿里云防火墙配置,多种设置方式(iptables和fireward)
Surveiller l'utilisation du pool de connexion des sources de données
[technology development] development and design of alcohol tester solution
Data warehouse: layered architecture of Finance / banking
JS获取手机型号和系统版本
Monitoring data source connection pool usage
Cloud management platform: 9 open source cloud management platforms (CMP)
数据源连接池未关闭的问题 Could not open JDBC Connection for transaction
分布式和集群分不清,我们讲讲两个厨子炒菜的故事
FreeRTOS(八)——时间管理
A 2.5D Cancer Segmentation for MRI Images Based on U-Net
FreeRTOS(九)——队列
User level threads and kernel level threads
float 与 int 相乘产生的令人崩溃的“ 2.3 * 10 = 22 ”
In XML layout, the button is always displayed on the top layer