package us.pinguo.edit.sdk.widget; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.AttributeSet; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.widget.Scroller; import us.pinguo.edit.sdk.R; /** * Created by taoli on 14-6-30. */ public class PGEditSeekBar extends View { private Scroller mScroller; private GestureDetector mGestureDetector; private Paint mThumbPaint; private Paint mLinePaint1; private Paint mLinePaint2; private Paint mHighLightLinePaint; private Paint mNailPaint; private float mThumbRadius = 24.0f; private float mNailRadius = 8.0f; private float mNailStrokeWidth = 1.5f; private float mLineWidth = 1.5f; private float mDefaultAreaRadius = ((mThumbRadius - mNailRadius - mNailStrokeWidth) + mThumbRadius) / 2; private float mSeekLength; private float mSeekLineStart; private float mSeekLineEnd; private float mNailOffset; private float mThumbOffset; private int mMaxValue = 100; private int mCurrentValue = 50; private int mDefaultValue = 50; private int mStartValue; private OnSeekChangeListener mListener; private float mStep; private SeekBarGestureListener mGestureListener; private String mLineColor; public PGEditSeekBar(Context context, AttributeSet attrs) { super(context, attrs); mThumbRadius = context.getResources().getDimension(R.dimen.seekbar_thumb_radius); mNailRadius = context.getResources().getDimension(R.dimen.seekbar_nail_radius); mNailStrokeWidth = context.getResources().getDimension(R.dimen.seekbar_nail_stroke_width); mLineWidth = context.getResources().getDimension(R.dimen.seekbar_line_width); init(); } private void init() { mScroller = new Scroller(getContext()); mGestureListener = new SeekBarGestureListener(); mGestureDetector = new GestureDetector(getContext(), mGestureListener); mNailPaint = new Paint(); mNailPaint.setAntiAlias(true); mNailPaint.setColor(Color.parseColor("#ffe049")); mNailPaint.setStrokeWidth(mNailStrokeWidth); mNailPaint.setStyle(Paint.Style.STROKE); mThumbPaint = new Paint(); mThumbPaint.setAntiAlias(true); mThumbPaint.setColor(Color.parseColor("#ffffff")); mThumbPaint.setStyle(Paint.Style.FILL); mLinePaint1 = new Paint(); mLinePaint1.setAntiAlias(true); mLinePaint1.setColor(Color.parseColor("#ffffff")); mLinePaint1.setAlpha(200); mLinePaint2 = new Paint(); mLinePaint2.setAntiAlias(true); mLinePaint2.setColor(Color.parseColor("#ffffff")); mLinePaint2.setAlpha(200); mHighLightLinePaint = new Paint(); mHighLightLinePaint.setAntiAlias(true); mHighLightLinePaint.setColor(Color.parseColor("#ffe049")); mHighLightLinePaint.setAlpha(200); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int hmode = MeasureSpec.getMode(heightMeasureSpec); if (hmode == MeasureSpec.AT_MOST) { int hsize = Math.round(mThumbRadius * 2); hsize += getPaddingTop() + getPaddingBottom(); int wsize = MeasureSpec.getSize(widthMeasureSpec); setMeasuredDimension(wsize, hsize); } else { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } } @Override protected void onDraw(Canvas canvas) { if (mSeekLength == 0) { final int width = getWidth(); mSeekLength = width - getPaddingLeft() - getPaddingRight() - mThumbRadius * 2; mSeekLineStart = getPaddingLeft() + mThumbRadius; mSeekLineEnd = width - getPaddingRight() - mThumbRadius; mNailOffset = mSeekLength * mDefaultValue / mMaxValue; if (mDefaultValue == 0 || mDefaultValue == mMaxValue) { mThumbOffset = mSeekLength * mCurrentValue / mMaxValue; } else { float defaultAreaLength = mDefaultAreaRadius * 2; if (mCurrentValue < mDefaultValue) { mThumbOffset = (mSeekLength - defaultAreaLength) * mCurrentValue / mMaxValue; } else if (mCurrentValue > mDefaultValue) { mThumbOffset = (mSeekLength - defaultAreaLength) * mCurrentValue / mMaxValue + mDefaultAreaRadius * 2; } else { mThumbOffset = mNailOffset; } } } final float top = getMeasuredHeight() / 2 - mLineWidth / 2; final float bottom = top + mLineWidth; final float right1 = mSeekLineStart + mNailOffset + mNailStrokeWidth / 2 - mNailRadius; if (right1 > mSeekLineStart) { canvas.drawRect(mSeekLineStart, top, right1, bottom, mLinePaint1); } final float left2 = right1 + mNailRadius * 2; if (mSeekLineEnd > left2) { canvas.drawRect(left2, top, mSeekLineEnd, bottom, mLinePaint2); } //draw thumb final float nailX = mSeekLineStart + mNailOffset; final float nailY = getMeasuredHeight() / 2; canvas.drawCircle(nailX, nailY, mNailRadius, mNailPaint); float thumbX = mSeekLineStart + mThumbOffset; final float thumbY = getMeasuredHeight() / 2; float highLightLeft = thumbX + mThumbRadius; float highLightRight = nailX - mNailRadius; if (thumbX > nailX) { highLightLeft = nailX + mNailRadius; highLightRight = thumbX - mThumbRadius; } canvas.drawRect(highLightLeft, top, highLightRight, bottom, mHighLightLinePaint); canvas.drawCircle(thumbX, thumbY, mThumbRadius, mThumbPaint); if (mScroller.computeScrollOffset()) { mThumbOffset = mScroller.getCurrY(); invalidate(); } super.onDraw(canvas); } @Override public boolean onTouchEvent(MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) { return true; } if (MotionEvent.ACTION_UP == event.getAction()) { mGestureListener.onUp(event); if (null != mListener) { mListener.onSeekStopped((mCurrentValue + mStartValue) * mStep, mStep); } return true; } return false; } private float getThumbOffset(float pos) { if (pos < 0) { pos = 0; } else if (pos > mSeekLength) { pos = mSeekLength; } return pos; } public void setLineColor(String color) { mHighLightLinePaint.setColor(Color.parseColor(color)); mNailPaint.setColor(Color.parseColor(color)); } public void setThumbSize(float size) { mThumbRadius = size; } private class SeekBarGestureListener extends GestureDetector.SimpleOnGestureListener { public boolean onUp(MotionEvent e) { float initThumbOffset = mThumbOffset; updateThumbOffset(); mScroller.startScroll(0, Math.round(initThumbOffset), 0, Math.round(mThumbOffset - initThumbOffset), 400); mThumbOffset = initThumbOffset; invalidate(); return true; } @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { mThumbOffset -= distanceX; if (mThumbOffset < mSeekLineStart - mThumbRadius) { mThumbOffset = mSeekLineStart - mThumbRadius; } if (mThumbOffset > mSeekLineEnd - mThumbRadius) { mThumbOffset = mSeekLineEnd - mThumbRadius; } float newValue; if (mDefaultValue == 0 || mDefaultValue == mMaxValue) { newValue = mThumbOffset * mMaxValue / mSeekLength; } else { float defaultAreaLength = mDefaultAreaRadius * 2; if (mThumbOffset < mNailOffset - mDefaultAreaRadius) { newValue = mThumbOffset * (mMaxValue - 2) / (mSeekLength - defaultAreaLength); } else if (mThumbOffset > mNailOffset + mDefaultAreaRadius) { newValue = mDefaultValue + (mThumbOffset - mNailOffset - mDefaultAreaRadius) * (mMaxValue - 2) / (mSeekLength - defaultAreaLength) + 1; } else { newValue = mDefaultValue; } } if (newValue < 0) { newValue = 0; } if (newValue > mMaxValue) { newValue = mMaxValue; } setValueInternal(Math.round(newValue)); invalidate(); return true; } @Override public boolean onSingleTapUp(MotionEvent e) { int newValue = mCurrentValue - 1; if (e.getX() > mThumbOffset) { newValue = mCurrentValue + 1; } if (newValue < 0) { newValue = 0; } if (newValue > mMaxValue) { newValue = mMaxValue; } setValueInternal(newValue); float initThumbOffset = mThumbOffset; updateThumbOffset(); mScroller.startScroll(0, Math.round(initThumbOffset), 0, Math.round(mThumbOffset - initThumbOffset), 400); mThumbOffset = initThumbOffset; invalidate(); if (null != mListener) { mListener.onSeekStopped((mCurrentValue + mStartValue) * mStep, mStep); } return true; } @Override public void onLongPress(MotionEvent e) { final int offsetLength = (int) getThumbOffset(e.getX() - mSeekLineStart); mScroller.startScroll(0, (int) mThumbOffset, 0, (int) (offsetLength - mThumbOffset), 400); setValueInternal((int) (offsetLength * mMaxValue / mSeekLength)); updateThumbOffset(); invalidate(); } } public void setSeekLength(int startValue, int endValue, int circleValue, float step) { mDefaultValue = Math.round((float) (circleValue - startValue) / step); mMaxValue = Math.round((float) (endValue - startValue) / step); mStartValue = Math.round((float) startValue / step); mStep = step; } public void setValue(float value) { int newValue = Math.round(value / mStep) - mStartValue; if (newValue == mCurrentValue) { return; } mCurrentValue = newValue; if (null != mListener) { mListener.onSeekChanged(value * mStep, mStep); } updateThumbOffset(); invalidate(); } public float getValue() { return (mCurrentValue + mStartValue) * mStep; } public void setOnSeekChangeListener(OnSeekChangeListener listener) { mListener = listener; } public interface OnSeekChangeListener { void onSeekChanged(float currentValue, float step); void onSeekStopped(float currentValue, float step); } private void setValueInternal(int value) { if (mCurrentValue == value) { return; } mCurrentValue = value; if (null != mListener) { mListener.onSeekChanged((value + mStartValue) * mStep, mStep); } } private void updateThumbOffset() { if (mDefaultValue == 0 || mDefaultValue == mMaxValue) { if (mCurrentValue == 0) { mThumbOffset = 0; } else if (mCurrentValue == mMaxValue) { mThumbOffset = mSeekLineEnd - mSeekLineStart; } else if (mCurrentValue == mDefaultValue) { mThumbOffset = mNailOffset; } else { mThumbOffset = mCurrentValue * mSeekLength / mMaxValue; } } else { float defaultAreaLength = mDefaultAreaRadius * 2; if (mCurrentValue == 0) { mThumbOffset = 0; } else if (mCurrentValue == mMaxValue) { mThumbOffset = mSeekLineEnd - mSeekLineStart; } else if (mCurrentValue < mDefaultValue) { mThumbOffset = (mSeekLength - defaultAreaLength) * mCurrentValue / mMaxValue; } else if (mCurrentValue > mDefaultValue) { mThumbOffset = (mSeekLength - defaultAreaLength) * mCurrentValue / mMaxValue + defaultAreaLength; } else { mThumbOffset = mNailOffset; } } } public void reset() { mSeekLength = 0; mSeekLineStart = 0; mSeekLineEnd = 0; mNailOffset = 0; mThumbOffset = 0; mMaxValue = 0; mCurrentValue = Integer.MAX_VALUE; mDefaultValue = 0; mStartValue = 0; mStep = 0; mScroller.abortAnimation(); } }