android 进度条_Android仿水波纹流球进度条控制器,实现高端大气的主流特效

今天看到一个效果挺不错的,就模仿了下来,加上了一些自己想要的效果,感觉还不错的样子,所以就分享出来了,话不多说,上图

05bb7f68d7884b4ab3c7ce6baae00167

CircleView

这里主要是实现中心圆以及水波特效

package com.lgl.circleview;

import android.content.Context;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.RectF;

import android.os.Handler;

import android.os.Parcel;

import android.os.Parcelable;

import android.util.AttributeSet;

import android.view.View;

import android.widget.ProgressBar;

/**

* 水波圆

*

* @author lgl

*

*/

public class CircleView extends View {

private Context mContext;

private int mScreenWidth;

private int mScreenHeight;

private Paint mRingPaint;

private Paint mCirclePaint;

private Paint mWavePaint;

private Paint linePaint;

private Paint flowPaint;

private Paint leftPaint;

private int mRingSTROKEWidth = 15;

private int mCircleSTROKEWidth = 2;

private int mLineSTROKEWidth = 1;

private int mCircleColor = Color.WHITE;

private int mRingColor = Color.WHITE;

private int mWaveColor = Color.WHITE;

private Handler mHandler;

private long c = 0L;

private boolean mStarted = false;

private final float f = 0.033F;

private int mAlpha = 50;// 透明度

private float mAmplitude = 10.0F; // 振幅

private float mWaterLevel = 0.5F;// 水高(0~1)

private Path mPath;

// 绘制文字显示在圆形中间,只是我没有设置,我觉得写在布局上也挺好的

private String flowNum = "";

private String flowLeft = "还剩余";

/**

* @param context

*/

public CircleView(Context context) {

super(context);

// TODO Auto-generated constructor stub

mContext = context;

init(mContext);

}

/**

* @param context

* @param attrs

*/

public CircleView(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

mContext = context;

init(mContext);

}

/**

* @param context

* @param attrs

* @param defStyleAttr

*/

public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

// TODO Auto-generated constructor stub

mContext = context;

init(mContext);

}

public void setmWaterLevel(float mWaterLevel) {

this.mWaterLevel = mWaterLevel;

}

private void init(Context context) {

mRingPaint = new Paint();

mRingPaint.setColor(mRingColor);

mRingPaint.setAlpha(50);

mRingPaint.setStyle(Paint.Style.STROKE);

mRingPaint.setAntiAlias(true);

mRingPaint.setStrokeWidth(mRingSTROKEWidth);

mCirclePaint = new Paint();

mCirclePaint.setColor(mCircleColor);

mCirclePaint.setStyle(Paint.Style.STROKE);

mCirclePaint.setAntiAlias(true);

mCirclePaint.setStrokeWidth(mCircleSTROKEWidth);

linePaint = new Paint();

linePaint.setColor(mCircleColor);

linePaint.setStyle(Paint.Style.STROKE);

linePaint.setAntiAlias(true);

linePaint.setStrokeWidth(mLineSTROKEWidth);

flowPaint = new Paint();

flowPaint.setColor(mCircleColor);

flowPaint.setStyle(Paint.Style.FILL);

flowPaint.setAntiAlias(true);

flowPaint.setTextSize(36);

leftPaint = new Paint();

leftPaint.setColor(mCircleColor);

leftPaint.setStyle(Paint.Style.FILL);

leftPaint.setAntiAlias(true);

leftPaint.setTextSize(36);

mWavePaint = new Paint();

mWavePaint.setStrokeWidth(1.0F);

mWavePaint.setColor(mWaveColor);

mWavePaint.setAlpha(mAlpha);

mPath = new Path();

mHandler = new Handler() {

@Override

public void handleMessage(android.os.Message msg) {

if (msg.what == 0) {

invalidate();

if (mStarted) {

// 不断发消息给自己,使自己不断被重绘

mHandler.sendEmptyMessageDelayed(0, 60L);

}

}

}

};

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int width = measure(widthMeasureSpec, true);

int height = measure(heightMeasureSpec, false);

if (width < height) {

setMeasuredDimension(width, width);

} else {

setMeasuredDimension(height, height);

}

}

