Android仿水波紋流量球進(jìn)度條控制器
仿水波紋流球進(jìn)度條控制器,Android實(shí)現(xiàn)高端大氣的主流特效,供大家參考,具體內(nèi)容如下
效果圖:

CircleView
這里主要是實(shí)現(xiàn)中心圓以及水波特效
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;
// 繪制文字顯示在圓形中間,只是我沒有設(shè)置,我覺得寫在布局上也挺好的
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) {
// 不斷發(fā)消息給自己,使自己不斷被重繪
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));
// 計(jì)算當(dāng)前油量線和水平中線的距離
float centerOffset = Math.abs(mScreenWidth / 2 * mWaterLevel
- mScreenWidth / 4);
// 計(jì)算油量線和與水平中線的角度
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);
// 如果未開始(未調(diào)用startWave方法),繪制一個(gè)扇形
if ((!mStarted) || (mScreenWidth == 0) || (mScreenHeight == 0)) {
// 繪制,即水面靜止時(shí)的高度
RectF oval = new RectF(mScreenWidth / 4, mScreenHeight / 4,
mScreenWidth * 3 / 4, mScreenHeight * 3 / 4);
canvas.drawArc(oval, startAngle, sweepAngle, false, mWavePaint);
return;
}
// 繪制,即水面靜止時(shí)的高度
// 繪制,即水面靜止時(shí)的高度
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時(shí)c都會自增
c = (1L + c);
float f1 = mScreenHeight * (1.0F - (0.25F + mWaterLevel / 2))
- mAmplitude;
// 當(dāng)前油量線的長度
float waveWidth = (float) Math.sqrt(mScreenWidth * mScreenWidth / 16
- centerOffset * centerOffset);
// 與圓半徑的偏移量
float offsetWidth = mScreenWidth / 4 - waveWidth;
int top = (int) (f1 + mAmplitude);
mPath.reset();
// 起始振動X坐標(biāo),結(jié)束振動X坐標(biāo)
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();
// 關(guān)閉硬件加速,防止異常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 保存狀態(tài)
*/
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];
}
};
}
}
我們運(yùn)行一下

其實(shí)他是十分的空曠的,所以也值得我們?nèi)ザㄖ疲覀冊谥虚g加個(gè)流量顯示,再加個(gè)進(jìn)度條
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>
我們要實(shí)現(xiàn)這個(gè),就要調(diào)用它的初始化以及start方法
mCircleView = (CircleView) findViewById(R.id.wave_view);
// 設(shè)置多高,float,0.1-1F
mCircleView.setmWaterLevel(0.1F);
// 開始執(zhí)行
mCircleView.startWave();
別忘了activity銷毀的時(shí)候把它回收哦
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
mCircleView.stopWave();
mCircleView = null;
super.onDestroy();
}
我們再運(yùn)行一遍

但是我們要怎么讓水波紋隨著進(jìn)度條一起上升下降尼?,這里我們就要用到我們剛才寫的SeekBar了,我們實(shí)現(xiàn)它的
setOnSeekBarChangeListener來監(jiān)聽,這樣我們就要復(fù)寫他的三個(gè)方法,這里我們只要用到一個(gè)
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
//跟隨進(jìn)度條滾動
mCircleView.setmWaterLevel((float) progress / 100);
}
這里,我們要這樣算的,我們設(shè)置高度的單位是float,也就是從0-1F,而我們的進(jìn)度是int progress,從0-100,我們就要用(float) progress / 100)并且強(qiáng)轉(zhuǎn)來得到單位,好了,我們現(xiàn)在水波紋的高度就是隨著我們的進(jìn)度條一起變化了,我們再來運(yùn)行一下

好的,這樣的話,我們就只剩下一個(gè)了,就是讓大小隨著我們的進(jìn)度條變化了,這里我們因?yàn)楦耈I不能再主線程中操作,所以我們需要用到我們的老伙計(jì)Handler了,但是用到handler還不夠,我們的進(jìn)度條數(shù)值也是在內(nèi)部類里面,所以這里我們需要用到Handler來傳值了,這里我們用的是Bundle,我們還是在onProgressChanged方法中操作了
//創(chuàng)建一個(gè)消息
Message message = new Message();
Bundle bundle = new Bundle();
//put一個(gè)int值
bundle.putInt("progress", progress);
//裝載
message.setData(bundle);
//發(fā)送消息
handler.sendMessage(message);
//創(chuàng)建表示
message.what = 1;
消息發(fā)送過去了,我們就在前面寫個(gè)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");
}
}
};
這里的計(jì)算公式尼,是當(dāng)前的數(shù)值/100得到百分比再去*最大值。我們現(xiàn)在可以完整的運(yùn)行一下了,其實(shí)和最上面運(yùn)行的圖片是一樣的

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);
// 設(shè)置多高,float,0.1-1F
mCircleView.setmWaterLevel(0.1F);
// 開始執(zhí)行
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);
// 創(chuàng)建一個(gè)消息
Message message = new Message();
Bundle bundle = new Bundle();
// put一個(gè)int值
bundle.putInt("progress", progress);
// 裝載
message.setData(bundle);
// 發(fā)送消息
handler.sendMessage(message);
// 創(chuàng)建表示
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();
}
}
以上就是本文的全部內(nèi)容,希望對大家學(xué)習(xí)Android軟件編程有所幫助。
相關(guān)文章
android實(shí)現(xiàn)系統(tǒng)信息推送
這篇文章主要為大家詳細(xì)介紹了android實(shí)現(xiàn)系統(tǒng)信息推送,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android獲取超級管理員權(quán)限的實(shí)現(xiàn)
這篇文章主要介紹了Android獲取超級管理員權(quán)限的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03
用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個(gè)Android項(xiàng)目(eclipse+android sdk)
這篇文章主要介紹了用Eclipse搭建Android開發(fā)環(huán)境并創(chuàng)建第一個(gè)Android項(xiàng)目,需要的朋友可以參考下2015-09-09
Android開發(fā)實(shí)現(xiàn)多進(jìn)程彈窗效果
這篇文章主要為大家詳細(xì)介紹了Android開發(fā)實(shí)現(xiàn)多進(jìn)程彈窗效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-10-10
Android使用fastjson庫解析json字符串實(shí)戰(zhàn)
fastjson是一個(gè)Java語言編寫的高性能功能完善的JSON庫,它采用一種“假定有序快速匹配”的算法,把JSON?Parse的性能提升到極致,是目前Java語言中最快的JSON庫,Fastjson接口簡單易用,已經(jīng)被廣泛使用在緩存序列化、協(xié)議交互、Web輸出、Android客戶端等多種應(yīng)用場景2023-11-11
Convert WebP to PNG using java
本文主要介紹Convert WebP to PNG using java,這里對 WebP 做了詳細(xì)說明,并講解Linux 環(huán)境下WebP 轉(zhuǎn)png格式的示例,有興趣的小伙伴可以參考下2016-08-08
Android Studio與SVN版本控制程序的協(xié)作使用指南
這篇文章主要介紹了Android Studio與SVN版本控制程序的協(xié)作使用指南,使用Gradle插件自動填寫SVN號并發(fā)布到指定目錄的方法,需要的朋友可以參考下2016-03-03
android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider
這篇文章主要介紹了android基礎(chǔ)總結(jié)篇之八:創(chuàng)建及調(diào)用自己的ContentProvider,有興趣的可以了解一下。2016-11-11

