Android特效专辑(十一)——仿水波纹流量球进度条控制器,实现高端大气的主流特效
Android特效专辑(十一)——仿水波纹流球进度条控制器,实现高端大气的主流特效
今天看到一个效果挺不错的,就模仿了下来,加上了一些自己想要的效果,感觉还不错的样子,所以就分享出来了,话不多说,上图
截图
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<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
我们运行一下
其实他是十分的空旷的,所以也值得我们去定制,我们在中间加个流量显示,再加个进度条
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/main_bg" >
<TextView
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" />
<com.lgl.circleview.CircleView
android:id="@+id/wave_view"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_centerInParent="true" />
<TextView
android:id="@+id/power"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textColor="@android:color/white" />
<SeekBar
android:id="@+id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="150dp" />
</RelativeLayout>
我们要实现这个,就要调用它的初始化以及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();
}
我们再运行一遍
但是我们要怎么让水波纹随着进度条一起上升下降尼?,这里我们就要用到我们刚才写的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)并且强转来得到单位,好了,我们现在水波纹的高度就是随着我们的进度条一起变化了,我们再来运行一下
好的,这样的话,我们就只剩下一个了,就是让大小随着我们的进度条变化了,这里我们因为更新UI不能再主线程中操作,所以我们需要用到我们的老伙计Handler了,但是用到handler还不够,我们的进度条数值也是在内部类里面,所以这里我们需要用到Handler来传值了,这里我们用的是Bundle,我们还是在onProgressChanged方法中操作了
//创建一个消息
Message message = new Message();
Bundle bundle = new Bundle();
//put一个int值
bundle.putInt("progress", progress);
//装载
message.setData(bundle);
//发送消息
handler.sendMessage(message);
//创建表示
message.what = 1;
消息发送过去了,我们就在前面写个Handler去接收就是了
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
int num = msg.getData().getInt("progress");
Log.i("num", num + "");
power.setText((float) num / 100 * max + "M/" + max + "M");
}
}
};
这里的计算公式尼,是当前的数值/100得到百分比再去*最大值。我们现在可以完整的运行一下了,其实和最上面运行的图片是一样的
MainActivity
package com.lgl.circleview;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.widget.SeekBar;
import android.widget.TextView;
public class MainActivity extends Activity {
private CircleView mCircleView;
private SeekBar mSeekBar;
private TextView power;
private int max = 1024;
private int min = 102;
private Handler handler = new Handler() {
public void handleMessage(android.os.Message msg) {
if (msg.what == 1) {
int num = msg.getData().getInt("progress");
Log.i("num", num + "");
power.setText((float) num / 100 * max + "M/" + max + "M");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActionBar().hide();
setContentView(R.layout.activity_main);
power = (TextView) findViewById(R.id.power);
power.setText(min + "M/" + max + "M");
mCircleView = (CircleView) findViewById(R.id.wave_view);
// 设置多高,float,0.1-1F
mCircleView.setmWaterLevel(0.1F);
// 开始执行
mCircleView.startWave();
mSeekBar = (SeekBar) findViewById(R.id.seekBar);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mCircleView.setmWaterLevel((float) progress / 100);
// 创建一个消息
Message message = new Message();
Bundle bundle = new Bundle();
// put一个int值
bundle.putInt("progress", progress);
// 装载
message.setData(bundle);
// 发送消息
handler.sendMessage(message);
// 创建表示
message.what = 1;
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
mCircleView.stopWave();
mCircleView = null;
super.onDestroy();
}
}
Demo下载地址:http://download.csdn.net/detail/qq_26787115/9435934
Android特效专辑(十一)——仿水波纹流量球进度条控制器,实现高端大气的主流特效的更多相关文章
- Android -- 真正的 高仿微信 打开网页的进度条效果
(本博客为原创,http://www.cnblogs.com/linguanh/) 目录: 一,为什么说是真正的高仿? 二,为什么要搞缓慢效果? 三,我的实现思路 四,代码,内含注释 五,使用方法与截 ...
- Android中通过线程实现更新ProgressDialog(对话进度条)
作为开发者我们需要经常站在用户角度考虑问题,比如在应用商城下载软件时,当用户点击下载按钮,则会有下载进度提示页面出现,现在我们通过线程休眠的方式模拟下载进度更新的演示,如图(这里为了截图方便设置对话进 ...
- Android UI系列-----时间、日期、Toasts和进度条Dialog
您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注按钮 来关注我的博客的最新动态. 如果文章内容对您有帮助, 不要忘记点击右下角的 推荐按钮 来支持一下哦 如果您对文章内 ...
- 【转】Android UI系列-----时间、日期、Toasts和进度条Dialog
原文网址:http://www.cnblogs.com/xiaoluo501395377/p/3421727.html 您可以通过点击 右下角 的按钮 来对文章内容作出评价, 也可以通过左下方的 关注 ...
- Android简易实战教程--第二话《两种进度条》
点击按钮模拟进度条下载进度,"下载"完成进度条消失. 代码如下: xml: <?xml version="1.0" encoding="utf- ...
- 阅读《Android 从入门到精通》(17)——进度条
进度条(ProgressBar) java.lang.Object; android.view.View; android.widget.ProgressBar; ProgressBar 类方法 Pr ...
- 仿MIUI音量变化环形进度条实现
Android中使用环形进度条的业务场景事实上蛮多的,比方下载文件的时候使用环形进度条.会给用户眼前一亮的感觉:再比方我大爱的MIUI系统,它的音量进度条就是使用环形进度条,尽显小米"为发烧 ...
- Android 自定义View—清爽小巧灵活的多节点进度条
前言 最近项目有一个节点进度条的小需求,完成后,想分享出来希望可以帮到有需要的同学. 真机效果图 自定义View完整代码 开箱即用~,注释已经炒鸡详细了 /** * @description: 节点进 ...
- Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...
随机推荐
- Linux 高性能服务器编程——I/O复用的高级应用
高级应用一:非阻塞connect connect系统调用的man手册中有如下的一段内容: EINPROGRESS The socket is non-blocking and the connecti ...
- Dynamics CRM2016 Web API之更新记录
本篇继续探索web api,介绍如何通过web api更新记录. 下面是一段简单的更新代码,更新了几个不同类型的字段,entity的赋值和前篇创建时候的一样的. var entity = {}; en ...
- [ExtJS5学习笔记]第三十二节 sencha extjs 5与struts2的ajax交互配置
本文地址:http://blog.csdn.net/sushengmiyan/article/details/43487751 本文作者:sushengmiyan ------------------ ...
- Android开发学习之路--Notification之初体验
一般当我们收到短信啊,微信啊,或者有些app的提醒,我们都会在通知栏收到一天简单的消息,然后点击消息进入到app里面,其实android中有专门的Notification的类可以完成这个工作,这里就实 ...
- JQuery实战---窗口效果
在前面的相关博文中,小编对jquery的相关知识进行了简单的总结,关于jquery的很多小的知识点,都需要我们自己去动手和实践,一行行代码都需要我们自己亲自动手去敲,今天我们继续来学习jquery的相 ...
- 最简单的基于FFmpeg的封装格式处理:视音频复用器(muxer)
===================================================== 最简单的基于FFmpeg的封装格式处理系列文章列表: 最简单的基于FFmpeg的封装格式处理 ...
- MVPHelper更新日志 --- 新增常规分包模式
MVPHelper是一款可以自动生成MVP接口以及实现类的android studio插件,彻底解放双手! MVPHelper更新版本啦. 由于之前只支持contract模式,不是很符合大众口味 所以 ...
- linq---我为你提笔序,你的美不只查询语句
LinQ百度百科对她这样解释,是一组用于c#和Visual Basic语言的扩展.它允许编写C#或者Visual Basic代码以查询数据库相同的方式操作内存数据. LINQ是Language Int ...
- J2EE进阶(二)从零开始之Struts2
J2EE进阶(二)从零开始之Struts2 以前自己总是听说什么SSH框架,不明觉厉.现在自己要重整旗鼓,开始系统性的学习SSH框架了.首先开始Struts2的学习.其实自己之前参与过Struts2项 ...
- JSP连接MySQL时出现--错误:Access denied for user 'root'@'localhost' (using password: YES)'解决方案
用代码进行用户验证的时候总是出现这个错误,翻译一下,应该是root用户的是权限的问题没有放开. 那就想办法解决一下吧,具体的来说可以有这样的几种方式. 解决方法,首先想到的是先重启一下MySQL服务吧 ...