From 02802a36db4225f89ee5f5a44953453b43d4057f Mon Sep 17 00:00:00 2001 From: woxingxiao Date: Wed, 22 Feb 2017 17:29:20 +0800 Subject: [PATCH] =?UTF-8?q?v1.9:=20=E2=91=A0new=20feature:=20touch=20to=20?= =?UTF-8?q?seek;=20=E2=91=A1bug=20fixed.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bubbleseekbar/build.gradle | 4 +- .../main/java/com/xw/repo/BubbleSeekBar.java | 163 +++++++++++------- bubbleseekbar/src/main/res/values/attr.xml | 1 + 3 files changed, 106 insertions(+), 62 deletions(-) diff --git a/bubbleseekbar/build.gradle b/bubbleseekbar/build.gradle index b2f2078..7a4ee17 100644 --- a/bubbleseekbar/build.gradle +++ b/bubbleseekbar/build.gradle @@ -8,8 +8,8 @@ android { defaultConfig { minSdkVersion 14 targetSdkVersion 25 - versionCode 8 - versionName "1.8" + versionCode 9 + versionName "1.9" } buildTypes { diff --git a/bubbleseekbar/src/main/java/com/xw/repo/BubbleSeekBar.java b/bubbleseekbar/src/main/java/com/xw/repo/BubbleSeekBar.java index 69ccd20..16aaf78 100644 --- a/bubbleseekbar/src/main/java/com/xw/repo/BubbleSeekBar.java +++ b/bubbleseekbar/src/main/java/com/xw/repo/BubbleSeekBar.java @@ -76,6 +76,7 @@ public class BubbleSeekBar extends View { private boolean isShowSectionMark; // 是否显示份数 private boolean isAutoAdjustSectionMark; // 是否自动滑到最近的整份数,以showSectionMark为前提 private boolean isShowProgressInFloat; // 是否显示小数形式progress,所有小数均保留1位 + private boolean isTouchToSeek; // 是否点击快速seek private int mDelta; // max - min private float mThumbCenterX; // thumb的中心X坐标 @@ -99,6 +100,7 @@ public class BubbleSeekBar extends View { private WindowManager.LayoutParams mLayoutParams; private int[] mPoint = new int[2]; private long mAnimDuration = 200; + private boolean isTouchToSeekAnimEnd = true; public BubbleSeekBar(Context context) { this(context, null); @@ -148,6 +150,7 @@ public BubbleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) { isShowProgressInFloat = a.getBoolean(R.styleable.BubbleSeekBar_bsb_show_progress_in_float, false); int duration = a.getInteger(R.styleable.BubbleSeekBar_bsb_anim_duration, -1); mAnimDuration = duration < 0 ? 200 : duration; + isTouchToSeek = a.getBoolean(R.styleable.BubbleSeekBar_bsb_touch_to_seek, false); a.recycle(); if (mMin > mMax) { @@ -209,20 +212,20 @@ private void calculateRadiusOfBubble() { // 计算滑到两端气泡里文字需要显示的宽度,比较取最大值为气泡的半径 String text = mMin < 0 ? "-" + mMin : "" + mMin; mPaint.getTextBounds(text, 0, text.length(), mRectText); - int w1 = (mRectText.width() + mTextSpace * 2) / 2; + int w1 = (mRectText.width() + mTextSpace * 2) >> 1; if (isShowProgressInFloat) { text = (mMin < 0 ? "-" + mMin : mMin) + ".0"; mPaint.getTextBounds(text, 0, text.length(), mRectText); - w1 = (mRectText.width() + mTextSpace * 2) / 2; + w1 = (mRectText.width() + mTextSpace * 2) >> 1; } text = mMax < 0 ? "-" + mMax : "" + mMax; mPaint.getTextBounds(text, 0, text.length(), mRectText); - int w2 = (mRectText.width() + mTextSpace * 2) / 2; + int w2 = (mRectText.width() + mTextSpace * 2) >> 1; if (isShowProgressInFloat) { text = (mMax < 0 ? "-" + mMax : mMax) + ".0"; mPaint.getTextBounds(text, 0, text.length(), mRectText); - w2 = (mRectText.width() + mTextSpace * 2) / 2; + w2 = (mRectText.width() + mTextSpace * 2) >> 1; } mBubbleRadius = dp2px(14); // 默认半径14dp @@ -306,7 +309,7 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); float x1 = getPaddingLeft(); - float x2 = getWidth() - getPaddingRight(); + float x2 = getMeasuredWidth() - getPaddingRight(); float y = getPaddingTop() + mThumbRadiusOnDragging; if (isShowText) { @@ -352,7 +355,7 @@ protected void onDraw(Canvas canvas) { mThumbCenterX = mTrackLength / mDelta * (mProgress - mMin) + x1; } - if (isShowThumbText && !isThumbOnDragging) { + if (isShowThumbText && !isThumbOnDragging && isTouchToSeekAnimEnd) { // 排除显示小数实时值、首尾文字在Bottom时,滑到首尾的情况(不会与首尾文字重合,故索性不显示) if (mTextPosition == SIDES || !isShowProgressInFloat || ((int) mProgress != mMin && (int) mProgress != mMax)) { @@ -424,9 +427,23 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_DOWN: isThumbOnDragging = isThumbTouched(event); if (isThumbOnDragging) { + showBubble(); + invalidate(); + } else if (isTouchToSeek && isTrackTouched(event)) { + mThumbCenterX = event.getX(); + if (mThumbCenterX < mLeft) { + mThumbCenterX = mLeft; + } + if (mThumbCenterX > mRight) { + mThumbCenterX = mRight; + } + mProgress = (mThumbCenterX - mLeft) * mDelta / mTrackLength + mMin; + mBubbleCenterRawX = mBubbleCenterRawSolidX + mTrackLength * (mProgress - mMin) / mDelta; + showBubble(); invalidate(); } + dx = mThumbCenterX - event.getX(); break; @@ -459,31 +476,47 @@ public boolean onTouchEvent(MotionEvent event) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: if (isAutoAdjustSectionMark) { - autoAdjustSection(); - } else if (isThumbOnDragging) { - mBubbleView.animate().alpha(0f).setDuration(mAnimDuration) - .setListener(new AnimatorListenerAdapter() { - @Override - public void onAnimationEnd(Animator animation) { - hideBubble(); - - isThumbOnDragging = false; - invalidate(); - - if (mProgressListener != null) { - mProgressListener.getProgressOnFinally(getProgress()); - mProgressListener.getProgressOnFinally(getProgressInFloat()); - } - } - - @Override - public void onAnimationCancel(Animator animation) { - hideBubble(); - - isThumbOnDragging = false; - invalidate(); - } - }).start(); + if (isTouchToSeek) { + mBubbleView.postDelayed(new Runnable() { + @Override + public void run() { + isTouchToSeekAnimEnd = false; + autoAdjustSection(); + } + }, 300); + } else { + autoAdjustSection(); + } + } else if (isThumbOnDragging || isTouchToSeek) { + mBubbleView.postDelayed(new Runnable() { + @Override + public void run() { + mBubbleView.animate().alpha(0f).setDuration(mAnimDuration) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + hideBubble(); + + isThumbOnDragging = false; + invalidate(); + + if (mProgressListener != null) { + mProgressListener.getProgressOnFinally(getProgress()); + mProgressListener.getProgressOnFinally(getProgressInFloat()); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + hideBubble(); + + isThumbOnDragging = false; + invalidate(); + } + }).start(); + + } + }, !isThumbOnDragging && isTouchToSeek ? 300 : 0); if (mProgressListener != null) { mProgressListener.getProgressOnActionUp(getProgress()); @@ -494,7 +527,7 @@ public void onAnimationCancel(Animator animation) { break; } - return isThumbOnDragging || super.onTouchEvent(event); + return isThumbOnDragging || isTouchToSeek || super.onTouchEvent(event); } /** @@ -502,11 +535,19 @@ public void onAnimationCancel(Animator animation) { */ private boolean isThumbTouched(MotionEvent event) { float x = mTrackLength / mDelta * (mProgress - mMin) + mLeft; - float y = getHeight() / 2f; + float y = getMeasuredHeight() / 2f; return (event.getX() - x) * (event.getX() - x) + (event.getY() - y) * (event.getY() - y) <= (mLeft + dp2px(8)) * (mLeft + dp2px(8)); } + /** + * 识别track是否被有效点击 + */ + private boolean isTrackTouched(MotionEvent event) { + return event.getX() >= getPaddingLeft() && event.getX() <= getMeasuredWidth() - getPaddingRight() + && event.getY() >= getPaddingTop() && event.getY() <= getPaddingTop() + mThumbRadiusOnDragging * 2; + } + /** * 显示气泡 * 原理是利用WindowManager动态添加一个与Toast相同类型的BubbleView,消失时再移除 @@ -543,6 +584,8 @@ public void onAnimationStart(Animator animation) { mWindowManager.addView(mBubbleView, mLayoutParams); } }).start(); + mBubbleView.setProgressText(isShowProgressInFloat ? + String.valueOf(getProgressInFloat()) : String.valueOf(getProgress())); } /** @@ -610,6 +653,7 @@ public void onAnimationEnd(Animator animation) { mProgress = (mThumbCenterX - mLeft) * mDelta / mTrackLength + mMin; isThumbOnDragging = false; + isTouchToSeekAnimEnd = true; invalidate(); if (mProgressListener != null) { @@ -624,6 +668,7 @@ public void onAnimationCancel(Animator animation) { mProgress = (mThumbCenterX - mLeft) * mDelta / mTrackLength + mMin; isThumbOnDragging = false; + isTouchToSeekAnimEnd = true; invalidate(); } }); @@ -689,34 +734,24 @@ public int getMin() { return mMin; } - public void setMin(int min) { - if (mMin == min || min > mMax) { - return; - } - - mMin = min; - mDelta = mMax - mMin; - - calculateRadiusOfBubble(); - - postInvalidate(); - } - public int getMax() { return mMax; } - public void setMax(int max) { - if (mMax == max || max < mMin) { - return; - } + public void setProgressRange(int min, int max) { + setProgressRange(min, max, getProgressInFloat()); + } - mMax = max; + public void setProgressRange(int min, int max, float progress) { + mMin = Math.min(min, max); + mMax = Math.max(min, max); mDelta = mMax - mMin; - - if (mProgress > mMax) { + if (progress > mMax) { mProgress = mMax; } + if (progress < mMin) { + mProgress = mMin; + } calculateRadiusOfBubble(); postInvalidate(); @@ -983,6 +1018,14 @@ public void setShowProgressInFloat(boolean showProgressInFloat) { } } + public boolean isTouchToSeek() { + return isTouchToSeek; + } + + public void setTouchToSeek(boolean touchToSeek) { + isTouchToSeek = touchToSeek; + } + @ColorInt public int getBubbleColor() { return mBubbleColor; @@ -1144,8 +1187,8 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(3 * mBubbleRadius, 3 * mBubbleRadius); - mBubbleRectF.set(getWidth() / 2f - mBubbleRadius, 0, - getWidth() / 2f + mBubbleRadius, 2 * mBubbleRadius); + mBubbleRectF.set(getMeasuredWidth() / 2f - mBubbleRadius, 0, + getMeasuredWidth() / 2f + mBubbleRadius, 2 * mBubbleRadius); } @Override @@ -1153,10 +1196,10 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); mBubblePath.reset(); - float x0 = getWidth() / 2f; - float y0 = getHeight() - mBubbleRadius / 3f; + float x0 = getMeasuredWidth() / 2f; + float y0 = getMeasuredHeight() - mBubbleRadius / 3f; mBubblePath.moveTo(x0, y0); - float x1 = (float) (getWidth() / 2f - Math.sqrt(3) / 2f * mBubbleRadius); + float x1 = (float) (getMeasuredWidth() / 2f - Math.sqrt(3) / 2f * mBubbleRadius); float y1 = 3 / 2f * mBubbleRadius; mBubblePath.quadTo( x1 - dp2px(2), y1 - dp2px(2), @@ -1164,7 +1207,7 @@ protected void onDraw(Canvas canvas) { ); mBubblePath.arcTo(mBubbleRectF, 150, 240); - float x2 = (float) (getWidth() / 2f + Math.sqrt(3) / 2f * mBubbleRadius); + float x2 = (float) (getMeasuredWidth() / 2f + Math.sqrt(3) / 2f * mBubbleRadius); mBubblePath.quadTo( x2 + dp2px(2), y1 - dp2px(2), x0, y0 @@ -1179,7 +1222,7 @@ protected void onDraw(Canvas canvas) { mBubblePaint.getTextBounds(mProgressText, 0, mProgressText.length(), mRect); Paint.FontMetrics fm = mBubblePaint.getFontMetrics(); float baseline = mBubbleRadius + (fm.descent - fm.ascent) / 2f - fm.descent; - canvas.drawText(mProgressText, getWidth() / 2f, baseline, mBubblePaint); + canvas.drawText(mProgressText, getMeasuredWidth() / 2f, baseline, mBubblePaint); } void setProgressText(String progressText) { diff --git a/bubbleseekbar/src/main/res/values/attr.xml b/bubbleseekbar/src/main/res/values/attr.xml index 006d722..f589210 100644 --- a/bubbleseekbar/src/main/res/values/attr.xml +++ b/bubbleseekbar/src/main/res/values/attr.xml @@ -29,5 +29,6 @@ + \ No newline at end of file