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 HDLOACCurtainSeekBar 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 mSeekBarWidthHalf;//重新测量后View实际的宽一半 private int mSeekBarHeight; private int mTopImageHeight; private float mCurtainViewWidth;//重新测量后View实际的宽 private float mCurtainViewWidthHalf;//重新测量后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 nowX; private float buttonWidth = 10; private int mCurtainPaddingTop = 5; //顶部图片补偿值 private Bitmap bitmap;//缓存bitmap private Canvas bitmapCanvas; private Bitmap bgBitmapCurtain,bgBitmapButton; private Bitmap bgBitmapTop; private boolean isOffline = false; private Paint offlinePaint; private int offlineRadius = 10; public HDLOACCurtainSeekBar(Context context) { this(context, null); } public HDLOACCurtainSeekBar(Context context, AttributeSet attrs) { this(context, attrs, 0); } public HDLOACCurtainSeekBar(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(15); mCurtainPaddingTop = dp2px(5); buttonWidth = 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_h_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() { mSeekBarWidthHalf = mSeekBarWidth / 2; mCurtainViewWidth = mSeekBarWidth - mPadding * 2; mCurtainViewWidthHalf = mCurtainViewWidth / 2; // mCurtainPaddingTop = mPadding - mCurtainPaddingTop; mCurtainViewHeight = mSeekBarHeight - mCurtainPaddingTop; buttonWidth = mCurtainViewWidth / 6; mTopImageHeight = (int) mSeekBarWidth*18/396; updateBitmapImage(); } private void updateBitmapImage(){ try { bgBitmapCurtain = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_curtain_h_progress); bgBitmapCurtain = resizeImage(bgBitmapCurtain, (int) mCurtainViewWidth, (int) mCurtainViewHeight); bgBitmapButton = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_curtain_h_open); bgBitmapButton = resizeImage(bgBitmapButton, (int) buttonWidth, (int) buttonWidth); bgBitmapTop = BitmapFactory.decodeResource(getResources(), R.drawable.ic_wd_curtain_h_top); bgBitmapTop = resizeImage(bgBitmapTop, (int) mSeekBarWidth, mTopImageHeight); } 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) mCurtainViewWidth, (int) mSeekBarHeight, Bitmap.Config.ARGB_8888); bitmapCanvas = new Canvas(bitmap); nowX = (mPercent) * mCurtainViewWidthHalf; if(mPercent==1){ bitmapCanvas.drawBitmap(bgBitmapCurtain, 0 , mCurtainPaddingTop, mImagePaint); }else { bitmapCanvas.drawBitmap(bgBitmapCurtain, nowX - mCurtainViewWidth, mCurtainPaddingTop,mImagePaint); bitmapCanvas.drawBitmap(bgBitmapCurtain, mCurtainViewWidth - nowX, mCurtainPaddingTop, mImagePaint); } canvas.drawBitmap(bitmap, mPadding , 0, null); if (isProgressTextShow) { canvas.drawText(String.valueOf(getProgress()) + mProgressBarUnitSring, mCenterX, mCenterY - buttonWidth, mTextPaint); } canvas.drawBitmap(bgBitmapButton, nowX + mPadding - bgBitmapButton.getWidth() / 2.0F, mSeekBarHeight / 2 - bgBitmapButton.getHeight() / 2.0F, mImagePaint); canvas.drawBitmap(bgBitmapTop,0,0,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; // nowX = (mPercent) * mcurtainViewWidthHalf + mPadding; // // curtainPath.reset(); // // //移动到左上方,也就是p3点 // curtainPath.moveTo(mCurtainLeftAndTop, mCurtainLeftAndTop); // //移动到左下边,也就是p2点 // curtainPath.lineTo(mCurtainLeftAndTop, mSeekBarHeight-mPadding); // //移动到右下方,也就是p1点 // curtainPath.lineTo(nowX, mSeekBarHeight-mPadding); // //移动到右上方,也就是p0点 // curtainPath.lineTo(nowX, 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-nowX, mSeekBarHeight-mPadding); // //移动到右上方,也就是p0点 // curtainPathRight.lineTo(mSeekBarWidth-nowX, 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; } bDownLeft = (event.getX() - mSeekBarWidthHalf) < 0; } private void setCurrentPercent(MotionEvent event) { // Log.i("getCurrentProgress ","getCurrentProgress :Y " + event.getY() + " X:"+event.getX()); // boolean isLeft = (event.getX() - mSeekBarWidthHalf) < 0; float progress = 0; if (bDownLeft) { float progressX = event.getX() - mPadding; progress = progressX / mCurtainViewWidthHalf; } else { float progressX = event.getX() - mSeekBarWidthHalf; progress = 1 - progressX / mCurtainViewWidthHalf; } 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 HDLOACCurtainSeekBar.OnProgressChangeListener mOnProgressChangeListener; public void setOnProgressChangeListener(HDLOACCurtainSeekBar.OnProgressChangeListener onProgressChangeListener) { mOnProgressChangeListener = onProgressChangeListener; } public interface OnProgressChangeListener { /** * 进度发生变化 * * @param seekBar 拖动条 * @param progress 当前进度数值 * @param isUser 是否是用户操作, true 表示用户拖动, false 表示通过代码设置 */ void onProgressChanged(HDLOACCurtainSeekBar seekBar, int progress, boolean isUser); /** * 用户开始拖动 * * @param seekBar 拖动条 */ void onStartTrackingTouch(HDLOACCurtainSeekBar seekBar); /** * 用户结束拖动 * * @param seekBar 拖动条 */ void onStopTrackingTouch(HDLOACCurtainSeekBar 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(); } /** *当前 进度X坐标 */ public float getProgressX() { return nowX; } }