专业编程基础技术教程

网站首页 > 基础教程 正文

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

ccvgpt 2024-07-24 11:08:14 基础教程 9 ℃

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

CircleView

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

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

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

Tags:

最近发表
标签列表