这段时间项目做完了,也没什么事情,本以为可以偷懒一段时间,结果老总接了一个车载项目让我做,心碎,没做过,很慌,经过各方打听,发现车载和手机app开发的流程基本一致,瞬间就淡定了,那就开始搞
项目开始就知道会有各种自定义控件,今天是第一天,搞了一个水波纹的自定义控件,先看效果
第一眼还是觉得可以看的,其实我觉得有点丑,ui妹子说挺好看,好吧,那就这样吧
这里面难点主要是波纹这个了,这个我也是借用了一个开源项目的代码
其他的都很简单,代码如下
package com.example.zhu.demo333.featrue.widget;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.Typeface;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;
/**
* Created by mou on 2017/8/23.
*/
public class WaveLoadingView extends View {
private static final float DEFAULT_AMPLITUDE_RATIO = 0.1f;
private static final float DEFAULT_AMPLITUDE_VALUE = 20.0f;
private static final float DEFAULT_WATER_LEVEL_RATIO = 0.5f;
private static final float DEFAULT_WAVE_LENGTH_RATIO = 1.0f;
//默认的进度
private static final int DEFAULT_WAVE_PROGRESS_VALUE = 100;
//默认波纹颜色
private static final int DEFAULT_WAVE_COLOR = Color.parseColor("#456cda");
//默认背景颜色
private static final int DEFAULT_WAVE_BACKGROUND_COLOR = Color.parseColor("#000000");
//默认文字的颜色
private static final int DEFAULT_TITLE_COLOR = Color.WHITE;
//上面文字的大小
private static final float DEFAULT_TITLE_TOP_SIZE = 16.0f;
//下面文字的大小
private static final float DEFAULT_TITLE_CENTER_SIZE = 18.0f;
//半径
private float radius;
//波浪的高度
private float mAmplitudeRatio = DEFAULT_AMPLITUDE_VALUE / 1000;
//波纹的颜色
private int mWaveColor = DEFAULT_WAVE_COLOR;
//上面的文字
private static final String TOP_TITLE = "本月可用流量";
//下面的文字
private String mCenterTitle = "0.0GB";
private float mDefaultWaterLevel;
private float mWaterLevelRatio = 1f;
private float mWaveShiftRatio = 0f;
private int mProgressValue = DEFAULT_WAVE_PROGRESS_VALUE;
private BitmapShader mWaveShader;
// 渐变矩阵
private Matrix mShaderMatrix;
//画波浪
private Paint mWavePaint;
//画背景
private Paint mWaveBgPaint;
// 上面文字的画笔
private Paint mTopTitlePaint;
//下面文字的画笔
private Paint mCenterTitlePaint;
//画背景
private Paint paintCircleBg;
//画小球的画笔
private Paint mPaintBall;
//外边缘的进度条
private Paint paintProgressOut;
//波浪动画
private ObjectAnimator waveShiftAnim;
private AnimatorSet mAnimatorSet;
//开始角度
private float startangle = 180f;
public WaveLoadingView(final Context context) {
this(context, null);
}
public WaveLoadingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
initAnimation();
mShaderMatrix = new Matrix();
mWavePaint = new Paint();
mWavePaint.setAntiAlias(true);
mWaveBgPaint = new Paint();
mWaveBgPaint.setAntiAlias(true);
mWaveBgPaint.setColor(DEFAULT_WAVE_BACKGROUND_COLOR);
mTopTitlePaint = new Paint();
mTopTitlePaint.setColor(DEFAULT_TITLE_COLOR);
mTopTitlePaint.setStyle(Paint.Style.FILL);
mTopTitlePaint.setAntiAlias(true);
mTopTitlePaint.setTextSize(sp2px(DEFAULT_TITLE_TOP_SIZE));
mCenterTitlePaint = new Paint();
mCenterTitlePaint.setColor(DEFAULT_TITLE_COLOR);
mCenterTitlePaint.setStyle(Paint.Style.FILL);
mCenterTitlePaint.setAntiAlias(true);
mCenterTitlePaint.setTextSize(sp2px(DEFAULT_TITLE_CENTER_SIZE));
Typeface font = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
mCenterTitlePaint.setTypeface(font);
paintCircleBg = new Paint();
paintCircleBg.setStyle(Paint.Style.FILL);
paintCircleBg.setAntiAlias(true);
paintCircleBg.setColor(Color.BLACK);
paintProgressOut = new Paint();
paintProgressOut.setStyle(Paint.Style.STROKE);
paintProgressOut.setAntiAlias(true);
paintProgressOut.setColor(Color.parseColor("#446eb5dd"));
paintProgressOut.setStrokeWidth(sp2px(3));
paintProgressOut.setStrokeCap(Paint.Cap.ROUND);
mPaintBall = new Paint();
mPaintBall.setStyle(Paint.Style.FILL);
mPaintBall.setAntiAlias(true);
mPaintBall.setColor(Color.parseColor("#bb45DAB9"));
}
@Override
public void onDraw(Canvas canvas) {
if (mWaveShader != null) {
if (mWavePaint.getShader() == null) {
mWavePaint.setShader(mWaveShader);
}
mShaderMatrix.setScale(1, mAmplitudeRatio / DEFAULT_AMPLITUDE_RATIO, 0, mDefaultWaterLevel);
mShaderMatrix.postTranslate(mWaveShiftRatio * getWidth(),
(DEFAULT_WATER_LEVEL_RATIO - mWaterLevelRatio) * getHeight());
mWaveShader.setLocalMatrix(mShaderMatrix); // Assign matrix to invalidate the shader.
drawCircle(canvas);
} else {
mWavePaint.setShader(null);
}
//画上面的文字
drawTopText(canvas);
//画中间的文字
drawCenterText(canvas);
}
private void drawCircle(Canvas canvas) {
radius = getWidth() / 2f;
paintCircleBg.setColor(Color.BLACK);
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius - dp2px(30), paintCircleBg);
paintCircleBg.setColor(Color.parseColor("#44000000"));
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius - dp2px(20), paintCircleBg);
//画背景
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius - dp2px(40), mWaveBgPaint);
//画波浪
canvas.drawCircle(getWidth() / 2f, getHeight() / 2f, radius - dp2px(40), mWavePaint);
//画进度圆弧
int percent = mProgressValue * (360) / DEFAULT_WAVE_PROGRESS_VALUE;
RectF bgRectf = new RectF(getPaddingLeft() + dp2px(10), getPaddingTop() + dp2px(10),
getMeasuredWidth() - dp2px(10), getMeasuredHeight() - dp2px(10));
canvas.drawArc(bgRectf, startangle, -percent, false, paintProgressOut);//percent的正负表示顺时针和逆时针
//画小球
canvas.save();
canvas.translate(getWidth() / 2, getHeight() / 2);
canvas.rotate(-percent);
canvas.drawCircle(-(radius - dp2px(10)), 0, dp2px(5), mPaintBall);
canvas.rotate(percent);
canvas.restore();
}
private void drawCenterText(Canvas canvas) {
if (!TextUtils.isEmpty(mCenterTitle)) {
float middle = mCenterTitlePaint.measureText(mCenterTitle);
canvas.drawText(mCenterTitle, (getWidth() - middle) / 2,
getHeight()/2 + dp2px(30), mCenterTitlePaint);
}
}
private void drawTopText(Canvas canvas) {
if (!TextUtils.isEmpty(TOP_TITLE)) {
float top = mTopTitlePaint.measureText(TOP_TITLE);
canvas.drawText(TOP_TITLE, (getWidth() - top) / 2, getHeight() / 2, mTopTitlePaint);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateWaveShader();
}
/**
*
*/
private void updateWaveShader() {
// IllegalArgumentException: width and height must be > 0 while loading Bitmap from View
// http://stackoverflow.com/questions/17605662/illegalargumentexception-width-and-height-must-be-0-while-loading-bitmap-from
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if (width > 0 && height > 0) {
double defaultAngularFrequency = 2.0f * Math.PI / DEFAULT_WAVE_LENGTH_RATIO / width;
float defaultAmplitude = height * DEFAULT_AMPLITUDE_RATIO;
mDefaultWaterLevel = height * DEFAULT_WATER_LEVEL_RATIO;
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint wavePaint = new Paint();
wavePaint.setStrokeWidth(2);
wavePaint.setAntiAlias(true);
// Draw default waves into the bitmap.
// y=Asin(ωx+φ)+h
final int endX = width + 1;
final int endY = height + 1;
float[] waveY = new float[endX];
wavePaint.setColor(adjustAlpha(mWaveColor, 0.3f));
for (int beginX = 0; beginX < endX; beginX++) {
double wx = beginX * defaultAngularFrequency;
float beginY = (float) (mDefaultWaterLevel + defaultAmplitude * Math.sin(wx));
canvas.drawLine(beginX, beginY, beginX, endY, wavePaint);
waveY[beginX] = beginY;
}
wavePaint.setColor(mWaveColor);
final int wave2Shift = (int) (width / 4);
for (int beginX = 0; beginX < endX; beginX++) {
canvas.drawLine(beginX, waveY[(beginX + wave2Shift) % endX], beginX, endY, wavePaint);
}
mWaveShader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.CLAMP);
this.mWavePaint.setShader(mWaveShader);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = measureWidth(widthMeasureSpec);
int height = measureHeight(heightMeasureSpec);
int imageSize = (width < height) ? width : height;
setMeasuredDimension(imageSize, imageSize);
}
/**
* 测量宽度
*/
private int measureWidth(int measureSpec) {
int result;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = specSize;
} else {
result = specSize;
}
return result;
}
/**
* 测量高度
*/
private int measureHeight(int measureSpecHeight) {
int result;
int specMode = MeasureSpec.getMode(measureSpecHeight);
int specSize = MeasureSpec.getSize(measureSpecHeight);
if (specMode == MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == MeasureSpec.AT_MOST) {
result = specSize;
} else {
result = specSize;
}
return (result + 2);
}
/**
* 设置进度
*/
public void setProgressValue(int progress) {
mProgressValue = progress;
ObjectAnimator waterLevelAnim = ObjectAnimator.ofFloat(this, "waterLevelRatio", mWaterLevelRatio, (mProgressValue * 1f / 100));
if (progress > 50) {
waterLevelAnim.setDuration(800);
} else {
waterLevelAnim.setDuration(1200);
}
// waterLevelAnim.setInterpolator(new DecelerateInterpolator());
AnimatorSet animatorSetProgress = new AnimatorSet();
animatorSetProgress.play(waterLevelAnim);
animatorSetProgress.start();
}
public void setWaveShiftRatio(float waveShiftRatio) {
if (this.mWaveShiftRatio != waveShiftRatio) {
this.mWaveShiftRatio = waveShiftRatio;
invalidate();
}
}
public float getWaveShiftRatio() {
return mWaveShiftRatio;
}
public void setWaterLevelRatio(float waterLevelRatio) {
if (this.mWaterLevelRatio != waterLevelRatio) {
this.mWaterLevelRatio = waterLevelRatio;
invalidate();
}
}
public void setCenterTitle(String centerTitle) {
mCenterTitle = centerTitle;
invalidate();
}
public void startAnimation() {
if (mAnimatorSet != null) {
mAnimatorSet.start();
}
}
public void endAnimation() {
if (mAnimatorSet != null) {
mAnimatorSet.end();
}
}
public void cancelAnimation() {
if (mAnimatorSet != null) {
mAnimatorSet.cancel();
}
}
private void initAnimation() {
// Wave waves infinitely.
waveShiftAnim = ObjectAnimator.ofFloat(this, "waveShiftRatio", 0f, 1f);
waveShiftAnim.setRepeatCount(ValueAnimator.INFINITE);
waveShiftAnim.setDuration(1000);
waveShiftAnim.setInterpolator(new LinearInterpolator());
mAnimatorSet = new AnimatorSet();
mAnimatorSet.play(waveShiftAnim);
}
@Override
protected void onAttachedToWindow() {
startAnimation();
super.onAttachedToWindow();
}
@Override
protected void onDetachedFromWindow() {
cancelAnimation();
super.onDetachedFromWindow();
}
/**
*/
private int adjustAlpha(int color, float factor) {
int alpha = Math.round(Color.alpha(color) * factor);
int red = Color.red(color);
int green = Color.green(color);
int blue = Color.blue(color);
return Color.argb(alpha, red, green, blue);
}
/**
* Paint.setTextSize(float textSize) default unit is px.
*/
private int sp2px(float spValue) {
final float fontScale = getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
private int dp2px(float dp) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dp * scale + 0.5f);
}
}