/**

* @category 测量

* @param measureSpec

* @param isWidth

* @return

*/

private int measure(int measureSpec, boolean isWidth) {

int result;

int mode = MeasureSpec.getMode(measureSpec);

int size = MeasureSpec.getSize(measureSpec);

int padding = isWidth ? getPaddingLeft() + getPaddingRight()

: getPaddingTop() + getPaddingBottom();

if (mode == MeasureSpec.EXACTLY) {

result = size;

} else {

result = isWidth ? getSuggestedMinimumWidth()

: getSuggestedMinimumHeight();

result += padding;

if (mode == MeasureSpec.AT_MOST) {

if (isWidth) {

result = Math.max(result, size);

} else {

result = Math.min(result, size);

}

}

}

return result;

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

// TODO Auto-generated method stub

super.onSizeChanged(w, h, oldw, oldh);

mScreenWidth = w;

mScreenHeight = h;

}

@Override

protected void onDraw(Canvas canvas) {

// TODO Auto-generated method stub

super.onDraw(canvas);

// 得到控件的宽高

int width = getWidth();

int height = getHeight();

setBackgroundColor(mContext.getResources().getColor(R.color.main_bg));

// 计算当前油量线和水平中线的距离

float centerOffset = Math.abs(mScreenWidth / 2 * mWaterLevel

- mScreenWidth / 4);

// 计算油量线和与水平中线的角度

float horiAngle = (float) (Math.asin(centerOffset / (mScreenWidth / 4)) * 180 / Math.PI);

// 扇形的起始角度和扫过角度

float startAngle, sweepAngle;

if (mWaterLevel > 0.5F) {

startAngle = 360F - horiAngle;

sweepAngle = 180F + 2 * horiAngle;

} else {

startAngle = horiAngle;

sweepAngle = 180F - 2 * horiAngle;

}

canvas.drawLine(mScreenWidth * 3 / 8, mScreenHeight * 5 / 8,

mScreenWidth * 5 / 8, mScreenHeight * 5 / 8, linePaint);

float num = flowPaint.measureText(flowNum);

canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2,

mScreenHeight * 4 / 8, flowPaint);

float left = leftPaint.measureText(flowLeft);

canvas.drawText(flowLeft, mScreenWidth * 4 / 8 - left / 2,

mScreenHeight * 3 / 8, leftPaint);

// 如果未开始(未调用startWave方法),绘制一个扇形

if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {

// 绘制,即水面静止时的高度

RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4,

mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);

canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);

return;

}

// 绘制,即水面静止时的高度

// 绘制,即水面静止时的高度

RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4,

mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);

canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);

if (this.c >= 8388607L) {

this.c = 0L;

}

// 每次onDraw时c都会自增

c = (1L + c);

float f1 = mScreenHeight * (1.0F - (0.25F + mWaterLevel / 2))

- mAmplitude;

// 当前油量线的长度

float waveWidth = (float) Math.sqrt(mScreenWidth * mScreenWidth / 16

- centerOffset * centerOffset);

// 与圆半径的偏移量

float offsetWidth = mScreenWidth / 4 - waveWidth;

int top = (int) (f1 + mAmplitude);

mPath.reset();

// 起始振动X坐标,结束振动X坐标

int startX, endX;

if (mWaterLevel > 0.50F) {

startX = (int) (mScreenWidth / 4 + offsetWidth);

endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth);

} else {

startX = (int) (mScreenWidth / 4 + offsetWidth - mAmplitude);

endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth + mAmplitude);

}

// 波浪效果

while (startX < endX) {

int startY = (int) (f1 - mAmplitude

* Math.sin(Math.PI

* (2.0F * (startX + this.c * width * this.f))

/ width));

canvas.drawLine(startX, startY, startX, top, mWavePaint);

startX++;

}

canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4

+ mRingSTROKEWidth / 2, mRingPaint);

canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2,

mScreenWidth / 4, mCirclePaint);

canvas.restore();

}

