package com.hdl.widget;
|
|
import android.annotation.SuppressLint;
|
import android.content.Context;
|
import android.graphics.Bitmap;
|
import android.graphics.BitmapFactory;
|
import android.graphics.Canvas;
|
import android.graphics.Color;
|
import android.graphics.Matrix;
|
import android.graphics.Paint;
|
import android.graphics.RectF;
|
import android.os.Bundle;
|
import android.os.Parcelable;
|
import android.util.AttributeSet;
|
import android.util.Log;
|
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/11/5
|
*/
|
public class HDLRollCurtainSeekBar extends View {
|
// private static final int DEFAULT_OFFLINE_COLOR = 0xC8E9E9EC;
|
private static final int DEFAULT_EDGE_LENGTH = 200; // 默认宽高
|
private Paint mTextPaint; //进度文字显示内容
|
private Paint mImagePaint; //
|
private float defaultSize;//自定义View默认的宽高
|
private float mPercent;//进度条占比
|
private int mProgress;//可以更新的进度条数值
|
private int mPadding = 10;
|
private boolean isClickable = true;
|
private float mSeekBarWidth;
|
private float mSeekBarHeight;
|
|
private float mCurtainViewWidth;//重新测量后View实际的宽
|
private float mCurtainViewHeight;//重新测量后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 mCurtainPaddingTop;
|
|
private boolean isProgressTextShow = true;
|
|
// private int mCornerRadius; //圆角半径大小
|
private float nowY;
|
private float buttonWidth = 10;
|
|
private int mCurtainPaddingTop = 5; //顶部图片补偿值
|
private int mCurtainPaddingHeight = 5; //
|
|
|
private Bitmap bitmap;//缓存bitmap
|
private Canvas bitmapCanvas;
|
private Bitmap bgBitmapCurtain,bgBitmapButton;
|
|
private boolean isOffline = false;
|
private Paint offlinePaint;
|
private int offlineRadius = 10;
|
|
|
|
public HDLRollCurtainSeekBar(Context context) {
|
this(context, null);
|
}
|
|
public HDLRollCurtainSeekBar(Context context, AttributeSet attrs) {
|
this(context, attrs, 0);
|
}
|
|
public HDLRollCurtainSeekBar(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);
|
mPadding = dp2px(4);
|
mCurtainPaddingTop = dp2px(55);
|
buttonWidth = dp2px(10);
|
mCurtainPaddingHeight= dp2px(10);
|
offlineRadius = dp2px(5);
|
mPercent = 0.0f;
|
mProgress = 0;
|
mMaxValue = 100;
|
mMinValue = 0;
|
// mCornerRadius = dp2px(2);
|
mProgressBarUnitSring = DEFAULT_ARC_PROGRESS_BAR_UNIT_STRING;
|
isProgressTextShow = true;
|
setBackgroundResource(R.drawable.ic_wd_curtain_roll_bg);
|
}
|
|
private void initPaint() {
|
mImagePaint = new Paint();
|
|
offlinePaint = new Paint();
|
offlinePaint.setColor(HDLUtlisXM.DEFAULT_OFFLINE_COLOR);
|
offlinePaint.setAntiAlias(true);//设置抗锯齿
|
|
initTextPanit();
|
}
|
|
// 初始化拖动按钮顶部文字画笔和进度圆弧画笔
|
private void initTextPanit() {
|
mTextPaint = new Paint();
|
mTextPaint.setAntiAlias(true);
|
mTextPaint.setStrokeWidth(1);
|
mTextPaint.setTextSize(getTextSizeDip(16));
|
mTextPaint.setColor(Color.BLACK);
|
mTextPaint.setTextAlign(Paint.Align.CENTER);
|
}
|
|
|
// private void initAttrs(Context context, AttributeSet attrs) {
|
// TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.HDLCurtainSeekBar);
|
//
|
// 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 oncurtainSizeChange() {
|
mCurtainViewWidth = mSeekBarWidth - mPadding * 2;
|
mCurtainViewHeight = mSeekBarHeight - mCurtainPaddingTop;
|
buttonWidth = mCurtainViewWidth / 5;
|
updateBitmapImage();
|
}
|
|
private void updateBitmapImage(){
|
try {
|
bgBitmapCurtain = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_curtain_roll_progress);
|
bgBitmapCurtain = resizeImage(bgBitmapCurtain, (int) mCurtainViewWidth, (int) mCurtainViewHeight + mCurtainPaddingHeight);
|
|
bgBitmapButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_curtain_open);
|
bgBitmapButton = resizeImage(bgBitmapButton, (int) buttonWidth, (int) buttonWidth);
|
} catch (Exception e) {
|
Log.e("updateBitmapImage 失败", e.getMessage());
|
}
|
}
|
|
|
@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;
|
oncurtainSizeChange();
|
}
|
|
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;
|
oncurtainSizeChange();
|
// Log.i("getCurrentProgress ","mSeekBarHeight :Y " + mSeekBarHeight + " mSeekBarWidth:"+mSeekBarWidth);
|
}
|
|
|
@SuppressLint("DrawAllocation")
|
@Override
|
protected void onDraw(Canvas canvas) {
|
super.onDraw(canvas);
|
|
canvas.save();
|
|
bitmap = Bitmap.createBitmap((int) mSeekBarWidth, (int) mCurtainViewHeight, Bitmap.Config.ARGB_8888);
|
bitmapCanvas = new Canvas(bitmap);
|
nowY = (mPercent) * mCurtainViewHeight + mCurtainPaddingTop;
|
|
bitmapCanvas.drawBitmap(bgBitmapCurtain, mPadding, nowY - mCurtainViewHeight - mCurtainPaddingTop - mCurtainPaddingHeight ,mImagePaint);
|
|
|
canvas.drawBitmap(bitmap, 0 , mCurtainPaddingTop, null);
|
|
if (isProgressTextShow) {
|
if(mPercent > 0.3) {//小于 70% 的时候文字显示在上方
|
canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mCenterX, nowY - bgBitmapButton.getHeight(), mTextPaint);
|
}else {
|
canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mCenterX, nowY + bgBitmapButton.getHeight(), mTextPaint);
|
}
|
}
|
|
canvas.drawBitmap(bgBitmapButton, mSeekBarWidth/2 - bgBitmapButton.getWidth()/2.0F, nowY - bgBitmapButton.getHeight()/2.0F, mImagePaint);
|
|
//绘制离线遮挡层
|
if(isOffline){
|
RectF rectF2 = new RectF(0, 0, mSeekBarWidth, mSeekBarHeight);
|
canvas.drawRoundRect(rectF2, offlineRadius, offlineRadius, offlinePaint);
|
}
|
|
canvas.restore();
|
}
|
|
|
public Bitmap getBitmap(int resId) {
|
BitmapFactory.Options options = new BitmapFactory.Options();
|
TypedValue value = new TypedValue();
|
getResources().openRawResource(resId, value);
|
options.inTargetDensity = value.density;
|
options.inScaled = false;//不缩放
|
return BitmapFactory.decodeResource(getResources(), resId, options);
|
}
|
|
public Bitmap resizeImage(Bitmap bitmap, int width, int height) {
|
int bmpWidth = bitmap.getWidth();
|
int bmpHeight = bitmap.getHeight();
|
|
float scaleWidth = ((float) width) / bmpWidth;
|
float scaleHeight = ((float) height) / bmpHeight;
|
|
Matrix matrix = new Matrix();
|
matrix.postScale(scaleWidth, scaleHeight);
|
|
return Bitmap.createBitmap(bitmap, 0, 0, bmpWidth, bmpHeight, matrix, true);
|
}
|
|
//--- 初始化结束 -------------------------------------------------------------------------------
|
|
//--- 状态存储 ---------------------------------------------------------------------------------
|
private static final String KEY_PROGRESS_PRESENT = "PRESENTHC"; // 用于存储和获取当前百分比
|
|
@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 getCurtainPath() {
|
//// float changeWaveHeight = (1 - mPercent) * curtainHeight;
|
// float changeWaveHeight = curtainHeight;
|
// nowY = (mPercent) * mcurtainViewWidthHalf + mPadding;
|
//
|
// curtainPath.reset();
|
//
|
// //移动到左上方,也就是p3点
|
// curtainPath.moveTo(mCurtainLeftAndTop, mCurtainLeftAndTop);
|
// //移动到左下边,也就是p2点
|
// curtainPath.lineTo(mCurtainLeftAndTop, mSeekBarHeight-mPadding);
|
// //移动到右下方,也就是p1点
|
// curtainPath.lineTo(nowY, mSeekBarHeight-mPadding);
|
// //移动到右上方,也就是p0点
|
// curtainPath.lineTo(nowY, mCurtainLeftAndTop);
|
//
|
// //将path封闭起来
|
// curtainPath.close();
|
//
|
//
|
// return curtainPath;
|
// }
|
//
|
// private Path getCurtainPathRight() {
|
// curtainPathRight.reset();
|
// //移动到左上方,也就是p3点
|
// curtainPathRight.moveTo(mCurtainRight, mCurtainLeftAndTop);
|
// //移动到左下边,也就是p2点
|
// curtainPathRight.lineTo(mCurtainRight, mSeekBarHeight-mPadding);
|
// //移动到右下方,也就是p1点
|
// curtainPathRight.lineTo(mSeekBarWidth-nowY, mSeekBarHeight-mPadding);
|
// //移动到右上方,也就是p0点
|
// curtainPathRight.lineTo(mSeekBarWidth-nowY, mCurtainLeftAndTop);
|
// //将path封闭起来
|
// curtainPathRight.close();
|
// return curtainPathRight;
|
// }
|
//
|
|
|
|
|
private boolean mCanDrag = false; // 是否允许拖动
|
// private boolean bDownLeft = 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());
|
// boolean isLeft = (event.getX() - mSeekBarWidthHalf) < 0;
|
|
float progressY = event.getY() - mCurtainPaddingTop;
|
float progress = progressY /mCurtainViewHeight;
|
|
|
if (progress < 0) progress = 0;
|
if (progress > 1) progress = 1;
|
mPercent = progress;
|
mProgress = (int) ((1 - mPercent) * (mMaxValue - mMinValue)) + mMinValue;
|
|
}
|
|
private boolean isInArcProgress(float px, float py) {
|
|
if (px < 0 || px > mSeekBarWidth || py < 0 || py > mSeekBarHeight) {
|
return false;
|
} else {
|
return true;
|
}
|
}
|
|
|
// endregion -----------------------------------------------------------------------------------
|
// region 状态回调 ------------------------------------------------------------------------------
|
|
private HDLRollCurtainSeekBar.OnProgressChangeListener mOnProgressChangeListener;
|
|
public void setOnProgressChangeListener(HDLRollCurtainSeekBar.OnProgressChangeListener onProgressChangeListener) {
|
mOnProgressChangeListener = onProgressChangeListener;
|
}
|
|
public interface OnProgressChangeListener {
|
/**
|
* 进度发生变化
|
*
|
* @param seekBar 拖动条
|
* @param progress 当前进度数值
|
* @param isUser 是否是用户操作, true 表示用户拖动, false 表示通过代码设置
|
*/
|
void onProgressChanged(HDLRollCurtainSeekBar seekBar, int progress, boolean isUser);
|
|
/**
|
* 用户开始拖动
|
*
|
* @param seekBar 拖动条
|
*/
|
void onStartTrackingTouch(HDLRollCurtainSeekBar seekBar);
|
|
/**
|
* 用户结束拖动
|
*
|
* @param seekBar 拖动条
|
*/
|
void onStopTrackingTouch(HDLRollCurtainSeekBar 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 = 1 - (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 setCurtainPadding(int mmPadding) {
|
// mPadding = dp2px(mmPadding);
|
mPadding = mmPadding;
|
oncurtainSizeChange();
|
}
|
|
public void setProgressTextShow(boolean progressTextShow) {
|
isProgressTextShow = progressTextShow;
|
}
|
|
public void setCurtainPaddingTop(int curtainPaddingTop) {
|
// mCurtainPaddingTop = dp2px(curtainPaddingTop);
|
mCurtainPaddingTop = curtainPaddingTop;
|
oncurtainSizeChange();
|
}
|
|
/**
|
* 设置离线状态
|
* @param offline
|
*/
|
public void setOffline(boolean offline) {
|
isOffline = offline;
|
isClickable = !isOffline;
|
postInvalidate();
|
}
|
|
/**
|
*当前 进度Y坐标
|
*/
|
public float getProgressY() {
|
return nowY;
|
}
|
|
|
}
|