自定义View之--圆形进度条(流量球)
来源:互联网 发布:数据缺失的解决方案 编辑:程序博客网 时间:2024/05/29 18:01
话不多说直接上效果和代码
package com.tuiliubao.tlb.view;import android.animation.Animator;import android.animation.ValueAnimator;import android.annotation.TargetApi;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.Path;import android.graphics.Point;import android.graphics.RectF;import android.graphics.Shader;import android.os.Build;import android.text.TextPaint;import android.util.AttributeSet;import android.util.Log;import android.view.View;import android.view.animation.LinearInterpolator;import com.tuiliubao.tlb.R;import com.tuiliubao.tlb.utils.Constant;import com.tuiliubao.tlb.utils.MiscUtil;/** * 水波进度条 * 双重水波纹效果 * 可以自行设置外圈进度、水波颜色等 * Created by Xl on 2017/2/26. */public class WaveProgress extends View { private static final String TAG = WaveProgress.class.getSimpleName(); //浅色波浪方向 private static final int L2R = 0; private static final int R2L = 1; private int mDefaultSize; //圆心 private Point mCenterPoint; //半径 private float mRadius; //圆的外接矩形 private RectF mRectF; //深色波浪移动距离 private float mDarkWaveOffset; //浅色波浪移动距离 private float mLightWaveOffset; //浅色波浪方向 private boolean isR2L; //是否锁定波浪不随进度移动 private boolean lockWave; //是否开启抗锯齿 private boolean antiAlias; //最大值 private float mMaxValue; //当前值 private float mValue; //当前进度 private float mPercent; //绘制提示 private TextPaint mHintPaint; private CharSequence mHint; private int mHintColor; private float mHintSize; private Paint mPercentPaint; private float mValueSize; private int mValueColor; //圆环宽度 private float mCircleWidth; //圆环 private Paint mCirclePaint; //圆环颜色 private int mCircleColor; //背景圆环颜色 private int mBgCircleColor; //水波路径 private Path mWaveLimitPath; private Path mWavePath; //水波高度 private float mWaveHeight; //水波数量 private int mWaveNum; //深色水波 private Paint mWavePaint; //深色水波颜色 private int mDarkWaveColor; //浅色水波颜色 private int mLightWaveColor; //深色水波贝塞尔曲线上的起始点、控制点 private Point[] mDarkPoints; //浅色水波贝塞尔曲线上的起始点、控制点 private Point[] mLightPoints; //贝塞尔曲线点的总个数 private int mAllPointCount; private int mHalfPointCount; private ValueAnimator mProgressAnimator; private long mDarkWaveAnimTime; private ValueAnimator mDarkWaveAnimator; private long mLightWaveAnimTime; private ValueAnimator mLightWaveAnimator; public WaveProgress(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(Context context, AttributeSet attrs) { mDefaultSize = MiscUtil.dipToPx(context, Constant.DEFAULT_SIZE); mRectF = new RectF(); mCenterPoint = new Point(); initAttrs(context, attrs); initPaint(); initPath(); } private void initAttrs(Context context, AttributeSet attrs) { TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.WaveProgress); antiAlias = typedArray.getBoolean(R.styleable.WaveProgress_antiAlias, true); mDarkWaveAnimTime = typedArray.getInt(R.styleable.WaveProgress_darkWaveAnimTime, Constant.DEFAULT_ANIM_TIME); mLightWaveAnimTime = typedArray.getInt(R.styleable.WaveProgress_lightWaveAnimTime, Constant.DEFAULT_ANIM_TIME); mMaxValue = typedArray.getFloat(R.styleable.WaveProgress_maxValue, Constant.DEFAULT_MAX_VALUE); mValue = typedArray.getFloat(R.styleable.WaveProgress_value, Constant.DEFAULT_VALUE); mValueSize = typedArray.getDimension(R.styleable.WaveProgress_valueSize, Constant.DEFAULT_VALUE_SIZE); mValueColor = typedArray.getColor(R.styleable.WaveProgress_valueColor, Color.BLACK); mHint = typedArray.getString(R.styleable.WaveProgress_hint); mHintColor = typedArray.getColor(R.styleable.WaveProgress_hintColor, Color.BLACK); mHintSize = typedArray.getDimension(R.styleable.WaveProgress_hintSize, Constant.DEFAULT_HINT_SIZE); mCircleWidth = typedArray.getDimension(R.styleable.WaveProgress_circleWidth, Constant.DEFAULT_ARC_WIDTH - 5); mCircleColor = typedArray.getColor(R.styleable.WaveProgress_circleColor, Color.GREEN); mBgCircleColor = typedArray.getColor(R.styleable.WaveProgress_bgCircleColor, Color.WHITE); mWaveHeight = typedArray.getDimension(R.styleable.WaveProgress_waveHeight, Constant.DEFAULT_WAVE_HEIGHT); mWaveNum = typedArray.getInt(R.styleable.WaveProgress_waveNum, 1); mDarkWaveColor = typedArray.getColor(R.styleable.WaveProgress_darkWaveColor, Color.GREEN); mLightWaveColor = typedArray.getColor(R.styleable.WaveProgress_lightWaveColor, getResources().getColor(android.R.color.holo_green_light)); isR2L = typedArray.getInt(R.styleable.WaveProgress_lightWaveDirect, R2L) == R2L; lockWave = typedArray.getBoolean(R.styleable.WaveProgress_lockWave, false); typedArray.recycle(); } private void initPaint() { mHintPaint = new TextPaint(); // 设置抗锯齿,会消耗较大资源,绘制图形速度会变慢。 mHintPaint.setAntiAlias(antiAlias); // 设置绘制文字大小 mHintPaint.setTextSize(mHintSize); // 设置画笔颜色 mHintPaint.setColor(mHintColor); // 从中间向两边绘制,不需要再次计算文字 mHintPaint.setTextAlign(Paint.Align.CENTER); mCirclePaint = new Paint(); mCirclePaint.setAntiAlias(antiAlias); mCirclePaint.setStrokeWidth(mCircleWidth); mCirclePaint.setStyle(Paint.Style.STROKE); mCirclePaint.setStrokeCap(Paint.Cap.ROUND); mWavePaint = new Paint(); mWavePaint.setAntiAlias(antiAlias); mWavePaint.setStyle(Paint.Style.FILL); // TODO: 2017/7/5 渐变色 LinearGradient gradient = new LinearGradient(250, 0, 250, 300, Color.GREEN, getResources().getColor(R.color.light), Shader.TileMode.MIRROR); mWavePaint.setShader(gradient); mPercentPaint = new Paint(); mPercentPaint.setTextAlign(Paint.Align.CENTER); mPercentPaint.setAntiAlias(antiAlias); mPercentPaint.setColor(mValueColor); mPercentPaint.setTextSize(mValueSize); } private void initPath() { mWaveLimitPath = new Path(); mWavePath = new Path(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); setMeasuredDimension(MiscUtil.measure(widthMeasureSpec, mDefaultSize), MiscUtil.measure(heightMeasureSpec, mDefaultSize)); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); Log.d(TAG, "onSizeChanged: w = " + w + "; h = " + h + "; oldw = " + oldw + "; oldh = " + oldh); int minSize = Math.min(getMeasuredWidth() - getPaddingLeft() - getPaddingRight() - 2 * (int) mCircleWidth, getMeasuredHeight() - getPaddingTop() - getPaddingBottom() - 2 * (int) mCircleWidth); mRadius = minSize / 2; mCenterPoint.x = getMeasuredWidth() / 2; mCenterPoint.y = getMeasuredHeight() / 2; //绘制圆弧的边界 mRectF.left = mCenterPoint.x - mRadius - mCircleWidth / 2; mRectF.top = mCenterPoint.y - mRadius - mCircleWidth / 2; mRectF.right = mCenterPoint.x + mRadius + mCircleWidth / 2; mRectF.bottom = mCenterPoint.y + mRadius + mCircleWidth / 2; Log.d(TAG, "onSizeChanged: 控件大小 = " + "(" + getMeasuredWidth() + ", " + getMeasuredHeight() + ")" + ";圆心坐标 = " + mCenterPoint.toString() + ";圆半径 = " + mRadius + ";圆的外接矩形 = " + mRectF.toString()); initWavePoints(); //开始动画 setValue(mValue); startWaveAnimator(); } private void initWavePoints() { //当前波浪宽度 float waveWidth = (mRadius * 2) / mWaveNum; mAllPointCount = 8 * mWaveNum + 1; mHalfPointCount = mAllPointCount / 2; mDarkPoints = getPoint(false, waveWidth); mLightPoints = getPoint(isR2L, waveWidth); } /** * 从左往右或者从右往左获取贝塞尔点 * * @return */ private Point[] getPoint(boolean isR2L, float waveWidth) { Point[] points = new Point[mAllPointCount]; //第1个点特殊处理,即数组的中点 points[mHalfPointCount] = new Point((int) (mCenterPoint.x + (isR2L ? mRadius : -mRadius)), mCenterPoint.y); //屏幕内的贝塞尔曲线点 for (int i = mHalfPointCount + 1; i < mAllPointCount; i += 4) { float width = points[mHalfPointCount].x + waveWidth * (i / 4 - mWaveNum); points[i] = new Point((int) (waveWidth / 4 + width), (int) (mCenterPoint.y - mWaveHeight)); points[i + 1] = new Point((int) (waveWidth / 2 + width), mCenterPoint.y); points[i + 2] = new Point((int) (waveWidth * 3 / 4 + width), (int) (mCenterPoint.y + mWaveHeight)); points[i + 3] = new Point((int) (waveWidth + width), mCenterPoint.y); } //屏幕外的贝塞尔曲线点 for (int i = 0; i < mHalfPointCount; i++) { int reverse = mAllPointCount - i - 1; points[i] = new Point((isR2L ? 2 : 1) * points[mHalfPointCount].x - points[reverse].x, points[mHalfPointCount].y * 2 - points[reverse].y); } //对从右向左的贝塞尔点数组反序,方便后续处理 return isR2L ? MiscUtil.reverse(points) : points; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); drawCircle(canvas); drawLightWave(canvas); drawDarkWave(canvas); drawProgress(canvas); } /** * 绘制圆环 * * @param canvas */ private void drawCircle(Canvas canvas) { canvas.save(); canvas.rotate(270, mCenterPoint.x, mCenterPoint.y); int currentAngle = (int) (360 * mPercent); //画背景圆环 mCirclePaint.setColor(mBgCircleColor); canvas.drawArc(mRectF, currentAngle, 360 - currentAngle, false, mCirclePaint); //画圆环 mCirclePaint.setColor(mCircleColor); canvas.drawArc(mRectF, 0, 360, false, mCirclePaint); canvas.restore(); } /** * 绘制深色波浪(贝塞尔曲线) * * @param canvas */ private void drawDarkWave(Canvas canvas) { mWavePaint.setColor(mDarkWaveColor); drawWave(canvas, mWavePaint, mDarkPoints, mDarkWaveOffset); } /** * 绘制浅色波浪(贝塞尔曲线) * * @param canvas */ private void drawLightWave(Canvas canvas) { mWavePaint.setColor(mLightWaveColor); //从右向左的水波位移应该被减去 drawWave(canvas, mWavePaint, mLightPoints, isR2L ? -mLightWaveOffset : mLightWaveOffset); } @TargetApi(Build.VERSION_CODES.KITKAT) private void drawWave(Canvas canvas, Paint paint, Point[] points, float waveOffset) { mWaveLimitPath.reset(); mWavePath.reset(); float height = lockWave ? 0 : mRadius - 2 * mRadius * mPercent; //moveTo和lineTo绘制出水波区域矩形 mWavePath.moveTo(points[0].x + waveOffset, points[0].y + height); for (int i = 1; i < mAllPointCount; i += 2) { mWavePath.quadTo(points[i].x + waveOffset, points[i].y + height, points[i + 1].x + waveOffset, points[i + 1].y + height); } mWavePath.lineTo(points[mAllPointCount - 1].x, points[mAllPointCount - 1].y + height); //不管如何移动,波浪与圆路径的交集底部永远固定,否则会造成上移的时候底部为空的情况 mWavePath.lineTo(points[mAllPointCount - 1].x, mCenterPoint.y + mRadius); mWavePath.lineTo(points[0].x, mCenterPoint.y + mRadius); mWavePath.close(); mWaveLimitPath.addCircle(mCenterPoint.x, mCenterPoint.y, mRadius, Path.Direction.CW); //取该圆与波浪路径的交集,形成波浪在圆内的效果 mWaveLimitPath.op(mWavePath, Path.Op.INTERSECT); canvas.drawPath(mWaveLimitPath, paint); } private void drawProgress(Canvas canvas) { float y = mCenterPoint.y - (mPercentPaint.descent() + mPercentPaint.ascent()) / 2; canvas.drawText(String.format("%.0f%%", mPercent * 100), mCenterPoint.x, y, mPercentPaint); if (mHint != null) { float hy = mCenterPoint.y * 2 / 3 - (mHintPaint.descent() + mHintPaint.ascent()) / 2; canvas.drawText(mHint.toString(), mCenterPoint.x, hy, mHintPaint); } } public float getMaxValue() { return mMaxValue; } public void setMaxValue(float maxValue) { mMaxValue = maxValue; } /** * 设置当前值 * * @param value */ public void setValue(float value) { if (value > mMaxValue) { value = mMaxValue; } float start = mPercent; float end = value / mMaxValue; Log.d(TAG, "setValue, value = " + value + ";start = " + start + "; end = " + end); startAnimator(start, end, mDarkWaveAnimTime); } private void startAnimator(final float start, float end, long animTime) { Log.d(TAG, "startAnimator,value = " + mValue + ";start = " + start + ";end = " + end + ";time = " + animTime); //当start=0且end=0时,不需要启动动画 if (start == 0 && end == 0) { return; } mProgressAnimator = ValueAnimator.ofFloat(start, end); mProgressAnimator.setDuration(animTime); mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mPercent = (float) animation.getAnimatedValue(); if (mPercent == 0.0f || mPercent == 1.0f) { stopWaveAnimator(); } else { startWaveAnimator(); } mValue = mPercent * mMaxValue;// if (BuildConfig.DEBUG) {// Log.d(TAG, "onAnimationUpdate: percent = " + mPercent// + ";value = " + mValue);// } invalidate(); } }); mProgressAnimator.start(); } private void startWaveAnimator() { startLightWaveAnimator(); startDarkWaveAnimator(); } private void stopWaveAnimator() { if (mDarkWaveAnimator != null && mDarkWaveAnimator.isRunning()) { mDarkWaveAnimator.cancel(); mDarkWaveAnimator = null; } if (mLightWaveAnimator != null && mLightWaveAnimator.isRunning()) { mLightWaveAnimator.cancel(); mLightWaveAnimator = null; } } private void startLightWaveAnimator() { if (mLightWaveAnimator != null && mLightWaveAnimator.isRunning()) { return; } mLightWaveAnimator = ValueAnimator.ofFloat(0, 2 * mRadius); mLightWaveAnimator.setDuration(mLightWaveAnimTime); mLightWaveAnimator.setRepeatCount(ValueAnimator.INFINITE); mLightWaveAnimator.setInterpolator(new LinearInterpolator()); mLightWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mLightWaveOffset = (float) animation.getAnimatedValue(); postInvalidate(); } }); mLightWaveAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mLightWaveOffset = 0; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mLightWaveAnimator.start(); } private void startDarkWaveAnimator() { if (mDarkWaveAnimator != null && mDarkWaveAnimator.isRunning()) { return; } mDarkWaveAnimator = ValueAnimator.ofFloat(0, 2 * mRadius); mDarkWaveAnimator.setDuration(mDarkWaveAnimTime); mDarkWaveAnimator.setRepeatCount(ValueAnimator.INFINITE); mDarkWaveAnimator.setInterpolator(new LinearInterpolator()); mDarkWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mDarkWaveOffset = (float) animation.getAnimatedValue(); postInvalidate(); } }); mDarkWaveAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mDarkWaveOffset = 0; } @Override public void onAnimationEnd(Animator animation) { } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mDarkWaveAnimator.start(); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); stopWaveAnimator(); if (mProgressAnimator != null && mProgressAnimator.isRunning()) { mProgressAnimator.cancel(); } }}
Style代码
<?xml version="1.0" encoding="utf-8"?><resources> <!-- 是否开启抗锯齿 --> <attr name="antiAlias" format="boolean" /> <!-- 圆弧起始角度,3点钟方向为0,顺时针递增,小于0或大于360进行取余 --> <attr name="startAngle" format="float" /> <!-- 圆弧度数 --> <attr name="sweepAngle" format="float" /> <!-- 设置动画时间 --> <attr name="animTime" format="integer" /> <!-- 绘制内容的数值 --> <attr name="maxValue" format="float" /> <attr name="value" format="float" /> <!-- 绘制内容的单位 --> <attr name="unit" format="string|reference" /> <attr name="unitSize" format="dimension" /> <attr name="unitColor" format="color|reference" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" format="string|reference" /> <attr name="hintSize" format="dimension" /> <attr name="hintColor" format="color|reference" /> <!-- 精度,默认为0 --> <attr name="precision" format="integer" /> <attr name="valueSize" format="dimension" /> <attr name="valueColor" format="color|reference" /> <!-- 圆弧颜色,设置多个可实现渐变 --> <attr name="arcColor1" format="color|reference" /> <attr name="arcColor2" format="color|reference" /> <attr name="arcColor3" format="color|reference" /> <!-- 背景圆弧颜色,默认白色 --> <attr name="bgArcColor" format="color|reference" /> <!-- 圆弧宽度 --> <attr name="arcWidth" format="dimension" /> <!-- 圆弧颜色, --> <attr name="arcColors" format="color|reference" /> <!-- 文字的偏移量。相对于圆半径而言,默认三分之一 --> <attr name="textOffsetPercentInRadius" format="float" /> <!-- 圆形进度条 --> <declare-styleable name="CircleProgressBar"> <attr name="antiAlias" /> <attr name="startAngle" /> <attr name="sweepAngle" /> <attr name="animTime" /> <attr name="maxValue" /> <attr name="value" /> <attr name="precision" /> <attr name="valueSize" /> <attr name="valueColor" /> <attr name="textOffsetPercentInRadius" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintColor" /> <!-- 绘制内容的单位 --> <attr name="unit" /> <attr name="unitSize" /> <attr name="unitColor" /> <!-- 圆弧宽度 --> <attr name="arcWidth" /> <attr name="arcColors" /> <!-- 背景圆弧颜色 --> <attr name="bgArcColor" /> <!-- 背景圆弧宽度 --> <attr name="bgArcWidth" format="dimension" /> </declare-styleable> <declare-styleable name="DialProgress"> <attr name="antiAlias" /> <attr name="startAngle" /> <attr name="sweepAngle" /> <attr name="animTime" /> <attr name="maxValue" /> <attr name="value" /> <attr name="precision" /> <attr name="valueSize" /> <attr name="valueColor" /> <attr name="textOffsetPercentInRadius" /> <!-- 绘制内容的单位 --> <attr name="unit" /> <attr name="unitSize" /> <attr name="unitColor" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintColor" /> <!-- 圆弧的宽度 --> <attr name="arcWidth" /> <!-- 刻度的宽度 --> <attr name="dialWidth" format="dimension|reference" /> <!-- 刻度之间的间隔 --> <attr name="dialIntervalDegree" format="integer" /> <!-- 圆弧颜色, --> <attr name="arcColors" /> <!-- 背景圆弧线颜色 --> <attr name="bgArcColor" /> <!-- 刻度线颜色 --> <attr name="dialColor" format="color|reference" /> </declare-styleable> <declare-styleable name="WaveProgress"> <!-- 是否开启抗锯齿 --> <attr name="antiAlias" /> <!-- 深色水波动画时间 --> <attr name="darkWaveAnimTime" format="integer" /> <!-- 浅色水波动画时间 --> <attr name="lightWaveAnimTime" format="integer" /> <!-- 最大值 --> <attr name="maxValue" /> <!-- 当前值 --> <attr name="value" /> <attr name="valueColor" /> <attr name="valueSize" /> <!-- 绘制内容相应的提示语 --> <attr name="hint" /> <attr name="hintSize" /> <attr name="hintColor" /> <!-- 圆环宽度 --> <attr name="circleWidth" format="dimension" /> <!-- 圆环颜色 --> <attr name="circleColor" format="color|reference" /> <!-- 背景圆环颜色 --> <attr name="bgCircleColor" format="color|reference" /> <!-- 锁定水波不随圆环进度改变,默认锁定在50%处 --> <attr name="lockWave" format="boolean" /> <!-- 水波数量 --> <attr name="waveNum" format="integer" /> <!-- 水波高度,峰值和谷值之和 --> <attr name="waveHeight" format="dimension" /> <!-- 深色水波颜色 --> <attr name="darkWaveColor" format="color|reference" /> <!-- 是否显示浅色水波 --> <attr name="showLightWave" format="boolean" /> <!-- 浅色水波颜色 --> <attr name="lightWaveColor" format="color|reference" /> <!-- 浅色水波的方向 --> <attr name="lightWaveDirect" format="enum"> <enum name="L2R" value="0" /> <enum name="R2L" value="1" /> </attr> </declare-styleable> <declare-styleable name="PieView"> <attr name="centerTextSize" format="dimension" /> <attr name="dataTextSize" format="dimension" /> <attr name="centerTextColor" format="color" /> <attr name="dataTextColor" format="color" /> </declare-styleable></resources>
package com.tuiliubao.tlb.view;import android.annotation.TargetApi;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.DrawFilter;import android.graphics.LinearGradient;import android.graphics.Paint;import android.graphics.PaintFlagsDrawFilter;import android.graphics.Path;import android.graphics.RectF;import android.graphics.Shader;import android.os.Build;import android.os.Handler;import android.os.Parcel;import android.os.Parcelable;import android.util.AttributeSet;import android.view.View;import android.widget.ProgressBar;import com.tuiliubao.tlb.R;import com.tuiliubao.tlb.utils.UiUtils;/** 单波水波纹效果进度球 */@TargetApi(Build.VERSION_CODES.HONEYCOMB)public class PercentageBallView extends View { private Context mContext; private int mScreenWidth; private int mScreenHeight; private Paint mRingPaint; private Paint mCirclePaint; private Paint mWavePaint; private Paint flowPaint; private int mRingSTROKEWidth = 8; private int mCircleSTROKEWidth = 8; private int mLineSTROKEWidth = 1; private Handler mHandler; private long c = 0L; private boolean mStarted = false; private final float f = 0.033F; private float mAmplitude = 0.0F; // 振幅 private float mWaterLevel = 0.0F;// 水高(0~1) private Path mPath; // 绘制文字显示在圆形中间,只是我没有设置,我觉得写在布局上也挺好的 private String flowNum = "";// 2/10 private int mGreenStart = getResources().getColor(R.color.green1); private int mGreenEnd = getResources().getColor(R.color.green2); int startX, endX, startY; // y = Asin(wx+b)+h private static final float STRETCH_FACTOR_A = 20; private static final int OFFSET_Y = 0; // 第一条水波移动速度 private static final int TRANSLATE_X_SPEED_ONE = 5; // 第二条水波移动速度 private static final int TRANSLATE_X_SPEED_TWO = 4; private float mCycleFactorW; private float[] mYPositions; private float[] mResetOneYPositions; private float[] mResetTwoYPositions; private int mXOffsetSpeedOne; private int mXOffsetSpeedTwo; private int mXOneOffset; private int mXTwoOffset; private DrawFilter mDrawFilter; /** * @param context */ public PercentageBallView(Context context) { super(context); // TODO Auto-generated constructor stub mContext = context; init(mContext); } /** * @param context * @param attrs */ public PercentageBallView(Context context, AttributeSet attrs) { super(context, attrs); // TODO Auto-generated constructor stub mContext = context; init(mContext); } /** * @param context * @param attrs * @param defStyleAttr */ public PercentageBallView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mContext = context; init(mContext); } public void setmWaterLevel(float mWaterLevel, String str) { this.mWaterLevel = mWaterLevel; this.flowNum = str; } public void setmWaterWave(float mWaterLevel, String str, float mWater) { this.mWaterLevel = mWaterLevel; this.flowNum = str; this.mAmplitude = mWater; } private void init(Context context) { // 将dp转化为px,用于控制不同分辨率上移动速度基本一致 mXOffsetSpeedOne = UiUtils.dipToPx(context, TRANSLATE_X_SPEED_ONE); mXOffsetSpeedTwo = UiUtils.dipToPx(context, TRANSLATE_X_SPEED_TWO); // 初始绘制波纹的画笔 mWavePaint = new Paint(); // 去除画笔锯齿 mWavePaint.setAntiAlias(true); // 设置风格为实线 mWavePaint.setStyle(Paint.Style.FILL); // 设置画笔颜色 mWavePaint.setColor(mGreenStart); mDrawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); // 外圈 mRingPaint = new Paint(); mRingPaint.setColor(mGreenEnd); mRingPaint.setStyle(Paint.Style.STROKE); mRingPaint.setAntiAlias(true); mRingPaint.setStrokeWidth(mRingSTROKEWidth); // 内圈 mCirclePaint = new Paint(); mCirclePaint.setColor(Color.WHITE); mCirclePaint.setStyle(Paint.Style.STROKE); mCirclePaint.setAntiAlias(true); mCirclePaint.setStrokeWidth(mCircleSTROKEWidth); // 文字 flowPaint = new Paint(); flowPaint.setColor(Color.WHITE); flowPaint.setStyle(Paint.Style.FILL); flowPaint.setAntiAlias(true); flowPaint.setTextSize(34); // 内填充 mWavePaint = new Paint(); mWavePaint.setStrokeWidth(1.0F); mWavePaint.setColor(mGreenEnd); LinearGradient gradient = new LinearGradient(250, 0, 250, 300, mGreenEnd, mGreenStart, Shader.TileMode.MIRROR); mWavePaint.setShader(gradient); 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); } } /** * @param measureSpec * @param isWidth * @return * @category 测量 */ 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; // 用于保存原始波纹的y值 mYPositions = new float[mScreenWidth]; // 用于保存波纹一的y值 mResetOneYPositions = new float[mScreenWidth]; // 用于保存波纹二的y值 mResetTwoYPositions = new float[mScreenWidth]; // 将周期定为view总宽度 mCycleFactorW = (float) (2 * Math.PI / mScreenWidth); // 根据view总宽度得出所有对应的y值 for (int i = 0; i < mScreenWidth; i++) { mYPositions[i] = (float) (STRETCH_FACTOR_A * Math.sin(mCycleFactorW * i) + OFFSET_Y); } } @Override protected void onDraw(final Canvas canvas) { // TODO Auto-generated method stub super.onDraw(canvas); // 得到控件的宽高 int width = getWidth(); int height = getHeight(); setBackgroundColor(Color.WHITE);// 可以自定义色值 // 计算当前油量线和水平中线的距离 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; } float num = flowPaint.measureText(flowNum); // 如果未开始(未调用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; final int top = (int) (f1 + mAmplitude); mPath.reset(); // 起始振动X坐标,结束振动X坐标 if (mWaterLevel > 0.50F) { startX = (int) (mScreenWidth / 4 + offsetWidth); endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth); startY = (int) (f1 - mAmplitude * Math.sin(Math.PI * (2.0F * (startX + this.c * width * this.f)) / width)); } else { startX = (int) (mScreenWidth / 4 + offsetWidth - mAmplitude); endX = (int) (mScreenWidth / 2 + mScreenWidth / 4 - offsetWidth + mAmplitude); } // 波浪效果// while (startX < endX) {// 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++;// }// RectF rectf = new RectF(110, 110, 340, 340);// canvas.drawArc(rectf, 320, 329, false, mRingPaint); canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4 + mRingSTROKEWidth / 2, mRingPaint); canvas.drawCircle(mScreenWidth / 2, mScreenHeight / 2, mScreenWidth / 4, mCirclePaint); // 放到这里绘制文字,不然会因为前面的绘图遮挡住文字 canvas.drawText(flowNum, mScreenWidth * 4 / 8 - num / 2, mScreenHeight * 4 / 8, flowPaint); canvas.restore(); /** 双重波纹实现 **/ canvas.setDrawFilter(mDrawFilter); resetPositonY(); for (int i = 0; i < mScreenWidth; i++) { // 减400只是为了控制波纹绘制的y的在屏幕的位置,大家可以改成一个变量,然后动态改变这个变量,从而形成波纹上升下降效果 // 绘制第一条水波纹 canvas.drawLine(i, mScreenHeight - mResetOneYPositions[i] - 400, i, mScreenHeight,mWavePaint); // 绘制第二条水波纹 canvas.drawLine(i, mScreenHeight - mResetTwoYPositions[i] - 400, i, mScreenHeight, mWavePaint); } // 改变两条波纹的移动点 mXOneOffset += mXOffsetSpeedOne; mXTwoOffset += mXOffsetSpeedTwo; // 如果已经移动到结尾处,则重头记录 if (mXOneOffset >= mScreenWidth) { mXOneOffset = 0; } if (mXTwoOffset > mScreenWidth) { mXTwoOffset = 0; } // 引发view重绘,一般可以考虑延迟20-30ms重绘,空出时间片 postInvalidate(); } @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(); } /** * 开始波动 */ 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]; } }; } private void resetPositonY() { // mXOneOffset代表当前第一条水波纹要移动的距离 int yOneInterval = mYPositions.length - mXOneOffset; // 使用System.arraycopy方式重新填充第一条波纹的数据 System.arraycopy(mYPositions, mXOneOffset, mResetOneYPositions, 0, yOneInterval); System.arraycopy(mYPositions, 0, mResetOneYPositions, yOneInterval, mXOneOffset); int yTwoInterval = mYPositions.length - mXTwoOffset; System.arraycopy(mYPositions, mXTwoOffset, mResetTwoYPositions, 0, yTwoInterval); System.arraycopy(mYPositions, 0, mResetTwoYPositions, yTwoInterval, mXTwoOffset); }}
代码下载地址
CSDN下载地址
阅读全文
0 0
- 自定义View之--圆形进度条(流量球)
- 自定义view之圆形进度条
- 自定义view之圆形进度条
- 自定义view之圆形进度条
- 自定义View之简单自定义圆形进度条
- 自定义view学习系列之圆形进度条
- Android自定义View之圆形进度条
- 自定义view之圆形进度条初体验
- 自定义View之渐变色圆形进度条
- 自定义View之音频播放圆形进度条
- Android-自定义View之圆形进度条总结
- Android自定义view之圆形进度条
- 自定义view之动态圆形进度条
- 自定义View圆形进度条
- 自定义view圆形进度条。
- 自定义View圆形进度条
- 自定义view圆形进度条
- 自定义view进度条圆形
- 时间工具类详细说明 DateUtils
- Android如何隐藏标题栏
- bootstrap打开模态滚动条modal窗口引起页面抖动解决办法
- 排序之直接插入排序
- Android系统源码
- 自定义View之--圆形进度条(流量球)
- struts2中使用xml进行validate验证
- VMware虚拟机安装linux系统
- 【原创】【水博客】【实用小工具】打表神器!
- 浅谈Android Surface机制
- 完美的一对------数据分析和市场营销
- R语言学习-JOIN的各种实现
- Apache 搭建一个图片服务器
- python_淘宝