@Override

public Parcelable onSaveInstanceState() {

Parcelable superState = super.onSaveInstanceState();

SavedState ss = new SavedState(superState);

ss.progress = (int) c;

return ss;

}

@Override

public void onRestoreInstanceState(Parcelable state) {

SavedState ss = (SavedState) state;

super.onRestoreInstanceState(ss.getSuperState());

c = ss.progress;

}

@Override

protected void onAttachedToWindow() {

super.onAttachedToWindow();

// 关闭硬件加速,防止异常unsupported operation exception

this.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

}

@Override

protected void onDetachedFromWindow() {

super.onDetachedFromWindow();

}

/**

* @category 开始波动

*/

public void startWave() {

if (!mStarted) {

this.c = 0L;

mStarted = true;

this.mHandler.sendEmptyMessage(0);

}

}

/**

* @category 停止波动

*/

public void stopWave() {

if (mStarted) {

this.c = 0L;

mStarted = false;

this.mHandler.removeMessages(0);

}

}

/**

* @category 保存状态

*/

static class SavedState extends BaseSavedState {

int progress;

/**

* Constructor called from {@link ProgressBar#onSaveInstanceState()}

*/

SavedState(Parcelable superState) {

super(superState);

}

/**

* Constructor called from {@link #CREATOR}

*/

private SavedState(Parcel in) {

super(in);

progress = in.readInt();

}

@Override

public void writeToParcel(Parcel out, int flags) {

super.writeToParcel(out, flags);

out.writeInt(progress);

}

public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {

public SavedState createFromParcel(Parcel in) {

return new SavedState(in);

}

public SavedState[] newArray(int size) {

return new SavedState[size];

}

};

}

}

我们运行一下

3c61b1aa29414e3f9c6e80d840a71e3d

其实他是十分的空旷的,所以也值得我们去定制,我们在中间加个流量显示,再加个进度条

activity_main.xml

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:background="@color/main_bg" >

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_alignParentTop="true"

android:layout_centerHorizontal="true"

android:layout_marginTop="10dp"

android:text="流量"

android:textColor="@android:color/white"

android:textSize="18sp" />

android:id="@+id/wave_view"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

android:layout_centerInParent="true" />

android:id="@+id/power"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerInParent="true"

android:textColor="@android:color/white" />

android:id="@+id/seekBar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:layout_marginBottom="150dp" />

```

>我们要实现这个,就要调用它的初始化以及start方法

```

mCircleView = (CircleView) findViewById(R.id.wave_view);

// 设置多高,float,0.1-1F

mCircleView.setmWaterLevel(0.1F);

// 开始执行

mCircleView.startWave();

别忘了activity销毁的时候把它回收哦

@Override

protected void onDestroy() {

// TODO Auto-generated method stub

mCircleView.stopWave();

mCircleView = null;

super.onDestroy();

}

我们再运行一遍

27a3e6fbd0ed4f2bacd7980f9b910ea3

但是我们要怎么让水波纹随着进度条一起上升下降尼?,这里我们就要用到我们刚才写的SeekBar了,我们实现它的setOnSeekBarChangeListener来监听,这样我们就要复写他的三个方法,这里我们只要用到一个

public void onProgressChanged(SeekBar seekBar, int progress,

boolean fromUser) {

//跟随进度条滚动

mCircleView.setmWaterLevel((float) progress / 100);

}

这里,我们要这样算的,我们设置高度的单位是float,也就是从0-1F,而我们的进度是int progress,从0-100,我们就要用(float) progress / 100)并且强转来得到单位,好了,我们现在水波纹的高度就是随着我们的进度条一起变化了,我们再来运行一下

1c17648986f8405e862d72c9e1ad81dd

好的,这样的话,我们就只剩下一个了,就是让大小随着我们的进度条变化了,这里我们因为更新UI不能再主线程中操作,所以我们需要用到我们的老伙计Handler了,但是用到handler还不够,我们的进度条数值也是在内部类里面,所以这里我们需要用到Handler来传值了,这里我们用的是Bundle,我们还是在onProgressChanged方法中操作了

