package com.hdl.widget;
|
|
import android.annotation.SuppressLint;
|
import android.content.Context;
|
import android.content.res.TypedArray;
|
import android.graphics.Bitmap;
|
import android.graphics.Canvas;
|
import android.graphics.Color;
|
import android.graphics.LinearGradient;
|
import android.graphics.Paint;
|
import android.graphics.Path;
|
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuffXfermode;
|
import android.graphics.RectF;
|
import android.graphics.Shader;
|
import android.os.Bundle;
|
import android.os.Parcelable;
|
import android.util.AttributeSet;
|
import android.util.TypedValue;
|
import android.view.MotionEvent;
|
import android.view.View;
|
|
import com.hdl.widgetxm.R;
|
|
import static android.view.MotionEvent.ACTION_CANCEL;
|
import static android.view.MotionEvent.ACTION_DOWN;
|
import static android.view.MotionEvent.ACTION_MOVE;
|
import static android.view.MotionEvent.ACTION_UP;
|
|
/**
|
* Created by JLChen on 2019/9/3
|
*/
|
public class HDLWaveSeekBar extends View {
|
|
private static final int DEFAULT_WAVE_BORDER_COLOR = 0xFF495780;
|
// private static final int DEFAULT_WAVE_COLOR = 0xFFFC5733;
|
private static final int DEFAULT_WAVE_COLOR = 0xFFFD692E;
|
private static final int DEFAULT_WAVE_END_COLOR = 0xFFFB3140;
|
|
|
|
private static final int DEFAULT_EDGE_LENGTH = 200; // 默认宽高
|
private Paint mBorderPaint;//圆形进度框画笔
|
private Paint circlePaint;//圆形进度框画笔
|
private Paint wavePaint;//绘制波浪画笔
|
private Path wavePath;//绘制波浪Path
|
private Paint mTextPaint; //进度文字显示内容
|
// private Paint secondWavePaint;//绘制第二个波浪的画笔
|
private Bitmap bitmap;//缓存bitmap
|
private Canvas bitmapCanvas;
|
|
private float waveWidth;//波浪宽度
|
private float waveHeight;//波浪高度
|
private int waveNum;//波浪组的数量(一次起伏为一组)
|
private float waveMovingDistance;//波浪平移的距离
|
|
private float defaultSize;//自定义View默认的宽高
|
private float mPercent;//进度条占比
|
private int mProgress;//可以更新的进度条数值
|
|
// private int waveColor;//波浪颜色
|
// private int secondWaveColor;//第二层波浪颜色
|
private int bgColor;//背景进度颜色
|
|
private int mBorderColor; //背景边框框颜色
|
private float mBorderWidth; //背景边框框宽度
|
private int mPadding = 10;
|
|
private boolean isClickable = true;
|
|
private float mSeekBarWidth;
|
private float mSeekBarHeight;
|
|
private float mWaveViewWidth;//重新测量后View实际的宽
|
private float mWaveViewHeight;//重新测量后View实际的高
|
|
private int mMaxValue; // 最大数值
|
private int mMinValue; // 最小数值
|
private float mCenterX; // 圆弧 SeekBar 中心点 X
|
private float mCenterY; // 圆弧 SeekBar 中心点 Y
|
private String mProgressBarUnitSring; //进度单位符号
|
private static final String DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING = "%"; // 默认显示单位
|
private float mWaveLeftAndTop,mWaveRight,mWaveBottom;
|
private float mBorderLeftAndTop;
|
private boolean isProgressTextShow = true;
|
|
private int mCornerRadius; //圆角半径大小
|
|
private boolean isDrawSecondWave;
|
private float nowY;
|
private int[] mArcProgressBarColors; // 当前进度渐变颜色数组
|
|
|
public HDLWaveSeekBar(Context context) {
|
this(context, null);
|
}
|
|
public HDLWaveSeekBar(Context context, AttributeSet attrs) {
|
this(context, attrs, 0);
|
}
|
|
public HDLWaveSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
|
super(context, attrs, defStyleAttr);
|
setSaveEnabled(true);
|
setLayerType(LAYER_TYPE_SOFTWARE, null);
|
initAttrs(context, attrs);
|
initData();
|
initPaint();
|
}
|
|
private void initData() {
|
defaultSize = dp2px(100);
|
mBorderWidth = dp2px(2);
|
mPadding = dp2px(10);
|
waveNum =(int) Math.ceil(Double.parseDouble(String.valueOf(defaultSize / waveWidth / 2)));
|
waveMovingDistance = 0;
|
mPercent = 0.0f;
|
mProgress = 0;
|
isDrawSecondWave = false;
|
mMaxValue = 100;
|
mMinValue = 0;
|
mCornerRadius = dp2px(10);
|
mProgressBarUnitSring = DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING;
|
isProgressTextShow = true;
|
mArcProgressBarColors = new int[]{DEFAULT_WAVE_COLOR,DEFAULT_WAVE_END_COLOR};
|
}
|
|
private void initPaint() {
|
wavePath = new Path();
|
wavePaint = new Paint();
|
wavePaint.setColor(DEFAULT_WAVE_COLOR);
|
wavePaint.setAntiAlias(true);//设置抗锯齿
|
wavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
// secondWavePaint = new Paint();
|
// secondWavePaint.setColor(secondWaveColor);
|
// secondWavePaint.setAntiAlias(true);//设置抗锯齿
|
// secondWavePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_ATOP));//因为要覆盖在第一层波浪上,且要让半透明生效,所以选此模式
|
|
circlePaint = new Paint();
|
circlePaint.setColor(bgColor);
|
circlePaint.setAntiAlias(true);//设置抗锯齿
|
|
mBorderPaint = new Paint();
|
mBorderPaint.setStyle(Paint.Style.STROKE);
|
mBorderPaint.setColor(mBorderColor);
|
mBorderPaint.setAntiAlias(true);//设置抗锯齿
|
mBorderPaint.setStrokeWidth(mBorderWidth);
|
initTextPanit();
|
}
|
|
// 初始化拖动按钮顶部文字画笔和进度圆弧画笔
|
private void initTextPanit() {
|
mTextPaint = new Paint();
|
mTextPaint.setAntiAlias(true);
|
mTextPaint.setStrokeWidth(1);
|
mTextPaint.setTextSize(getTextSizeDip(20));
|
mTextPaint.setColor(Color.BLACK);
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
}
|
|
// public HDLWaveSeekBar(Context context, @Nullable AttributeSet attrs) {
|
// super(context, attrs);
|
// init(context,attrs);
|
// }
|
|
private void initAttrs(Context context,AttributeSet attrs){
|
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HDLWaveSeekBar);
|
waveWidth = typedArray.getDimension(R.styleable.HDLWaveSeekBar_wave_width, dp2px(80));
|
waveHeight = typedArray.getDimension(R.styleable.HDLWaveSeekBar_wave_height,dp2px(15));
|
// waveColor = typedArray.getColor(R.styleable.HDLWaveSeekBar_wave_color, DEFAULT_WAVE_COLOR);
|
// secondWaveColor = typedArray.getColor(R.styleable.HDLWaveSeekBar_second_wave_color,getResources().getColor(R.color.light));
|
bgColor = typedArray.getColor(R.styleable.HDLWaveSeekBar_wave_bg_color,Color.WHITE);
|
mBorderColor = typedArray.getColor(R.styleable.HDLWaveSeekBar_wave_border_color, DEFAULT_WAVE_BORDER_COLOR);
|
|
typedArray.recycle();
|
}
|
|
private int dp2px(int dp) {
|
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getContext().getResources().getDisplayMetrics());
|
}
|
|
private float getTextSizeDip(float value) {
|
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, value, getResources().getDisplayMetrics());
|
}
|
|
|
private void onWaveSizeChange(){
|
mWaveViewWidth = mSeekBarWidth - mPadding*4 - mBorderWidth*2;
|
mWaveViewHeight = mSeekBarHeight - mPadding*4 - mBorderWidth*2;
|
mWaveLeftAndTop = mPadding*2 + mBorderWidth;
|
|
mWaveRight = mSeekBarWidth - mPadding*2 - mBorderWidth;
|
mWaveBottom = mSeekBarHeight - mPadding*2 - mBorderWidth;
|
|
mBorderLeftAndTop = mPadding + mBorderWidth/2;
|
waveNum = (int) Math.ceil(Double.parseDouble(String.valueOf(mWaveViewWidth / waveWidth / 2)));
|
|
}
|
|
@Override
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
int ws = MeasureSpec.getSize(widthMeasureSpec); //取出宽度的确切数值
|
int wm = MeasureSpec.getMode(widthMeasureSpec); //取出宽度的测量模式
|
int hs = MeasureSpec.getSize(heightMeasureSpec); //取出高度的确切数值
|
int hm = MeasureSpec.getMode(heightMeasureSpec); //取出高度的测量模
|
|
if (wm == MeasureSpec.UNSPECIFIED) {
|
wm = MeasureSpec.EXACTLY;
|
ws = dp2px(DEFAULT_EDGE_LENGTH);
|
} else if (wm == MeasureSpec.AT_MOST) {
|
wm = MeasureSpec.EXACTLY;
|
ws = Math.min(dp2px(DEFAULT_EDGE_LENGTH), ws);
|
}
|
if (hm == MeasureSpec.UNSPECIFIED) {
|
hm = MeasureSpec.EXACTLY;
|
hs = dp2px(DEFAULT_EDGE_LENGTH);
|
} else if (hm == MeasureSpec.AT_MOST) {
|
hm = MeasureSpec.EXACTLY;
|
hs = Math.min(dp2px(DEFAULT_EDGE_LENGTH), hs);
|
}
|
setMeasuredDimension(MeasureSpec.makeMeasureSpec(ws, wm), MeasureSpec.makeMeasureSpec(hs, hm));
|
|
mSeekBarHeight = hs;
|
mSeekBarWidth = ws;
|
mCenterX = mSeekBarWidth/2;
|
mCenterY = mSeekBarHeight/2;
|
onWaveSizeChange();
|
}
|
|
private int measureSize(int defaultSize,int measureSpec) {
|
int result = defaultSize;
|
int specMode = MeasureSpec.getMode(measureSpec);
|
int specSize = MeasureSpec.getSize(measureSpec);
|
|
if (specMode == MeasureSpec.EXACTLY) {
|
result = specSize;
|
} else if (specMode == MeasureSpec.AT_MOST) {
|
result = Math.min(result, specSize);
|
}
|
return result;
|
}
|
|
@Override
|
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
super.onSizeChanged(w, h, oldw, oldh);
|
// 计算在当前大小下,内容应该显示的大小和起始位置
|
int safeW = w - getPaddingLeft() - getPaddingRight();
|
int safeH = h - getPaddingTop() - getPaddingBottom();
|
mSeekBarHeight = safeH;
|
mSeekBarWidth = safeW;
|
mCenterX = mSeekBarWidth/2;
|
mCenterY = mSeekBarHeight/2;
|
onWaveSizeChange();
|
// Log.i("getCurrentProgress ","mSeekBarHeight :Y " + mSeekBarHeight + " mSeekBarWidth:"+mSeekBarWidth);
|
}
|
|
|
|
@Override
|
protected void onDraw(Canvas canvas) {
|
super.onDraw(canvas);
|
|
canvas.save();
|
|
//画圆角矩形
|
RectF rectF = new RectF(mBorderLeftAndTop, mBorderLeftAndTop, mSeekBarWidth - mBorderLeftAndTop, mSeekBarHeight-mBorderLeftAndTop);
|
canvas.drawRoundRect(rectF, mCornerRadius, mCornerRadius, mBorderPaint);
|
|
// //画圆角矩形
|
// RectF rectF2 = new RectF(10, 10, mWaveViewWidth-20, mWaveViewWidth-20);
|
// canvas.drawRoundRect(rectF2, 80, 80, circlePaint);
|
// canvas.drawPath(getWavePath(),wavePaint);
|
// canvas.restore();
|
|
bitmap = Bitmap.createBitmap((int)mSeekBarWidth, (int)mSeekBarHeight, Bitmap.Config.ARGB_8888);
|
bitmapCanvas = new Canvas(bitmap);
|
|
RectF rectF2 = new RectF(mWaveLeftAndTop, mWaveLeftAndTop, mWaveRight, mWaveBottom);
|
bitmapCanvas.drawRoundRect(rectF2, mCornerRadius, mCornerRadius, circlePaint);
|
resetmArcProgressBarColor();//实现渐变效果
|
// bitmapCanvas.drawCircle(mWaveViewWidth/2, mWaveViewWidth/2, mWaveViewWidth/2, circlePaint);
|
bitmapCanvas.drawPath(getWavePath(), wavePaint);
|
// if(isDrawSecondWave){
|
// bitmapCanvas.drawPath(getSecondWavePath(),secondWavePaint);
|
// }
|
|
|
canvas.drawBitmap(bitmap, 0, 0, null);
|
|
if(isProgressTextShow) {
|
if(mPercent>0.6){
|
mTextPaint.setColor(Color.WHITE);
|
}else {
|
mTextPaint.setColor(Color.BLACK);
|
}
|
canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mCenterX, mCenterY, mTextPaint);
|
}
|
|
canvas.restore();
|
|
}
|
|
/**
|
* 重置 mArcProgressBarColors 颜色
|
*/
|
private void resetmArcProgressBarColor() {
|
LinearGradient gradient = new LinearGradient(mBorderLeftAndTop, mBorderLeftAndTop, mSeekBarWidth - mBorderLeftAndTop, mSeekBarHeight-mBorderLeftAndTop, mArcProgressBarColors, null, Shader.TileMode.MIRROR);
|
wavePaint.setShader(gradient);
|
}
|
|
//--- 初始化结束 -------------------------------------------------------------------------------
|
|
//--- 状态存储 ---------------------------------------------------------------------------------
|
private static final String KEY_PROGRESS_PRESENT = "PRESENTWAVE"; // 用于存储和获取当前百分比
|
@Override
|
protected Parcelable onSaveInstanceState() {
|
Bundle bundle = new Bundle();
|
bundle.putParcelable("superState", super.onSaveInstanceState());
|
bundle.putFloat(KEY_PROGRESS_PRESENT, mProgress);
|
return bundle;
|
}
|
|
@Override
|
protected void onRestoreInstanceState(Parcelable state) {
|
if (state instanceof Bundle) {
|
Bundle bundle = (Bundle) state;
|
this.mProgress = bundle.getInt(KEY_PROGRESS_PRESENT);
|
state = bundle.getParcelable("superState");
|
}
|
if (null != mOnProgressChangeListener) {
|
mOnProgressChangeListener.onProgressChanged(this, this.mProgress, false);
|
}
|
super.onRestoreInstanceState(state);
|
}
|
|
//--- 状态存储结束 -----------------------------------------------------------------------------
|
|
private Path getWavePath(){
|
// float changeWaveHeight = (1 - mPercent) * waveHeight;
|
float changeWaveHeight = waveHeight;
|
nowY = (1-mPercent)*mWaveViewHeight + mWaveLeftAndTop;
|
|
wavePath.reset();
|
|
//移动到右上方,也就是p0点
|
wavePath.moveTo(mWaveRight, nowY);
|
//移动到右下方,也就是p1点
|
wavePath.lineTo(mWaveRight, mWaveBottom);
|
//移动到左下边,也就是p2点
|
wavePath.lineTo(mWaveLeftAndTop, mWaveBottom);
|
//移动到左上方,也就是p3点
|
//wavePath.lineTo(0, (1-mPercent)*mWaveViewWidth);
|
// wavePath.lineTo(-waveMovingDistance, (1-mPercent)*mSeekBarHeight);
|
|
if(mPercent == 0 || mPercent ==1){
|
wavePath.lineTo(mWaveLeftAndTop, nowY);
|
}else {
|
wavePath.lineTo(-waveMovingDistance, nowY);
|
//从p3开始向p0方向绘制波浪曲线
|
for (int i = 0; i < waveNum * 2; i++) {
|
wavePath.rQuadTo(waveWidth / 2, changeWaveHeight, waveWidth, 0);
|
wavePath.rQuadTo(waveWidth / 2, -changeWaveHeight, waveWidth, 0);
|
}
|
}
|
|
//将path封闭起来
|
wavePath.close();
|
return wavePath;
|
}
|
|
|
private boolean mCanDrag = false; // 是否允许拖动
|
private boolean moved = false; // 是否允许
|
private int lastProgress = -1;
|
|
@SuppressLint("ClickableViewAccessibility")
|
@Override
|
public boolean onTouchEvent(MotionEvent event) {
|
super.onTouchEvent(event);
|
if(!isClickable) return false;
|
int action = event.getActionMasked();
|
switch (action) {
|
case ACTION_DOWN:
|
moved = false;
|
judgeCanDrag(event);
|
if (null != mOnProgressChangeListener) {
|
mOnProgressChangeListener.onStartTrackingTouch(this);
|
}
|
break;
|
case ACTION_MOVE:
|
if (!mCanDrag) {
|
break;
|
}
|
setCurrentPercent(event);
|
// 事件回调
|
if (null != mOnProgressChangeListener && mProgress != lastProgress) {
|
mOnProgressChangeListener.onProgressChanged(this, mProgress, true);
|
lastProgress = mProgress;
|
}
|
moved = true;
|
break;
|
case ACTION_UP:
|
case ACTION_CANCEL:
|
if(!moved){
|
if (isInArcProgress(event.getX(),event.getY())) {
|
setCurrentPercent(event);
|
}
|
}
|
if (null != mOnProgressChangeListener) {
|
mOnProgressChangeListener.onStopTrackingTouch(this);
|
}
|
break;
|
}
|
|
invalidate();
|
return true;
|
}
|
|
|
// 判断是否允许拖动
|
private void judgeCanDrag(MotionEvent event) {
|
if(isInArcProgress(event.getX(),event.getY())){
|
mCanDrag = true;
|
}else {
|
mCanDrag = false;
|
}
|
}
|
|
private void setCurrentPercent(MotionEvent event) {
|
// Log.i("getCurrentProgress ","getCurrentProgress :Y " + event.getY() + " X:"+event.getX());
|
float progressY = event.getY() - mWaveLeftAndTop;
|
float progress = progressY /mWaveViewHeight;
|
progress = 1 - progress;
|
if (progress < 0) progress = 0;
|
if (progress > 1) progress = 1;
|
mPercent = progress;
|
mProgress = (int) (mPercent * (mMaxValue - mMinValue)) + mMinValue;
|
waveMovingDistance = mPercent * 500;
|
// Log.i("getCurrentProgress ","getCurrentProgress :mProgress " + mProgress);
|
}
|
|
private boolean isInArcProgress(float px ,float py) {
|
|
if(px < 0 || px > mSeekBarWidth || py < 0 || py > mSeekBarHeight){
|
return false;
|
}else {
|
return true;
|
}
|
}
|
|
|
|
// endregion -----------------------------------------------------------------------------------
|
// region 状态回调 ------------------------------------------------------------------------------
|
|
private HDLWaveSeekBar.OnProgressChangeListener mOnProgressChangeListener;
|
|
public void setOnProgressChangeListener(HDLWaveSeekBar.OnProgressChangeListener onProgressChangeListener) {
|
mOnProgressChangeListener = onProgressChangeListener;
|
}
|
|
public interface OnProgressChangeListener {
|
/**
|
* 进度发生变化
|
*
|
* @param seekBar 拖动条
|
* @param progress 当前进度数值
|
* @param isUser 是否是用户操作, true 表示用户拖动, false 表示通过代码设置
|
*/
|
void onProgressChanged(HDLWaveSeekBar seekBar, int progress, boolean isUser);
|
|
/**
|
* 用户开始拖动
|
*
|
* @param seekBar 拖动条
|
*/
|
void onStartTrackingTouch(HDLWaveSeekBar seekBar);
|
|
/**
|
* 用户结束拖动
|
*
|
* @param seekBar 拖动条
|
*/
|
void onStopTrackingTouch(HDLWaveSeekBar seekBar);
|
}
|
// endregion -----------------------------------------------------------------------------------
|
|
|
//****************************对外接口****************************************************
|
/**
|
* 设置控件是否可用点击
|
* @param isClickable
|
*/
|
public void setIsClickable(boolean isClickable) {
|
this.isClickable = isClickable;
|
}
|
|
public int getProgress() {
|
return mProgress;
|
}
|
|
public void setProgress(int progress) {
|
System.out.println("setProgress = " + progress);
|
if (progress > mMaxValue) progress = mMaxValue;
|
if (progress < mMinValue) progress = mMinValue;
|
mProgress = progress;
|
mPercent = (progress - mMinValue) * 1.0f / (mMaxValue - mMinValue);
|
// System.out.println("setProgress present = " + progress);
|
// if (null != mOnProgressChangeListener) {
|
// mOnProgressChangeListener.onProgressChanged(this, progress, false);
|
// }
|
postInvalidate();
|
|
}
|
|
/**
|
* 进度文字大小
|
* @param textSize
|
*/
|
public void setProgressTextSize(int textSize){
|
mTextPaint.setTextSize(getTextSizeDip(textSize));
|
}
|
|
/**
|
* 进度文字颜色
|
* @param textPaintColor
|
*/
|
public void setProgressTextColor(int textPaintColor){
|
mTextPaint.setColor(textPaintColor);
|
}
|
|
/**
|
* 设置进度显示值单位
|
*
|
* @param progressBarUnitSring 进度显示值单位
|
*/
|
public void setProgressBarUnitSring(String progressBarUnitSring) {
|
mProgressBarUnitSring = progressBarUnitSring;
|
}
|
|
/**
|
* 设置最大数值
|
*
|
* @param max 最大数值
|
*/
|
public void setMaxValue(int max) {
|
mMaxValue = max;
|
}
|
|
/**
|
* 设置最小数值
|
*
|
* @param min 最小数值
|
*/
|
public void setMinValue(int min) {
|
mMinValue = min;
|
}
|
|
public void setBorderWidth(int borderWidth) {
|
// mBorderWidth = dp2px(borderWidth);
|
mBorderWidth = borderWidth;
|
mBorderPaint.setStrokeWidth(mBorderWidth);
|
onWaveSizeChange();
|
postInvalidate();
|
}
|
|
//
|
// public void setWaveColor(int mWaveColor){
|
// waveColor = mWaveColor;
|
// wavePaint.setColor(waveColor);
|
//
|
// }
|
|
/**
|
* 设置颜色
|
* 3个颜色 渐变效果
|
* @param colors 颜色
|
*/
|
public void setProgressBarColors(int[] colors) {
|
if(colors.length < 2) return;
|
mArcProgressBarColors = colors;
|
resetmArcProgressBarColor();
|
postInvalidate();
|
}
|
|
/**
|
* 设置颜色 单种颜色
|
* @param colors 颜色
|
*/
|
public void setProgressBarColor(int colors) {
|
int[] colorsArray = new int[]{colors, colors};
|
mArcProgressBarColors = colorsArray;
|
resetmArcProgressBarColor();
|
postInvalidate();
|
}
|
|
public void setWaveBorderColor(int mmBorderColor){
|
mBorderColor = mmBorderColor;
|
mBorderPaint.setColor(mBorderColor);
|
}
|
|
public void setWaveBgColor(int mBgColor){
|
bgColor = mBgColor;
|
circlePaint.setColor(bgColor);
|
}
|
|
public void setWavePadding(int mmPadding){
|
// mPadding = dp2px(mmPadding);
|
mPadding = mmPadding;
|
onWaveSizeChange();
|
}
|
|
public void setCornerRadius(int mmCornerRadius){
|
// mCornerRadius = dp2px(mmCornerRadius);
|
mCornerRadius = mmCornerRadius;
|
}
|
|
public void setProgressTextShow(boolean progressTextShow) {
|
isProgressTextShow = progressTextShow;
|
}
|
|
/**
|
*当前 进度Y坐标
|
*/
|
public float getProgressY() {
|
return nowY;
|
}
|
}
|