//创建一个消息

Message message = new Message();

Bundle bundle = new Bundle();

//put一个int值

bundle.putInt("progress

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409815.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

docker php composer 使用_如何使用Docker部署PHP开发环境

本文主要介绍了如何使用Docker构建PHP的开发环境&#xff0c;文中作者也探讨了构建基于Docker的开发环境应该使用单容器还是多容器&#xff0c;各有什么利弊。推荐PHP开发者阅读。希望对大家有所帮助。环境部署一直是一个很大的问题&#xff0c;无论是开发环境还是生产环境&…

from 下拉框多个值提交_Git提交规范

规范的作用大多数情况下&#xff0c;看提交历史的人跟提交代码的人都不是同一个人&#xff0c;当别人阅读你的提交历史时&#xff0c;他很可能是不知道具体代码细节的&#xff0c;你如何在最短的时间内让他一眼知道每次提交的意义&#xff1a;每次提交影响的具体范围&#xff1…

【物联网智能网关-03】GPRS模块中文短信收发

在去年年初&#xff0c;就已经推出V1.0.0的GPRS库&#xff0c;不过在这个版本上只是实现了西文短信收发和字符串方式的GPRS数据通信&#xff0c;功能还相对不完善&#xff08;参见我以前的博文《GPRS通信实现》&#xff09;。最近升级的版本&#xff0c;对以上功能进行了大幅度…

蓄电池单格电压多少伏_蓄电池充电规范手册

很多用户在买完蓄电池之后第一想法就是赶紧充电&#xff0c;很多陋习让用户使用行为造成了新买的蓄电池没怎么用感觉就和旧的没啥区别。而且使用时间越来越短到头来企业还失去了很多的客户&#xff0c;德国阳光蓄电池手册整理整编了在不同的环境中我们该如何很好的去维护自己的…

钉钉机器人关键词应答_除了用于电销,智能语音机器人可以应用哪些地方?

之前的文章探讨的是智能语音机器人在电销行业的应用&#xff0c;然而在实际的场景中&#xff0c;电销行业的应用只是大家所熟知的行业之一。对比于人工电销&#xff0c;使用智能语音机器人有着诸多优势&#xff0c;例如&#xff1a;工作效率高、意向筛选、电话录音并转化为文字…

1-2docker-基本的使用

1、Docker 官⽅提供了⼀个公共的镜像仓库 https://hub.docker.com2、获取镜像 docker pull [选项] [Docker Registry 地址[:端⼝]/]仓库名[:标签]3、运行镜像 docker run -it --rm ubuntu:16.04 /bin/bash -it&#xff1a;这是两个参数&#xff0c;⼀个是 -i&#xff1a;交互式…

assert函数_PHP 之 assert()函数

assert()函数其实是一个断言函数。那么什么是断言呢&#xff1f;百度百科上是这么说的&#xff1a;编写代码时&#xff0c;我们总是会做出一些假设&#xff0c;断言就是用于在代码中捕捉这些假设。说到这里&#xff0c;大家应该能知道assert()函数是干嘛用的了吧&#xff1f;好…

1-3docker commit定制镜像

以定制⼀个 Web 服务器为例⼦1、commit定制镜像 docker pull nginx:1.17运行容器 --name:容器名字 -d&#xff1a;后台 -p本地端口&#xff1a;容器内端口 docker run --name webserver -d -p 8080:80 nginx:1.17#进入容器 docker exec -it webserver /bin/bash#进入容器执…

mysql 启动_mysql安装、启动

在这个网址下载的&#xff1a;Download MySQL Community Server​dev.mysql.com下载后解压到了D盘&#xff0c;我是重命名为mysql。进去目录下bin子目录&#xff0c;进行以下操作&#xff0c;初始化&#xff1a;mysqld --initialize --console执行完成后&#xff0c;会输出 roo…

2010 Stanford Local ACM Programming Contest-H解题报告

题意是说&#xff0c;给出一些道路&#xff0c;要修建一条高速公路&#xff0c;高速公路不能分叉&#xff0c;而且是在给出的图中的一些路径组成&#xff0c;求的是不在高速公路上的点离高速公路的最远距离的最小值是多少。首先要找到一条这个图中的关键路径&#xff0c;既最长…

qtmessagebox对话框里自定义按钮文本_按钮你可以这样设计

作者&#xff1a;Michal Malewicz译者&#xff1a;Matrix审稿&#xff1a;afang原文链接&#xff1a;https://uxdesign.cc/design-better-buttons-a5c90a113280文章由交译所成员翻译&#xff0c;如需转载&#xff0c;请先申请授权。译文如下&#xff1a;按钮是触发它所描述功能…

zabbix入门之添加主机

添加主机的方法有两种&#xff1a;手动添加、自动发现 前提是&#xff1a;在被监控主机中安装zabbix-agent、zabbix-sender组件&#xff0c;并配置好启动服务。 手动添加&#xff1a; 自动发现&#xff1a; 这里等待1分钟左右即可发现主机 开启默认的动作 等待几分钟后即可在“…

如何保持连接_工高连城 | 连接器连接失效的原因有哪些

【温馨提示】本公众号是工高电子旗下工高连城中国连城双电商平台的官方公众号&#xff0c;简称工高连城连接器商城 中国连城平台定位&#xff1a;中国连接器行业专业供应链服务平台中国连接器行业的阿里巴巴永不落幕的online线上慕尼黑连接器展会。中国连城官网&#xff1a;w…

centos7删除文件命令_干货 | 玩转云文件存储——利用CFS实现web应用的共享访问...

京东云文件服务(Cloud File Service,以下简称&#xff1a;CFS)是一种高可靠、可扩展、可共享访问的全托管分布式文件系统。它可在不中断应用服务的情况下&#xff0c;根据您对文件系统的使用&#xff0c;按需扩展或缩减&#xff0c;并按照实际用量计费。采用NFS协议&#xff0c…

1-4dockerfile基本使用

1.创建一个文件夹 mkdir mynginxcd mynginxtouch Dockerfile [rootVM_0_10_centos mynginx]# cat Dockerfile FROM nginx:1.17 #第一次镜像RUN echo echo <h1>Hello, zjy!</h1> > /usr/share/nginx/html/index.html1-1、如果说没有第一层镜像&#xff0c;是…

zTree v2.6 - v3.0 文件对比

转载于:https://www.cnblogs.com/MyFlora/archive/2012/06/05/2536377.html

lvs服务器需要开启web服务么_Centos7搭建LVS+Keepalived高可用Web

LVS Keepalived 高可用集群Keepalived的设计目标是构建高可用的LVS负载均衡的集群&#xff0c;可以调用ipvsadm工具创建虚拟机&#xff0c;不仅仅用作双机热备&#xff0c;还可以使用keepalived构建更加方便快捷的节点&#xff0c;进行相关的健康检查&#xff0c;自动移除失效…

1-5docker私有镜像仓库

1、简单操作 1、在 https://cloud.docker.com 免费注册一个 Docker 账号 2、登录 docker login #命令登录 Docker Hub。 3、注销docker logout # 退出登录。 拉取镜像 4、docker search #命令来查找官方仓库中的镜像 5、docker pull 命令来将它下载到本地。#推送镜像到自己…

NCoreCoder.Aop详解

于今天&#xff0c;功能终于完善度到比较满意的程度了 准备好好写一篇文章&#xff0c;而不是之前的流水账&#xff0c;分享一下最近这些天的踩坑 一开始AOP选的微软提供的DispatchProxy 关于这个&#xff0c;有大佬的文章&#xff0c;可以看看&#xff0c;了解一下 https://ww…

主类网络号怎么算_一文了解网络文化经营许可证的审批范围

3月10日&#xff0c;北京市文化和旅游局发布《关于电商类、教育类、医疗类、培训类、金融类、旅游类、美食类、体育类、聊天类不需要申请办理的特别提示》(以下简称《特别提示》)。《特别提示》指出&#xff0c;随着互联网技术的快速发展&#xff0c;利用互联网从事文化经营活动…