Commit 59834452 by 霍志良

feat:rangeSeekbar引入

parent d78fbf45
...@@ -107,7 +107,6 @@ class FilterPopupWindow( ...@@ -107,7 +107,6 @@ class FilterPopupWindow(
view.btnReset.setOnClickListener { view.btnReset.setOnClickListener {
reset() reset()
} }
view.rangeSliderPrice.labelBehavior = LabelFormatter.LABEL_WITHIN_BOUNDS
view.btnConfirm.setOnClickListener { view.btnConfirm.setOnClickListener {
if (tempFilter.priceRanges?.key1 == "avg_price" && filterData.priceRanges.isNotEmpty()) { if (tempFilter.priceRanges?.key1 == "avg_price" && filterData.priceRanges.isNotEmpty()) {
tempFilter.priceRanges?.key1 = filterData.priceRanges[0].key1 tempFilter.priceRanges?.key1 = filterData.priceRanges[0].key1
......
package com.yidianling.consultant.ui.view.rangeseekbar;
/**
* ================================================
* 作 者:JayGoo
* 版 本:
* 创建日期:2018/5/8
* 描 述:
* ================================================
*/
public interface OnRangeChangedListener {
void onRangeChanged(RangeSeekBar view, float leftValue, float rightValue, boolean isFromUser);
void onStartTrackingTouch(RangeSeekBar view, boolean isLeft);
void onStopTrackingTouch(RangeSeekBar view, boolean isLeft);
}
package com.yidianling.consultant.ui.view.rangeseekbar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import com.yidianling.consultant.R;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
import static com.yidianling.consultant.ui.view.rangeseekbar.SeekBar.INDICATOR_ALWAYS_HIDE;
import static com.yidianling.consultant.ui.view.rangeseekbar.SeekBar.INDICATOR_ALWAYS_SHOW;
public class RangeSeekBar extends View {
//normal seekBar mode
public final static int SEEKBAR_MODE_SINGLE = 1;
//RangeSeekBar
public final static int SEEKBAR_MODE_RANGE = 2;
//number according to the actual proportion of the number of arranged;
public final static int TRICK_MARK_MODE_NUMBER = 0;
//other equally arranged
public final static int TRICK_MARK_MODE_OTHER = 1;
//tick mark text gravity
public final static int TICK_MARK_GRAVITY_LEFT = 0;
public final static int TICK_MARK_GRAVITY_CENTER = 1;
public final static int TICK_MARK_GRAVITY_RIGHT = 2;
private final static int MIN_INTERCEPT_DISTANCE = 100;
float touchDownX, touchDownY;
//剩余最小间隔的进度
float reservePercent;
boolean isScaleThumb = false;
Paint paint = new Paint();
RectF progressDefaultDstRect = new RectF();
RectF progressDstRect = new RectF();
Rect progressSrcRect = new Rect();
RectF stepDivRect = new RectF();
Rect tickMarkTextRect = new Rect();
SeekBar leftSB;
SeekBar rightSB;
SeekBar currTouchSB;
Bitmap progressBitmap;
Bitmap progressDefaultBitmap;
List<Bitmap> stepsBitmaps = new ArrayList<>();
private int progressTop, progressBottom, progressLeft, progressRight;
private int seekBarMode;
//刻度模式:number根据数字实际比例排列;other 均分排列
private int tickMarkMode;
//刻度与进度条间的间距
//The spacing between the tick mark and the progress bar
private int tickMarkTextMargin;
//刻度文字与提示文字的大小
//tick mark text and prompt text size
private int tickMarkTextSize;
private int tickMarkGravity;
private int tickMarkLayoutGravity;
private int tickMarkTextColor;
private int tickMarkInRangeTextColor;
//刻度上显示的文字
//The texts displayed on the scale
private CharSequence[] tickMarkTextArray;
//进度条圆角
//radius of progress bar
private float progressRadius;
//进度中进度条的颜色
//the color of seekBar in progress
private int progressColor;
//默认进度条颜色
//the default color of the progress bar
private int progressDefaultColor;
//the drawable of seekBar in progress
private int progressDrawableId;
//the default Drawable of the progress bar
private int progressDefaultDrawableId;
//the progress height
private int progressHeight;
// the progress width
private int progressWidth;
//the range interval of RangeSeekBar
private float minInterval;
private int gravity;
//****************** the above is attr value ******************//
//enable RangeSeekBar two thumb Overlap
private boolean enableThumbOverlap;
//the color of step divs
private int stepsColor;
//the width of each step
private float stepsWidth;
//the height of each step
private float stepsHeight;
//the radius of step divs
private float stepsRadius;
//steps is 0 will disable StepSeekBar
private int steps;
//the thumb will automatic bonding close to its value
private boolean stepsAutoBonding;
private int stepsDrawableId;
//True values set by the user
private float minProgress, maxProgress;
private boolean isEnable = true;
private int progressPaddingRight;
private OnRangeChangedListener callback;
public RangeSeekBar(Context context) {
this(context, null);
}
public RangeSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
initAttrs(attrs);
initPaint();
initSeekBar(attrs);
initStepsBitmap();
}
private void initProgressBitmap() {
if (progressBitmap == null) {
progressBitmap = Utils.drawableToBitmap(getContext(), progressWidth, progressHeight, progressDrawableId);
}
if (progressDefaultBitmap == null) {
progressDefaultBitmap = Utils.drawableToBitmap(getContext(), progressWidth, progressHeight, progressDefaultDrawableId);
}
}
private boolean verifyStepsMode() {
if (steps < 1 || stepsHeight <= 0 || stepsWidth <= 0) return false;
return true;
}
private void initStepsBitmap() {
if (!verifyStepsMode() || stepsDrawableId == 0) return;
if (stepsBitmaps.isEmpty()) {
Bitmap bitmap = Utils.drawableToBitmap(getContext(), (int) stepsWidth, (int) stepsHeight, stepsDrawableId);
for (int i = 0; i <= steps; i++) {
stepsBitmaps.add(bitmap);
}
}
}
private void initSeekBar(AttributeSet attrs) {
leftSB = new SeekBar(this, attrs, true);
rightSB = new SeekBar(this, attrs, false);
rightSB.setVisible(seekBarMode != SEEKBAR_MODE_SINGLE);
}
private void initAttrs(AttributeSet attrs) {
try {
TypedArray t = getContext().obtainStyledAttributes(attrs, R.styleable.RangeSeekBar);
seekBarMode = t.getInt(R.styleable.RangeSeekBar_rsb_mode, SEEKBAR_MODE_RANGE);
minProgress = t.getFloat(R.styleable.RangeSeekBar_rsb_min, 0);
maxProgress = t.getFloat(R.styleable.RangeSeekBar_rsb_max, 100);
minInterval = t.getFloat(R.styleable.RangeSeekBar_rsb_min_interval, 0);
gravity = t.getInt(R.styleable.RangeSeekBar_rsb_gravity, Gravity.TOP);
progressColor = t.getColor(R.styleable.RangeSeekBar_rsb_progress_color, 0xFF4BD962);
progressRadius = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_progress_radius, -1);
progressDefaultColor = t.getColor(R.styleable.RangeSeekBar_rsb_progress_default_color, 0xFFD7D7D7);
progressDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_progress_drawable, 0);
progressDefaultDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_progress_drawable_default, 0);
progressHeight = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_progress_height, Utils.dp2px(getContext(), 2));
tickMarkMode = t.getInt(R.styleable.RangeSeekBar_rsb_tick_mark_mode, TRICK_MARK_MODE_NUMBER);
tickMarkGravity = t.getInt(R.styleable.RangeSeekBar_rsb_tick_mark_gravity, TICK_MARK_GRAVITY_CENTER);
tickMarkLayoutGravity = t.getInt(R.styleable.RangeSeekBar_rsb_tick_mark_layout_gravity, Gravity.TOP);
tickMarkTextArray = t.getTextArray(R.styleable.RangeSeekBar_rsb_tick_mark_text_array);
tickMarkTextMargin = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_tick_mark_text_margin, Utils.dp2px(getContext(), 7));
tickMarkTextSize = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_tick_mark_text_size, Utils.dp2px(getContext(), 12));
tickMarkTextColor = t.getColor(R.styleable.RangeSeekBar_rsb_tick_mark_text_color, progressDefaultColor);
tickMarkInRangeTextColor = t.getColor(R.styleable.RangeSeekBar_rsb_tick_mark_in_range_text_color, progressColor);
steps = t.getInt(R.styleable.RangeSeekBar_rsb_steps, 0);
stepsColor = t.getColor(R.styleable.RangeSeekBar_rsb_step_color, 0xFF9d9d9d);
stepsRadius = t.getDimension(R.styleable.RangeSeekBar_rsb_step_radius, 0);
stepsWidth = t.getDimension(R.styleable.RangeSeekBar_rsb_step_width, 0);
stepsHeight = t.getDimension(R.styleable.RangeSeekBar_rsb_step_height, 0);
stepsDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_step_drawable, 0);
stepsAutoBonding = t.getBoolean(R.styleable.RangeSeekBar_rsb_step_auto_bonding, true);
t.recycle();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* measure progress bar position
*/
protected void onMeasureProgress(int w, int h) {
int viewHeight = h - getPaddingBottom() - getPaddingTop();
if (h <= 0) return;
if (gravity == Gravity.TOP) {
//calculate the height of indicator and thumb exceeds the part of the progress
float maxIndicatorHeight = 0;
if (leftSB.getIndicatorShowMode() != INDICATOR_ALWAYS_HIDE
|| rightSB.getIndicatorShowMode() != INDICATOR_ALWAYS_HIDE) {
maxIndicatorHeight = Math.max(leftSB.getIndicatorRawHeight(), rightSB.getIndicatorRawHeight());
}
float thumbHeight = Math.max(leftSB.getThumbScaleHeight(), rightSB.getThumbScaleHeight());
thumbHeight -= progressHeight / 2f;
//default height is indicator + thumb exceeds the part of the progress bar
//if tickMark height is greater than (indicator + thumb exceeds the part of the progress)
progressTop = (int) (maxIndicatorHeight + (thumbHeight - progressHeight) / 2f);
if (tickMarkTextArray != null && tickMarkLayoutGravity == Gravity.TOP) {
progressTop = (int) Math.max(getTickMarkRawHeight(), maxIndicatorHeight + (thumbHeight - progressHeight) / 2f);
}
progressBottom = progressTop + progressHeight;
} else if (gravity == Gravity.BOTTOM) {
if (tickMarkTextArray != null && tickMarkLayoutGravity == Gravity.BOTTOM) {
progressBottom = viewHeight - getTickMarkRawHeight();
} else {
progressBottom = (int) (viewHeight - Math.max(leftSB.getThumbScaleHeight(), rightSB.getThumbScaleHeight()) / 2f
+ progressHeight / 2f);
}
progressTop = progressBottom - progressHeight;
} else {
progressTop = (viewHeight - progressHeight) / 2;
progressBottom = progressTop + progressHeight;
}
int maxThumbWidth = (int) Math.max(leftSB.getThumbScaleWidth(), rightSB.getThumbScaleWidth());
progressLeft = maxThumbWidth / 2 + getPaddingLeft();
progressRight = w - maxThumbWidth / 2 - getPaddingRight();
progressWidth = progressRight - progressLeft;
progressDefaultDstRect.set(getProgressLeft(), getProgressTop(), getProgressRight(), getProgressBottom());
progressPaddingRight = w - progressRight;
//default value
if (progressRadius <= 0) {
progressRadius = (int) ((getProgressBottom() - getProgressTop()) * 0.45f);
}
initProgressBitmap();
}
//Android 7.0以后,优化了View的绘制,onMeasure和onSizeChanged调用顺序有所变化
//Android7.0以下:onMeasure--->onSizeChanged--->onMeasure
//Android7.0以上:onMeasure--->onSizeChanged
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
/*
* onMeasure传入的widthMeasureSpec和heightMeasureSpec不是一般的尺寸数值,而是将模式和尺寸组合在一起的数值
* MeasureSpec.EXACTLY 是精确尺寸
* MeasureSpec.AT_MOST 是最大尺寸
* MeasureSpec.UNSPECIFIED 是未指定尺寸
*/
if (heightMode == MeasureSpec.EXACTLY) {
heightSize = MeasureSpec.makeMeasureSpec(heightSize, MeasureSpec.EXACTLY);
} else if (heightMode == MeasureSpec.AT_MOST && getParent() instanceof ViewGroup
&& heightSize == ViewGroup.LayoutParams.MATCH_PARENT) {
heightSize = MeasureSpec.makeMeasureSpec(((ViewGroup) getParent()).getMeasuredHeight(), MeasureSpec.AT_MOST);
} else {
int heightNeeded;
if (gravity == Gravity.CENTER) {
if (tickMarkTextArray != null && tickMarkLayoutGravity == Gravity.BOTTOM) {
heightNeeded = (int) (2 * (getRawHeight() - getTickMarkRawHeight()));
} else {
heightNeeded = (int) (2 * (getRawHeight() - Math.max(leftSB.getThumbScaleHeight(), rightSB.getThumbScaleHeight()) / 2));
}
} else {
heightNeeded = (int) getRawHeight();
}
heightSize = MeasureSpec.makeMeasureSpec(heightNeeded, MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightSize);
}
protected int getTickMarkRawHeight() {
if (tickMarkTextArray != null && tickMarkTextArray.length > 0) {
return tickMarkTextMargin + Utils.measureText(String.valueOf(tickMarkTextArray[0]), tickMarkTextSize).height() + 3;
}
return 0;
}
protected float getRawHeight() {
float rawHeight;
if (seekBarMode == SEEKBAR_MODE_SINGLE) {
rawHeight = leftSB.getRawHeight();
if (tickMarkLayoutGravity == Gravity.BOTTOM && tickMarkTextArray != null) {
float h = Math.max((leftSB.getThumbScaleHeight() - progressHeight) / 2, getTickMarkRawHeight());
rawHeight = rawHeight - leftSB.getThumbScaleHeight() / 2 + progressHeight / 2f + h;
}
} else {
rawHeight = Math.max(leftSB.getRawHeight(), rightSB.getRawHeight());
if (tickMarkLayoutGravity == Gravity.BOTTOM && tickMarkTextArray != null) {
float thumbHeight = Math.max(leftSB.getThumbScaleHeight(), rightSB.getThumbScaleHeight());
float h = Math.max((thumbHeight - progressHeight) / 2, getTickMarkRawHeight());
rawHeight = rawHeight - thumbHeight / 2 + progressHeight / 2f + h;
}
}
return rawHeight;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
onMeasureProgress(w, h);
//set default value
setRange(minProgress, maxProgress, minInterval);
// initializes the positions of the two thumbs
int lineCenterY = (getProgressBottom() + getProgressTop()) / 2;
leftSB.onSizeChanged(getProgressLeft(), lineCenterY);
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.onSizeChanged(getProgressLeft(), lineCenterY);
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
onDrawTickMark(canvas, paint);
onDrawProgressBar(canvas, paint, true);
onDrawSteps(canvas, paint);
onDrawSeekBar(canvas);
}
//绘制刻度,并且根据当前位置是否在刻度范围内设置不同的颜色显示
// Draw the scales, and according to the current position is set within
// the scale range of different color display
protected void onDrawTickMark(Canvas canvas, Paint paint) {
if (tickMarkTextArray != null) {
int trickPartWidth = progressWidth / (tickMarkTextArray.length - 1);
for (int i = 0; i < tickMarkTextArray.length; i++) {
final String text2Draw = tickMarkTextArray[i].toString();
if (TextUtils.isEmpty(text2Draw)) continue;
paint.getTextBounds(text2Draw, 0, text2Draw.length(), tickMarkTextRect);
paint.setColor(tickMarkTextColor);
//平分显示
float x;
if (tickMarkMode == TRICK_MARK_MODE_OTHER) {
Log.e("aaaa", "tickMarkGravity" + tickMarkGravity);
if (tickMarkGravity == TICK_MARK_GRAVITY_RIGHT) {
x = getProgressLeft() + i * trickPartWidth - tickMarkTextRect.width();
} else if (tickMarkGravity == TICK_MARK_GRAVITY_CENTER) {
if (i == tickMarkTextArray.length - 1) {
x = getProgressLeft() + i * trickPartWidth - tickMarkTextRect.width() / 2f - 20;
} else {
x = getProgressLeft() + i * trickPartWidth - tickMarkTextRect.width() / 2f;
}
} else {
x = getProgressLeft() + i * trickPartWidth;
}
} else {
float num = Utils.parseFloat(text2Draw);
SeekBarState[] states = getRangeSeekBarState();
if (Utils.compareFloat(num, states[0].value) != -1 && Utils.compareFloat(num, states[1].value) != 1 && (seekBarMode == SEEKBAR_MODE_RANGE)) {
paint.setColor(tickMarkInRangeTextColor);
}
//按实际比例显示
x = getProgressLeft() + progressWidth * (num - minProgress) / (maxProgress - minProgress)
- tickMarkTextRect.width() / 2f;
}
float y;
if (tickMarkLayoutGravity == Gravity.TOP) {
y = getProgressTop() - tickMarkTextMargin;
} else {
y = getProgressBottom() + tickMarkTextMargin + tickMarkTextRect.height() - 10;
}
canvas.drawText(text2Draw, x, y, paint);
}
}
}
//绘制进度条 另外在收尾两端绘制2个实心点
// draw the progress bar
protected void onDrawProgressBar(Canvas canvas, Paint paint, Boolean isNeedStartEnd) {
//draw default progress
if (Utils.verifyBitmap(progressDefaultBitmap)) {
canvas.drawBitmap(progressDefaultBitmap, null, progressDefaultDstRect, paint);
} else {
paint.setColor(progressDefaultColor);
canvas.drawRoundRect(progressDefaultDstRect, progressRadius, progressRadius, paint);
}
//draw progress
if (seekBarMode == SEEKBAR_MODE_RANGE) {
progressDstRect.top = getProgressTop();
progressDstRect.left = leftSB.left + leftSB.getThumbScaleWidth() / 2f + progressWidth * leftSB.currPercent;
progressDstRect.right = rightSB.left + rightSB.getThumbScaleWidth() / 2f + progressWidth * rightSB.currPercent;
progressDstRect.bottom = getProgressBottom();
} else {
progressDstRect.top = getProgressTop();
progressDstRect.left = leftSB.left + leftSB.getThumbScaleWidth() / 2f;
progressDstRect.right = leftSB.left + leftSB.getThumbScaleWidth() / 2f + progressWidth * leftSB.currPercent;
progressDstRect.bottom = getProgressBottom();
}
if (Utils.verifyBitmap(progressBitmap)) {
progressSrcRect.top = 0;
progressSrcRect.bottom = progressBitmap.getHeight();
int bitmapWidth = progressBitmap.getWidth();
if (seekBarMode == SEEKBAR_MODE_RANGE) {
progressSrcRect.left = (int) (bitmapWidth * leftSB.currPercent);
progressSrcRect.right = (int) (bitmapWidth * rightSB.currPercent);
} else {
progressSrcRect.left = 0;
progressSrcRect.right = (int) (bitmapWidth * leftSB.currPercent);
}
canvas.drawBitmap(progressBitmap, progressSrcRect, progressDstRect, null);
} else {
paint.setColor(progressColor);
canvas.drawRoundRect(progressDstRect, progressRadius, progressRadius, paint);
}
//如果需要绘制start和end的实心圆
if (isNeedStartEnd) {
paint.setColor(progressDefaultColor);
//画左边固定实心圆形
canvas.drawCircle(leftSB.left + leftSB.getThumbScaleWidth() / 2f, leftSB.top +
leftSB.getThumbScaleWidth() / 2f, 10f, paint);
//画右边固定实心圆
canvas.drawCircle(progressWidth + rightSB.getThumbScaleWidth() / 2f, rightSB.top + rightSB
.getThumbScaleWidth() / 2f, 10f, paint);
}
}
//draw steps
protected void onDrawSteps(Canvas canvas, Paint paint) {
if (!verifyStepsMode()) return;
int stepMarks = getProgressWidth() / (steps);
float extHeight = (stepsHeight - getProgressHeight()) / 2f;
for (int k = 1; k <= steps - 1; k++) {
float x = getProgressLeft() + k * stepMarks - stepsWidth / 2f;
stepDivRect.set(x, getProgressTop() + stepsHeight, x + stepsWidth, getProgressBottom() + 3 * stepsHeight);
if (stepsBitmaps.isEmpty() || stepsBitmaps.size() <= k) {
paint.setColor(stepsColor);
canvas.drawRoundRect(stepDivRect, stepsRadius, stepsRadius, paint);
} else {
canvas.drawBitmap(stepsBitmaps.get(k), null, stepDivRect, paint);
}
}
}
//绘制SeekBar相关
protected void onDrawSeekBar(Canvas canvas) {
//draw left SeekBar
if (leftSB.getIndicatorShowMode() == INDICATOR_ALWAYS_SHOW) {
leftSB.setShowIndicatorEnable(true);
}
leftSB.draw(canvas);
//draw right SeekBar
if (seekBarMode == SEEKBAR_MODE_RANGE) {
if (rightSB.getIndicatorShowMode() == INDICATOR_ALWAYS_SHOW) {
rightSB.setShowIndicatorEnable(true);
}
rightSB.draw(canvas);
}
}
//初始化画笔
private void initPaint() {
paint.setStyle(Paint.Style.FILL);
paint.setColor(progressDefaultColor);
paint.setTextSize(tickMarkTextSize);
}
private void changeThumbActivateState(boolean hasActivate) {
if (hasActivate && currTouchSB != null) {
boolean state = currTouchSB == leftSB;
leftSB.setActivate(state);
if (seekBarMode == SEEKBAR_MODE_RANGE)
rightSB.setActivate(!state);
} else {
leftSB.setActivate(false);
if (seekBarMode == SEEKBAR_MODE_RANGE)
rightSB.setActivate(false);
}
}
protected float getEventX(MotionEvent event) {
return event.getX();
}
protected float getEventY(MotionEvent event) {
return event.getY();
}
/**
* scale the touch seekBar thumb
*/
private void scaleCurrentSeekBarThumb() {
if (currTouchSB != null && currTouchSB.getThumbScaleRatio() > 1f && !isScaleThumb) {
isScaleThumb = true;
currTouchSB.scaleThumb();
}
}
/**
* reset the touch seekBar thumb
*/
private void resetCurrentSeekBarThumb() {
if (currTouchSB != null && currTouchSB.getThumbScaleRatio() > 1f && isScaleThumb) {
isScaleThumb = false;
currTouchSB.resetThumb();
}
}
//calculate currTouchSB percent by MotionEvent
protected float calculateCurrentSeekBarPercent(float touchDownX) {
if (currTouchSB == null) return 0;
float percent = (touchDownX - getProgressLeft()) * 1f / (progressWidth);
if (touchDownX < getProgressLeft()) {
percent = 0;
} else if (touchDownX > getProgressRight()) {
percent = 1;
}
//RangeMode minimum interval
if (seekBarMode == SEEKBAR_MODE_RANGE) {
if (currTouchSB == leftSB) {
if (percent > rightSB.currPercent - reservePercent) {
percent = rightSB.currPercent - reservePercent;
}
} else if (currTouchSB == rightSB) {
if (percent < leftSB.currPercent + reservePercent) {
percent = leftSB.currPercent + reservePercent;
}
}
}
return percent;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!isEnable) return true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDownX = getEventX(event);
touchDownY = getEventY(event);
if (seekBarMode == SEEKBAR_MODE_RANGE) {
if (rightSB.currPercent >= 1 && leftSB.collide(getEventX(event), getEventY(event))) {
currTouchSB = leftSB;
scaleCurrentSeekBarThumb();
} else if (rightSB.collide(getEventX(event), getEventY(event))) {
currTouchSB = rightSB;
scaleCurrentSeekBarThumb();
} else {
float performClick = (touchDownX - getProgressLeft()) * 1f / (progressWidth);
float distanceLeft = Math.abs(leftSB.currPercent - performClick);
float distanceRight = Math.abs(rightSB.currPercent - performClick);
if (distanceLeft < distanceRight) {
currTouchSB = leftSB;
} else {
currTouchSB = rightSB;
}
performClick = calculateCurrentSeekBarPercent(touchDownX);
currTouchSB.slide(performClick);
}
} else {
currTouchSB = leftSB;
scaleCurrentSeekBarThumb();
}
//Intercept parent TouchEvent
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (callback != null) {
callback.onStartTrackingTouch(this, currTouchSB == leftSB);
}
changeThumbActivateState(true);
return true;
case MotionEvent.ACTION_MOVE:
float x = getEventX(event);
if ((seekBarMode == SEEKBAR_MODE_RANGE) && leftSB.currPercent == rightSB.currPercent) {
currTouchSB.materialRestore();
if (callback != null) {
callback.onStopTrackingTouch(this, currTouchSB == leftSB);
}
if (x - touchDownX > 0) {
//method to move right
if (currTouchSB != rightSB) {
currTouchSB.setShowIndicatorEnable(false);
resetCurrentSeekBarThumb();
currTouchSB = rightSB;
}
} else {
//method to move left
if (currTouchSB != leftSB) {
currTouchSB.setShowIndicatorEnable(false);
resetCurrentSeekBarThumb();
currTouchSB = leftSB;
}
}
if (callback != null) {
callback.onStartTrackingTouch(this, currTouchSB == leftSB);
}
}
scaleCurrentSeekBarThumb();
currTouchSB.material = currTouchSB.material >= 1 ? 1 : currTouchSB.material + 0.1f;
touchDownX = x;
currTouchSB.slide(calculateCurrentSeekBarPercent(touchDownX));
currTouchSB.setShowIndicatorEnable(true);
if (callback != null) {
SeekBarState[] states = getRangeSeekBarState();
callback.onRangeChanged(this, states[0].value, states[1].value, true);
}
invalidate();
//Intercept parent TouchEvent
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
changeThumbActivateState(true);
break;
case MotionEvent.ACTION_CANCEL:
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.setShowIndicatorEnable(false);
}
if (currTouchSB == leftSB) {
resetCurrentSeekBarThumb();
} else if (currTouchSB == rightSB) {
resetCurrentSeekBarThumb();
}
leftSB.setShowIndicatorEnable(false);
if (callback != null) {
SeekBarState[] states = getRangeSeekBarState();
callback.onRangeChanged(this, states[0].value, states[1].value, false);
}
//Intercept parent TouchEvent
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
changeThumbActivateState(false);
break;
case MotionEvent.ACTION_UP:
if (verifyStepsMode() && stepsAutoBonding) {
float percent = calculateCurrentSeekBarPercent(getEventX(event));
float stepPercent = 1.0f / steps;
int stepSelected = new BigDecimal(percent / stepPercent).setScale(0, RoundingMode.HALF_UP).intValue();
currTouchSB.slide(stepSelected * stepPercent);
}
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.setShowIndicatorEnable(false);
}
leftSB.setShowIndicatorEnable(false);
currTouchSB.materialRestore();
resetCurrentSeekBarThumb();
if (callback != null) {
SeekBarState[] states = getRangeSeekBarState();
callback.onRangeChanged(this, states[0].value, states[1].value, false);
}
//Intercept parent TouchEvent
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (callback != null) {
callback.onStopTrackingTouch(this, currTouchSB == leftSB);
}
changeThumbActivateState(false);
break;
}
return super.onTouchEvent(event);
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
ss.minValue = minProgress;
ss.maxValue = maxProgress;
ss.rangeInterval = minInterval;
SeekBarState[] results = getRangeSeekBarState();
ss.currSelectedMin = results[0].value;
ss.currSelectedMax = results[1].value;
return ss;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
try {
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
float min = ss.minValue;
float max = ss.maxValue;
float rangeInterval = ss.rangeInterval;
setRange(min, max, rangeInterval);
float currSelectedMin = ss.currSelectedMin;
float currSelectedMax = ss.currSelectedMax;
setProgress(currSelectedMin, currSelectedMax);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setOnRangeChangedListener(OnRangeChangedListener listener) {
callback = listener;
}
public void setProgress(float value) {
setProgress(value, maxProgress);
}
public void setProgress(float leftValue, float rightValue) {
leftValue = Math.min(leftValue, rightValue);
rightValue = Math.max(leftValue, rightValue);
if (rightValue - leftValue < minInterval) {
if (leftValue - minProgress > maxProgress - rightValue) {
leftValue = rightValue - minInterval;
} else {
rightValue = leftValue + minInterval;
}
}
if (leftValue < minProgress) {
throw new IllegalArgumentException("setProgress() min < (preset min - offsetValue) . #min:" + leftValue + " #preset min:" + rightValue);
}
if (rightValue > maxProgress) {
throw new IllegalArgumentException("setProgress() max > (preset max - offsetValue) . #max:" + rightValue + " #preset max:" + rightValue);
}
float range = maxProgress - minProgress;
leftSB.currPercent = Math.abs(leftValue - minProgress) / range;
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.currPercent = Math.abs(rightValue - minProgress) / range;
}
if (callback != null) {
callback.onRangeChanged(this, leftValue, rightValue, false);
}
invalidate();
}
/**
* 设置范围
*
* @param min 最小值
* @param max 最大值
*/
public void setRange(float min, float max) {
setRange(min, max, minInterval);
}
/**
* 设置范围
*
* @param min 最小值
* @param max 最大值
* @param minInterval 最小间隔
*/
public void setRange(float min, float max, float minInterval) {
if (max <= min) {
throw new IllegalArgumentException("setRange() max must be greater than min ! #max:" + max + " #min:" + min);
}
if (minInterval < 0) {
throw new IllegalArgumentException("setRange() interval must be greater than zero ! #minInterval:" + minInterval);
}
if (minInterval >= max - min) {
throw new IllegalArgumentException("setRange() interval must be less than (max - min) ! #minInterval:" + minInterval + " #max - min:" + (max - min));
}
maxProgress = max;
minProgress = min;
this.minInterval = minInterval;
reservePercent = minInterval / (max - min);
//set default value
if (seekBarMode == SEEKBAR_MODE_RANGE) {
if (leftSB.currPercent + reservePercent <= 1 && leftSB.currPercent + reservePercent > rightSB.currPercent) {
rightSB.currPercent = leftSB.currPercent + reservePercent;
} else if (rightSB.currPercent - reservePercent >= 0 && rightSB.currPercent - reservePercent < leftSB.currPercent) {
leftSB.currPercent = rightSB.currPercent - reservePercent;
}
}
invalidate();
}
/**
* @return the two seekBar state , see {@link }
*/
public SeekBarState[] getRangeSeekBarState() {
SeekBarState leftSeekBarState = new SeekBarState();
leftSeekBarState.value = leftSB.getProgress();
leftSeekBarState.indicatorText = String.valueOf(leftSeekBarState.value);
if (Utils.compareFloat(leftSeekBarState.value, minProgress) == 0) {
leftSeekBarState.isMin = true;
} else if (Utils.compareFloat(leftSeekBarState.value, maxProgress) == 0) {
leftSeekBarState.isMax = true;
}
SeekBarState rightSeekBarState = new SeekBarState();
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSeekBarState.value = rightSB.getProgress();
rightSeekBarState.indicatorText = String.valueOf(rightSeekBarState.value);
if (Utils.compareFloat(rightSB.currPercent, minProgress) == 0) {
rightSeekBarState.isMin = true;
} else if (Utils.compareFloat(rightSB.currPercent, maxProgress) == 0) {
rightSeekBarState.isMax = true;
}
}
return new SeekBarState[]{leftSeekBarState, rightSeekBarState};
}
//******************* Attributes getter and setter *******************//
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
this.isEnable = enabled;
}
public void setIndicatorText(String progress) {
leftSB.setIndicatorText(progress);
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.setIndicatorText(progress);
}
}
/**
* format number indicator text
*
* @param formatPattern format rules
*/
public void setIndicatorTextDecimalFormat(String formatPattern) {
leftSB.setIndicatorTextDecimalFormat(formatPattern);
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.setIndicatorTextDecimalFormat(formatPattern);
}
}
/**
* format string indicator text
*
* @param formatPattern format rules
*/
public void setIndicatorTextStringFormat(String formatPattern) {
leftSB.setIndicatorTextStringFormat(formatPattern);
if (seekBarMode == SEEKBAR_MODE_RANGE) {
rightSB.setIndicatorTextStringFormat(formatPattern);
}
}
/**
* if is single mode, please use it to get the SeekBar
*
* @return left seek bar
*/
public SeekBar getLeftSeekBar() {
return leftSB;
}
public SeekBar getRightSeekBar() {
return rightSB;
}
public int getProgressTop() {
return progressTop;
}
public void setProgressTop(int progressTop) {
this.progressTop = progressTop;
}
public int getProgressBottom() {
return progressBottom;
}
public void setProgressBottom(int progressBottom) {
this.progressBottom = progressBottom;
}
public int getProgressLeft() {
return progressLeft;
}
public void setProgressLeft(int progressLeft) {
this.progressLeft = progressLeft;
}
public int getProgressRight() {
return progressRight;
}
public void setProgressRight(int progressRight) {
this.progressRight = progressRight;
}
public int getProgressPaddingRight() {
return progressPaddingRight;
}
public int getProgressHeight() {
return progressHeight;
}
public void setProgressHeight(int progressHeight) {
this.progressHeight = progressHeight;
}
public float getMinProgress() {
return minProgress;
}
public float getMaxProgress() {
return maxProgress;
}
public void setProgressColor(@ColorInt int progressDefaultColor, @ColorInt int progressColor) {
this.progressDefaultColor = progressDefaultColor;
this.progressColor = progressColor;
}
public int getTickMarkTextColor() {
return tickMarkTextColor;
}
public void setTickMarkTextColor(@ColorInt int tickMarkTextColor) {
this.tickMarkTextColor = tickMarkTextColor;
}
public int getTickMarkInRangeTextColor() {
return tickMarkInRangeTextColor;
}
public void setTickMarkInRangeTextColor(@ColorInt int tickMarkInRangeTextColor) {
this.tickMarkInRangeTextColor = tickMarkInRangeTextColor;
}
public int getSeekBarMode() {
return seekBarMode;
}
/**
* {@link #SEEKBAR_MODE_SINGLE} is single SeekBar
* {@link #SEEKBAR_MODE_RANGE} is range SeekBar
*
* @param seekBarMode
*/
public void setSeekBarMode(@SeekBarModeDef int seekBarMode) {
this.seekBarMode = seekBarMode;
rightSB.setVisible(seekBarMode != SEEKBAR_MODE_SINGLE);
}
public int getTickMarkMode() {
return tickMarkMode;
}
/**
* {@link #TICK_MARK_GRAVITY_LEFT} is number tick mark, it will locate the position according to the value.
* {@link #TICK_MARK_GRAVITY_RIGHT} is text tick mark, it will be equally positioned.
*
* @param tickMarkMode
*/
public void setTickMarkMode(@TickMarkModeDef int tickMarkMode) {
this.tickMarkMode = tickMarkMode;
}
public int getTickMarkTextMargin() {
return tickMarkTextMargin;
}
public void setTickMarkTextMargin(int tickMarkTextMargin) {
this.tickMarkTextMargin = tickMarkTextMargin;
}
public int getTickMarkTextSize() {
return tickMarkTextSize;
}
public void setTickMarkTextSize(int tickMarkTextSize) {
this.tickMarkTextSize = tickMarkTextSize;
}
public int getTickMarkGravity() {
return tickMarkGravity;
}
/**
* the tick mark text gravity
* {@link #TICK_MARK_GRAVITY_LEFT}
* {@link #TICK_MARK_GRAVITY_RIGHT}
* {@link #TICK_MARK_GRAVITY_CENTER}
*
* @param tickMarkGravity
*/
public void setTickMarkGravity(@TickMarkGravityDef int tickMarkGravity) {
this.tickMarkGravity = tickMarkGravity;
}
public CharSequence[] getTickMarkTextArray() {
return tickMarkTextArray;
}
public void setTickMarkTextArray(CharSequence[] tickMarkTextArray) {
this.tickMarkTextArray = tickMarkTextArray;
}
public float getMinInterval() {
return minInterval;
}
public float getProgressRadius() {
return progressRadius;
}
public void setProgressRadius(float progressRadius) {
this.progressRadius = progressRadius;
}
public int getProgressColor() {
return progressColor;
}
public void setProgressColor(@ColorInt int progressColor) {
this.progressColor = progressColor;
}
public int getProgressDefaultColor() {
return progressDefaultColor;
}
public void setProgressDefaultColor(@ColorInt int progressDefaultColor) {
this.progressDefaultColor = progressDefaultColor;
}
public int getProgressDrawableId() {
return progressDrawableId;
}
public void setProgressDrawableId(@DrawableRes int progressDrawableId) {
this.progressDrawableId = progressDrawableId;
progressBitmap = null;
initProgressBitmap();
}
public int getProgressDefaultDrawableId() {
return progressDefaultDrawableId;
}
public void setProgressDefaultDrawableId(@DrawableRes int progressDefaultDrawableId) {
this.progressDefaultDrawableId = progressDefaultDrawableId;
progressDefaultBitmap = null;
initProgressBitmap();
}
public int getProgressWidth() {
return progressWidth;
}
public void setProgressWidth(int progressWidth) {
this.progressWidth = progressWidth;
}
public void setTypeface(Typeface typeFace) {
paint.setTypeface(typeFace);
}
public boolean isEnableThumbOverlap() {
return enableThumbOverlap;
}
public void setEnableThumbOverlap(boolean enableThumbOverlap) {
this.enableThumbOverlap = enableThumbOverlap;
}
public int getSteps() {
return steps;
}
public void setSteps(int steps) {
this.steps = steps;
}
public int getStepsColor() {
return stepsColor;
}
public void setStepsColor(@ColorInt int stepsColor) {
this.stepsColor = stepsColor;
}
public float getStepsWidth() {
return stepsWidth;
}
public void setStepsWidth(float stepsWidth) {
this.stepsWidth = stepsWidth;
}
public float getStepsHeight() {
return stepsHeight;
}
public void setStepsHeight(float stepsHeight) {
this.stepsHeight = stepsHeight;
}
public float getStepsRadius() {
return stepsRadius;
}
public void setStepsRadius(float stepsRadius) {
this.stepsRadius = stepsRadius;
}
public int getTickMarkLayoutGravity() {
return tickMarkLayoutGravity;
}
/**
* the tick mark layout gravity
* Gravity.TOP and Gravity.BOTTOM
*
* @param tickMarkLayoutGravity
*/
public void setTickMarkLayoutGravity(@TickMarkLayoutGravityDef int tickMarkLayoutGravity) {
this.tickMarkLayoutGravity = tickMarkLayoutGravity;
}
public int getGravity() {
return gravity;
}
/**
* the RangeSeekBar gravity
* Gravity.TOP and Gravity.BOTTOM
*
* @param gravity
*/
public void setGravity(@GravityDef int gravity) {
this.gravity = gravity;
}
public boolean isStepsAutoBonding() {
return stepsAutoBonding;
}
public void setStepsAutoBonding(boolean stepsAutoBonding) {
this.stepsAutoBonding = stepsAutoBonding;
}
public int getStepsDrawableId() {
return stepsDrawableId;
}
public void setStepsDrawableId(@DrawableRes int stepsDrawableId) {
this.stepsBitmaps.clear();
this.stepsDrawableId = stepsDrawableId;
initStepsBitmap();
}
public List<Bitmap> getStepsBitmaps() {
return stepsBitmaps;
}
public void setStepsBitmaps(List<Bitmap> stepsBitmaps) {
if (stepsBitmaps == null || stepsBitmaps.isEmpty() || stepsBitmaps.size() <= steps) {
throw new IllegalArgumentException("stepsBitmaps must > steps !");
}
this.stepsBitmaps.clear();
this.stepsBitmaps.addAll(stepsBitmaps);
}
public void setStepsDrawable(List<Integer> stepsDrawableIds) {
if (stepsDrawableIds == null || stepsDrawableIds.isEmpty() || stepsDrawableIds.size() <= steps) {
throw new IllegalArgumentException("stepsDrawableIds must > steps !");
}
if (!verifyStepsMode()) {
throw new IllegalArgumentException("stepsWidth must > 0, stepsHeight must > 0,steps must > 0 First!!");
}
List<Bitmap> stepsBitmaps = new ArrayList<>();
for (int i = 0; i < stepsDrawableIds.size(); i++) {
stepsBitmaps.add(Utils.drawableToBitmap(getContext(), (int) stepsWidth, (int) stepsHeight, stepsDrawableIds.get(i)));
}
setStepsBitmaps(stepsBitmaps);
}
/**
* @hide
*/
@IntDef({SEEKBAR_MODE_SINGLE, SEEKBAR_MODE_RANGE})
@Retention(RetentionPolicy.SOURCE)
public @interface SeekBarModeDef {
}
/**
* @hide
*/
@IntDef({TRICK_MARK_MODE_NUMBER, TRICK_MARK_MODE_OTHER})
@Retention(RetentionPolicy.SOURCE)
public @interface TickMarkModeDef {
}
/**
* @hide
*/
@IntDef({TICK_MARK_GRAVITY_LEFT, TICK_MARK_GRAVITY_CENTER, TICK_MARK_GRAVITY_RIGHT})
@Retention(RetentionPolicy.SOURCE)
public @interface TickMarkGravityDef {
}
/**
* @hide
*/
@IntDef({Gravity.TOP, Gravity.BOTTOM})
@Retention(RetentionPolicy.SOURCE)
public @interface TickMarkLayoutGravityDef {
}
/**
* @hide
*/
@IntDef({Gravity.TOP, Gravity.CENTER, Gravity.BOTTOM})
@Retention(RetentionPolicy.SOURCE)
public @interface GravityDef {
}
public static class Gravity {
public final static int TOP = 0;
public final static int BOTTOM = 1;
public final static int CENTER = 2;
}
}
package com.yidianling.consultant.ui.view.rangeseekbar;
import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;
/**
* ================================================
* 作 者:JayGoo
* 版 本:
* 创建日期:2018/5/8
* 描 述:
* ================================================
*/
public class SavedState extends View.BaseSavedState {
public static final Creator<SavedState> CREATOR = new Creator<SavedState>() {
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
public float minValue;
public float maxValue;
public float rangeInterval;
public int tickNumber;
public float currSelectedMin;
public float currSelectedMax;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
minValue = in.readFloat();
maxValue = in.readFloat();
rangeInterval = in.readFloat();
tickNumber = in.readInt();
currSelectedMin = in.readFloat();
currSelectedMax = in.readFloat();
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeFloat(minValue);
out.writeFloat(maxValue);
out.writeFloat(rangeInterval);
out.writeInt(tickNumber);
out.writeFloat(currSelectedMin);
out.writeFloat(currSelectedMax);
}
}
package com.yidianling.consultant.ui.view.rangeseekbar;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.os.Build;
import android.text.TextUtils;
import android.util.AttributeSet;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IntDef;
import androidx.core.content.ContextCompat;
import com.yidianling.consultant.R;
import java.text.DecimalFormat;
/**
* ================================================
* 作 者:JayGoo
* 版 本:
* 创建日期:2018/5/8
* 描 述:
* ================================================
*/
public class SeekBar {
//the indicator show mode
public static final int INDICATOR_SHOW_WHEN_TOUCH = 0;
public static final int INDICATOR_ALWAYS_HIDE = 1;
public static final int INDICATOR_ALWAYS_SHOW_AFTER_TOUCH = 2;
public static final int INDICATOR_ALWAYS_SHOW = 3;
public static final int WRAP_CONTENT = -1;
public static final int MATCH_PARENT = -2;
//when you touch or move, the thumb will scale, default not scale
float thumbScaleRatio;
int left, right, top, bottom;
float currPercent;
float material = 0;
boolean isLeft;
Bitmap thumbBitmap;
Bitmap thumbInactivatedBitmap;
Bitmap indicatorBitmap;
ValueAnimator anim;
String userText2Draw;
boolean isActivate = false;
boolean isVisible = true;
RangeSeekBar rangeSeekBar;
String indicatorTextStringFormat;
Path indicatorArrowPath = new Path();
Rect indicatorTextRect = new Rect();
Rect indicatorRect = new Rect();
//****************** the above is attr value ******************//
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
DecimalFormat indicatorTextDecimalFormat;
int scaleThumbWidth;
int scaleThumbHeight;
private int indicatorShowMode;
//进度提示背景的高度,宽度如果是0的话会自适应调整
//Progress prompted the background height, width,
private int indicatorHeight;
private int indicatorWidth;
//进度提示背景与按钮之间的距离
//The progress indicates the distance between the background and the button
private int indicatorMargin;
private int indicatorDrawableId;
private int indicatorArrowSize;
private int indicatorTextSize;
private int indicatorTextColor;
private float indicatorRadius;
private int indicatorBackgroundColor;
private int indicatorPaddingLeft, indicatorPaddingRight, indicatorPaddingTop, indicatorPaddingBottom;
private int thumbDrawableId;
private int thumbInactivatedDrawableId;
private int thumbWidth;
private int thumbHeight;
private boolean isShowIndicator;
public SeekBar(RangeSeekBar rangeSeekBar, AttributeSet attrs, boolean isLeft) {
this.rangeSeekBar = rangeSeekBar;
this.isLeft = isLeft;
initAttrs(attrs);
initBitmap();
initVariables();
}
private void initAttrs(AttributeSet attrs) {
TypedArray t = getContext().obtainStyledAttributes(attrs, R.styleable.RangeSeekBar);
if (t == null) return;
indicatorMargin = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_margin, 0);
indicatorDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_indicator_drawable, 0);
indicatorShowMode = t.getInt(R.styleable.RangeSeekBar_rsb_indicator_show_mode, INDICATOR_ALWAYS_HIDE);
indicatorHeight = t.getLayoutDimension(R.styleable.RangeSeekBar_rsb_indicator_height, WRAP_CONTENT);
indicatorWidth = t.getLayoutDimension(R.styleable.RangeSeekBar_rsb_indicator_width, WRAP_CONTENT);
indicatorTextSize = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_text_size, Utils.dp2px(getContext(), 14));
indicatorTextColor = t.getColor(R.styleable.RangeSeekBar_rsb_indicator_text_color, Color.WHITE);
indicatorBackgroundColor = t.getColor(R.styleable.RangeSeekBar_rsb_indicator_background_color, ContextCompat.getColor(getContext(), R.color.colorAccent));
indicatorPaddingLeft = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_padding_left, 0);
indicatorPaddingRight = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_padding_right, 0);
indicatorPaddingTop = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_padding_top, 0);
indicatorPaddingBottom = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_padding_bottom, 0);
indicatorArrowSize = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_arrow_size, 0);
thumbDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_thumb_drawable, R.drawable.rsb_default_thumb);
thumbInactivatedDrawableId = t.getResourceId(R.styleable.RangeSeekBar_rsb_thumb_inactivated_drawable, 0);
thumbWidth = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_thumb_width, Utils.dp2px(getContext(), 26));
thumbHeight = (int) t.getDimension(R.styleable.RangeSeekBar_rsb_thumb_height, Utils.dp2px(getContext(), 26));
thumbScaleRatio = t.getFloat(R.styleable.RangeSeekBar_rsb_thumb_scale_ratio, 1f);
indicatorRadius = t.getDimension(R.styleable.RangeSeekBar_rsb_indicator_radius, 0f);
t.recycle();
}
protected void initVariables() {
scaleThumbWidth = thumbWidth;
scaleThumbHeight = thumbHeight;
if (indicatorHeight == WRAP_CONTENT) {
indicatorHeight = Utils.measureText("8", indicatorTextSize).height() + indicatorPaddingTop + indicatorPaddingBottom;
}
if (indicatorArrowSize <= 0) {
indicatorArrowSize = (int) (thumbWidth / 4);
}
}
public Context getContext() {
return rangeSeekBar.getContext();
}
public Resources getResources() {
if (getContext() != null) return getContext().getResources();
return null;
}
/**
* 初始化进度提示的背景
*/
private void initBitmap() {
setIndicatorDrawableId(indicatorDrawableId);
setThumbDrawableId(thumbDrawableId, thumbWidth, thumbHeight);
setThumbInactivatedDrawableId(thumbInactivatedDrawableId, thumbWidth, thumbHeight);
}
/**
* 计算每个按钮的位置和尺寸
* Calculates the position and size of each button
*
* @param x position x
* @param y position y
*/
protected void onSizeChanged(int x, int y) {
initVariables();
initBitmap();
left = (int) (x - getThumbScaleWidth() / 2);
right = (int) (x + getThumbScaleWidth() / 2);
top = y - getThumbHeight() / 2;
bottom = y + getThumbHeight() / 2;
}
public void scaleThumb() {
scaleThumbWidth = (int) getThumbScaleWidth();
scaleThumbHeight = (int) getThumbScaleHeight();
int y = rangeSeekBar.getProgressBottom();
top = y - scaleThumbHeight / 2;
bottom = y + scaleThumbHeight / 2;
setThumbDrawableId(thumbDrawableId, scaleThumbWidth, scaleThumbHeight);
}
public void resetThumb() {
scaleThumbWidth = getThumbWidth();
scaleThumbHeight = getThumbHeight();
int y = rangeSeekBar.getProgressBottom();
top = y - scaleThumbHeight / 2;
bottom = y + scaleThumbHeight / 2;
setThumbDrawableId(thumbDrawableId, scaleThumbWidth, scaleThumbHeight);
}
public float getRawHeight() {
return getIndicatorHeight() + getIndicatorArrowSize() + getIndicatorMargin() + getThumbScaleHeight();
}
/**
* 绘制按钮和提示背景和文字
* Draw buttons and tips for background and text
*
* @param canvas Canvas
*/
protected void draw(Canvas canvas) {
if (!isVisible) {
return;
}
int offset = (int) (rangeSeekBar.getProgressWidth() * currPercent);
canvas.save();
canvas.translate(offset, 0);
// translate canvas, then don't care left
canvas.translate(left, 0);
if (isShowIndicator) {
onDrawIndicator(canvas, paint, formatCurrentIndicatorText(userText2Draw));
}
onDrawThumb(canvas);
canvas.restore();
}
/**
* 绘制按钮
* 如果没有图片资源,则绘制默认按钮
* <p>
* draw the thumb button
* If there is no image resource, draw the default button
*
* @param canvas canvas
*/
protected void onDrawThumb(Canvas canvas) {
if (thumbInactivatedBitmap != null && !isActivate) {
canvas.drawBitmap(thumbInactivatedBitmap, 0, rangeSeekBar.getProgressTop() + (rangeSeekBar.getProgressHeight() - scaleThumbHeight) / 2f, null);
} else if (thumbBitmap != null) {
canvas.drawBitmap(thumbBitmap, 0, rangeSeekBar.getProgressTop() + (rangeSeekBar.getProgressHeight() - scaleThumbHeight) / 2f, null);
}
}
/**
* 格式化提示文字
* format the indicator text
*
* @param text2Draw
* @return
*/
protected String formatCurrentIndicatorText(String text2Draw) {
SeekBarState[] states = rangeSeekBar.getRangeSeekBarState();
if (TextUtils.isEmpty(text2Draw)) {
if (isLeft) {
if (indicatorTextDecimalFormat != null) {
text2Draw = indicatorTextDecimalFormat.format(states[0].value);
} else {
text2Draw = states[0].indicatorText;
}
} else {
if (indicatorTextDecimalFormat != null) {
text2Draw = indicatorTextDecimalFormat.format(states[1].value);
} else {
text2Draw = states[1].indicatorText;
}
}
}
if (indicatorTextStringFormat != null) {
text2Draw = String.format(indicatorTextStringFormat, text2Draw);
}
return text2Draw;
}
/**
* This method will draw the indicator background dynamically according to the text.
* you can use to set padding
*
* @param canvas Canvas
* @param text2Draw Indicator text
*/
protected void onDrawIndicator(Canvas canvas, Paint paint, String text2Draw) {
if (text2Draw == null) return;
paint.setTextSize(indicatorTextSize);
paint.setStyle(Paint.Style.FILL);
paint.setColor(indicatorBackgroundColor);
paint.getTextBounds(text2Draw, 0, text2Draw.length(), indicatorTextRect);
int realIndicatorWidth = indicatorTextRect.width() + indicatorPaddingLeft + indicatorPaddingRight;
if (indicatorWidth > realIndicatorWidth) {
realIndicatorWidth = indicatorWidth;
}
int realIndicatorHeight = indicatorTextRect.height() + indicatorPaddingTop + indicatorPaddingBottom;
if (indicatorHeight > realIndicatorHeight) {
realIndicatorHeight = indicatorHeight;
}
indicatorRect.left = (int) (scaleThumbWidth / 2f - realIndicatorWidth / 2f);
indicatorRect.top = bottom - realIndicatorHeight - scaleThumbHeight - indicatorMargin;
indicatorRect.right = indicatorRect.left + realIndicatorWidth;
indicatorRect.bottom = indicatorRect.top + realIndicatorHeight;
//draw default indicator arrow
if (indicatorBitmap == null) {
//arrow three point
// b c
// a
int ax = scaleThumbWidth / 2;
int ay = indicatorRect.bottom;
int bx = ax - indicatorArrowSize;
int by = ay - indicatorArrowSize;
int cx = ax + indicatorArrowSize;
indicatorArrowPath.reset();
indicatorArrowPath.moveTo(ax, ay);
indicatorArrowPath.lineTo(bx, by);
indicatorArrowPath.lineTo(cx, by);
indicatorArrowPath.close();
canvas.drawPath(indicatorArrowPath, paint);
indicatorRect.bottom -= indicatorArrowSize;
indicatorRect.top -= indicatorArrowSize;
}
//indicator background edge processing
int defaultPaddingOffset = Utils.dp2px(getContext(), 1);
int leftOffset = indicatorRect.width() / 2 - (int) (rangeSeekBar.getProgressWidth() * currPercent) - rangeSeekBar.getProgressLeft() + defaultPaddingOffset;
int rightOffset = indicatorRect.width() / 2 - (int) (rangeSeekBar.getProgressWidth() * (1 - currPercent)) - rangeSeekBar.getProgressPaddingRight() + defaultPaddingOffset;
if (leftOffset > 0) {
indicatorRect.left += leftOffset;
indicatorRect.right += leftOffset;
} else if (rightOffset > 0) {
indicatorRect.left -= rightOffset;
indicatorRect.right -= rightOffset;
}
//draw indicator background
if (indicatorBitmap != null) {
Utils.drawBitmap(canvas, paint, indicatorBitmap, indicatorRect);
} else if (indicatorRadius > 0f) {
canvas.drawRoundRect(new RectF(indicatorRect), indicatorRadius, indicatorRadius, paint);
} else {
canvas.drawRect(indicatorRect, paint);
}
//draw indicator content text
int tx, ty;
if (indicatorPaddingLeft > 0) {
tx = indicatorRect.left + indicatorPaddingLeft;
} else if (indicatorPaddingRight > 0) {
tx = indicatorRect.right - indicatorPaddingRight - indicatorTextRect.width();
} else {
tx = indicatorRect.left + (realIndicatorWidth - indicatorTextRect.width()) / 2;
}
if (indicatorPaddingTop > 0) {
ty = indicatorRect.top + indicatorTextRect.height() + indicatorPaddingTop;
} else if (indicatorPaddingBottom > 0) {
ty = indicatorRect.bottom - indicatorTextRect.height() - indicatorPaddingBottom;
} else {
ty = indicatorRect.bottom - (realIndicatorHeight - indicatorTextRect.height()) / 2 + 1;
}
//draw indicator text
paint.setColor(indicatorTextColor);
canvas.drawText(text2Draw, tx, ty, paint);
}
/**
* 拖动检测
*
* @return is collide
*/
protected boolean collide(float x, float y) {
int offset = (int) (rangeSeekBar.getProgressWidth() * currPercent);
return x > left + offset && x < right + offset && y > top && y < bottom;
}
protected void slide(float percent) {
if (percent < 0) percent = 0;
else if (percent > 1) percent = 1;
currPercent = percent;
}
protected void setShowIndicatorEnable(boolean isEnable) {
switch (indicatorShowMode) {
case INDICATOR_SHOW_WHEN_TOUCH:
isShowIndicator = isEnable;
break;
case INDICATOR_ALWAYS_SHOW:
case INDICATOR_ALWAYS_SHOW_AFTER_TOUCH:
isShowIndicator = true;
break;
case INDICATOR_ALWAYS_HIDE:
isShowIndicator = false;
break;
}
}
public void materialRestore() {
if (anim != null) anim.cancel();
anim = ValueAnimator.ofFloat(material, 0);
anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
material = (float) animation.getAnimatedValue();
if (rangeSeekBar != null) rangeSeekBar.invalidate();
}
});
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
material = 0;
if (rangeSeekBar != null) rangeSeekBar.invalidate();
}
});
anim.start();
}
public void setIndicatorText(String text) {
userText2Draw = text;
}
public DecimalFormat getIndicatorTextDecimalFormat() {
return indicatorTextDecimalFormat;
}
public void setIndicatorTextDecimalFormat(String formatPattern) {
indicatorTextDecimalFormat = new DecimalFormat(formatPattern);
}
public void setIndicatorTextStringFormat(String formatPattern) {
indicatorTextStringFormat = formatPattern;
}
public int getIndicatorDrawableId() {
return indicatorDrawableId;
}
public void setIndicatorDrawableId(@DrawableRes int indicatorDrawableId) {
if (indicatorDrawableId != 0) {
this.indicatorDrawableId = indicatorDrawableId;
indicatorBitmap = BitmapFactory.decodeResource(getResources(), indicatorDrawableId);
}
}
public int getIndicatorArrowSize() {
return indicatorArrowSize;
}
public void setIndicatorArrowSize(int indicatorArrowSize) {
this.indicatorArrowSize = indicatorArrowSize;
}
public int getIndicatorPaddingLeft() {
return indicatorPaddingLeft;
}
public void setIndicatorPaddingLeft(int indicatorPaddingLeft) {
this.indicatorPaddingLeft = indicatorPaddingLeft;
}
public int getIndicatorPaddingRight() {
return indicatorPaddingRight;
}
public void setIndicatorPaddingRight(int indicatorPaddingRight) {
this.indicatorPaddingRight = indicatorPaddingRight;
}
public int getIndicatorPaddingTop() {
return indicatorPaddingTop;
}
public void setIndicatorPaddingTop(int indicatorPaddingTop) {
this.indicatorPaddingTop = indicatorPaddingTop;
}
public int getIndicatorPaddingBottom() {
return indicatorPaddingBottom;
}
public void setIndicatorPaddingBottom(int indicatorPaddingBottom) {
this.indicatorPaddingBottom = indicatorPaddingBottom;
}
public int getIndicatorMargin() {
return indicatorMargin;
}
public void setIndicatorMargin(int indicatorMargin) {
this.indicatorMargin = indicatorMargin;
}
public int getIndicatorShowMode() {
return indicatorShowMode;
}
/**
* the indicator show mode
* {@link #INDICATOR_SHOW_WHEN_TOUCH}
* {@link #INDICATOR_ALWAYS_SHOW}
* {@link #INDICATOR_ALWAYS_SHOW_AFTER_TOUCH}
* {@link #INDICATOR_ALWAYS_SHOW}
*
* @param indicatorShowMode
*/
public void setIndicatorShowMode(@IndicatorModeDef int indicatorShowMode) {
this.indicatorShowMode = indicatorShowMode;
}
public void showIndicator(boolean isShown) {
isShowIndicator = isShown;
}
public boolean isShowIndicator() {
return isShowIndicator;
}
/**
* include indicator text Height、padding、margin
*
* @return The actual occupation height of indicator
*/
public int getIndicatorRawHeight() {
if (indicatorHeight > 0) {
if (indicatorBitmap != null) {
return indicatorHeight + indicatorMargin;
} else {
return indicatorHeight + indicatorArrowSize + indicatorMargin;
}
} else {
if (indicatorBitmap != null) {
return Utils.measureText("8", indicatorTextSize).height() + indicatorPaddingTop + indicatorPaddingBottom + indicatorMargin;
} else {
return Utils.measureText("8", indicatorTextSize).height() + indicatorPaddingTop + indicatorPaddingBottom + indicatorMargin + indicatorArrowSize;
}
}
}
public int getIndicatorHeight() {
return indicatorHeight;
}
public void setIndicatorHeight(int indicatorHeight) {
this.indicatorHeight = indicatorHeight;
}
public int getIndicatorWidth() {
return indicatorWidth;
}
public void setIndicatorWidth(int indicatorWidth) {
this.indicatorWidth = indicatorWidth;
}
public int getIndicatorTextSize() {
return indicatorTextSize;
}
public void setIndicatorTextSize(int indicatorTextSize) {
this.indicatorTextSize = indicatorTextSize;
}
public int getIndicatorTextColor() {
return indicatorTextColor;
}
public void setIndicatorTextColor(@ColorInt int indicatorTextColor) {
this.indicatorTextColor = indicatorTextColor;
}
public int getIndicatorBackgroundColor() {
return indicatorBackgroundColor;
}
public void setIndicatorBackgroundColor(@ColorInt int indicatorBackgroundColor) {
this.indicatorBackgroundColor = indicatorBackgroundColor;
}
public int getThumbInactivatedDrawableId() {
return thumbInactivatedDrawableId;
}
public void setThumbInactivatedDrawableId(@DrawableRes int thumbInactivatedDrawableId, int width, int height) {
if (thumbInactivatedDrawableId != 0 && getResources() != null) {
this.thumbInactivatedDrawableId = thumbInactivatedDrawableId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
thumbInactivatedBitmap = Utils.drawableToBitmap(width, height, getResources().getDrawable(thumbInactivatedDrawableId, null));
} else {
thumbInactivatedBitmap = Utils.drawableToBitmap(width, height, getResources().getDrawable(thumbInactivatedDrawableId));
}
}
}
public int getThumbDrawableId() {
return thumbDrawableId;
}
public void setThumbDrawableId(@DrawableRes int thumbDrawableId) {
if (thumbWidth <= 0 || thumbHeight <= 0) {
throw new IllegalArgumentException("please set thumbWidth and thumbHeight first!");
}
if (thumbDrawableId != 0 && getResources() != null) {
this.thumbDrawableId = thumbDrawableId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
thumbBitmap = Utils.drawableToBitmap(thumbWidth, thumbHeight, getResources().getDrawable(thumbDrawableId, null));
} else {
thumbBitmap = Utils.drawableToBitmap(thumbWidth, thumbHeight, getResources().getDrawable(thumbDrawableId));
}
}
}
public void setThumbDrawableId(@DrawableRes int thumbDrawableId, int width, int height) {
if (thumbDrawableId != 0 && getResources() != null && width > 0 && height > 0) {
this.thumbDrawableId = thumbDrawableId;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
thumbBitmap = Utils.drawableToBitmap(width, height, getResources().getDrawable(thumbDrawableId, null));
} else {
thumbBitmap = Utils.drawableToBitmap(width, height, getResources().getDrawable(thumbDrawableId));
}
}
}
public int getThumbWidth() {
return thumbWidth;
}
public void setThumbWidth(int thumbWidth) {
this.thumbWidth = thumbWidth;
}
public float getThumbScaleHeight() {
return thumbHeight * thumbScaleRatio;
}
public float getThumbScaleWidth() {
return thumbWidth * thumbScaleRatio;
}
public int getThumbHeight() {
return thumbHeight;
}
public void setThumbHeight(int thumbHeight) {
this.thumbHeight = thumbHeight;
}
public float getIndicatorRadius() {
return indicatorRadius;
}
public void setIndicatorRadius(float indicatorRadius) {
this.indicatorRadius = indicatorRadius;
}
protected boolean getActivate() {
return isActivate;
}
protected void setActivate(boolean activate) {
isActivate = activate;
}
public void setTypeface(Typeface typeFace) {
paint.setTypeface(typeFace);
}
/**
* when you touch or move, the thumb will scale, default not scale
*
* @return default 1.0f
*/
public float getThumbScaleRatio() {
return thumbScaleRatio;
}
public boolean isVisible() {
return isVisible;
}
/**
* if visble is false, will clear the Canvas
*
* @param visible
*/
public void setVisible(boolean visible) {
isVisible = visible;
}
public float getProgress() {
float range = rangeSeekBar.getMaxProgress() - rangeSeekBar.getMinProgress();
return rangeSeekBar.getMinProgress() + range * currPercent;
}
@IntDef({INDICATOR_SHOW_WHEN_TOUCH, INDICATOR_ALWAYS_HIDE, INDICATOR_ALWAYS_SHOW_AFTER_TOUCH, INDICATOR_ALWAYS_SHOW})
public @interface IndicatorModeDef {
}
}
package com.yidianling.consultant.ui.view.rangeseekbar;
/**
* ================================================
* 作 者:JayGoo
* 版 本:
* 创建日期:2018/5/9
* 描 述: it works for draw indicator text
* ================================================
*/
public class SeekBarState {
public String indicatorText;
public float value; //now progress value
public boolean isMin;
public boolean isMax;
@Override
public String toString() {
return "indicatorText: " + indicatorText + " ,isMin: " + isMin + " ,isMax: " + isMax;
}
}
package com.yidianling.consultant.ui.view.rangeseekbar;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.NinePatch;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.Log;
import androidx.annotation.ColorRes;
import androidx.core.content.ContextCompat;
/**
* ================================================
* 作 者:JayGoo
* 版 本:
* 创建日期:2018/5/8
* 描 述:
* ================================================
*/
public class Utils {
private static final String TAG = "RangeSeekBar";
public static void print(String log) {
Log.d(TAG, log);
}
public static void print(Object... logs) {
StringBuilder stringBuilder = new StringBuilder();
for (Object log : logs) {
stringBuilder.append(log);
}
Log.d(TAG, stringBuilder.toString());
}
public static Bitmap drawableToBitmap(Context context, int width, int height, int drawableId) {
if (context == null || width <= 0 || height <= 0 || drawableId == 0) return null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return Utils.drawableToBitmap(width, height, context.getResources().getDrawable(drawableId, null));
} else {
return Utils.drawableToBitmap(width, height, context.getResources().getDrawable(drawableId));
}
}
/**
* make a drawable to a bitmap
*
* @param drawable drawable you want convert
* @return converted bitmap
*/
public static Bitmap drawableToBitmap(int width, int height, Drawable drawable) {
Bitmap bitmap = null;
try {
if (drawable instanceof BitmapDrawable) {
BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
bitmap = bitmapDrawable.getBitmap();
if (bitmap != null && bitmap.getHeight() > 0) {
Matrix matrix = new Matrix();
float scaleWidth = width * 1.0f / bitmap.getWidth();
float scaleHeight = height * 1.0f / bitmap.getHeight();
matrix.postScale(scaleWidth, scaleHeight);
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
return bitmap;
}
}
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} catch (Exception e) {
e.printStackTrace();
}
return bitmap;
}
/**
* draw 9Path
*
* @param canvas Canvas
* @param bmp 9path bitmap
* @param rect 9path rect
*/
public static void drawNinePath(Canvas canvas, Bitmap bmp, Rect rect) {
NinePatch.isNinePatchChunk(bmp.getNinePatchChunk());
NinePatch patch = new NinePatch(bmp, bmp.getNinePatchChunk(), null);
patch.draw(canvas, rect);
}
public static void drawBitmap(Canvas canvas, Paint paint, Bitmap bmp, Rect rect) {
try {
if (NinePatch.isNinePatchChunk(bmp.getNinePatchChunk())) {
drawNinePath(canvas, bmp, rect);
return;
}
} catch (Exception e) {
}
canvas.drawBitmap(bmp, rect.left, rect.top, paint);
}
public static int dp2px(Context context, float dpValue) {
if (context == null || compareFloat(0f, dpValue) == 0) return 0;
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
/**
* Compare the size of two floating point numbers
*
* @param a
* @param b
* @return 1 is a > b
* -1 is a < b
* 0 is a == b
*/
public static int compareFloat(float a, float b) {
int ta = Math.round(a * 1000000);
int tb = Math.round(b * 1000000);
if (ta > tb) {
return 1;
} else if (ta < tb) {
return -1;
} else {
return 0;
}
}
/**
* Compare the size of two floating point numbers with accuracy
*
* @param a
* @param b
* @return 1 is a > b
* -1 is a < b
* 0 is a == b
*/
public static int compareFloat(float a, float b, int degree) {
if (Math.abs(a - b) < Math.pow(0.1, degree)) {
return 0;
} else {
if (a < b) {
return -1;
} else {
return 1;
}
}
}
public static float parseFloat(String s) {
try {
return Float.parseFloat(s);
} catch (NumberFormatException e) {
return 0f;
}
}
public static Rect measureText(String text, float textSize) {
Paint paint = new Paint();
Rect textRect = new Rect();
paint.setTextSize(textSize);
paint.getTextBounds(text, 0, text.length(), textRect);
paint.reset();
return textRect;
}
public static boolean verifyBitmap(Bitmap bitmap) {
if (bitmap == null || bitmap.isRecycled() || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
return false;
}
return true;
}
public static int getColor(Context context, @ColorRes int colorId) {
if (context != null) {
return ContextCompat.getColor(context.getApplicationContext(), colorId);
}
return Color.WHITE;
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/platform_gray7" />
<stroke
android:width="1dp"
android:color="@color/platform_gray7" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@android:color/white" />
<stroke
android:width="2dp"
android:color="@color/platform_main_theme" />
</shape>
...@@ -185,15 +185,27 @@ ...@@ -185,15 +185,27 @@
</ScrollView> </ScrollView>
<com.google.android.material.slider.RangeSlider <com.yidianling.consultant.ui.view.rangeseekbar.RangeSeekBar
android:id="@+id/rangeSliderPrice" android:id="@+id/sb_steps_6"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:stepSize="50" android:layout_width="wrap_content"
android:theme="@style/Theme.MaterialComponents.DayNight.NoActionBar" app:rsb_gravity="bottom"
android:valueFrom="50" app:rsb_mode="range"
android:valueTo="500" app:rsb_progress_color="@color/platform_main_theme"
app:values="@array/initial_slider_values"></com.google.android.material.slider.RangeSlider> app:rsb_step_auto_bonding="false"
app:rsb_step_color="@color/platform_gray7"
app:rsb_step_height="5dp"
app:rsb_step_width="1dp"
app:rsb_steps="4"
app:rsb_thumb_drawable="@drawable/thumb_yellow_stroke"
app:rsb_thumb_height="15dp"
app:rsb_thumb_width="15dp"
app:rsb_tick_mark_layout_gravity="bottom"
app:rsb_tick_mark_mode="other"
app:rsb_tick_mark_text_array="@array/wordsArray"
app:rsb_tick_mark_text_margin="20dp">
</com.yidianling.consultant.ui.view.rangeseekbar.RangeSeekBar>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
......
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="wordsArray">
<item>0</item>
<item>100</item>
<item>200</item>
<item>300</item>
<item>不限</item>
</string-array>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RangeSeekBar">
<!--RangeSeekBar common attrs-->
<!--The maximum-->
<attr name="rsb_max" format="float" />
<!--The minimum-->
<attr name="rsb_min" format="float" />
<!--RangeSeekBar mode, single is normal seekBar, range is RangeSeekBar-->
<attr name="rsb_mode" format="enum">
<enum name="single" value="1" />
<enum name="range" value="2" />
</attr>
<!--RangeSeekBar gravity-->
<attr name="rsb_gravity" format="enum">
<enum name="top" value="0" />
<enum name="bottom" value="1" />
<enum name="center" value="2" />
</attr>
<!--The min interval of the thumbs -->
<attr name="rsb_min_interval" format="float" />
<!-- 0 for the normal mode, greater than 1 to switch to scale mode-->
<attr name="rsb_tick_mark_number" format="integer" />
<!--Scale mode
Number according to the scale of the actual proportion of the distribution of the location (markTextArray must be a number)
Other bisects the current layout (markTextArray can be any character)
-->
<attr name="rsb_tick_mark_mode" format="enum">
<enum name="number" value="0" />
<enum name="other" value="1" />
</attr>
<!--The tick mark text gravity -->
<attr name="rsb_tick_mark_gravity" format="enum">
<enum name="left" value="0" />
<enum name="center" value="1" />
<enum name="right" value="2" />
</attr>
<!--The tick mark text layout gravity -->
<attr name="rsb_tick_mark_layout_gravity" format="enum">
<enum name="top" value="0" />
<enum name="bottom" value="1" />
</attr>
<!--The tick mark text array -->
<attr name="rsb_tick_mark_text_array" format="reference" />
<!--The tick mark text margin bottom to progress -->
<attr name="rsb_tick_mark_text_margin" format="dimension" />
<attr name="rsb_tick_mark_text_size" format="dimension" />
<attr name="rsb_tick_mark_text_color" format="color" />
<!--it just work in range && number mode now-->
<attr name="rsb_tick_mark_in_range_text_color" format="color" />
<attr name="rsb_progress_height" format="dimension" />
<attr name="rsb_progress_radius" format="dimension" />
<!--the color of progress bar when in progress-->
<attr name="rsb_progress_color" format="color" />
<!--the default color of the progress bar-->
<attr name="rsb_progress_default_color" format="color" />
<attr name="rsb_progress_drawable" format="reference" />
<attr name="rsb_progress_drawable_default" format="reference" />
<!--SeekBar attrs-->
<attr name="rsb_indicator_show_mode" format="enum">
<enum name="showWhenTouch" value="0" />
<enum name="alwaysHide" value="1" />
<enum name="alwaysShowAfterTouch" value="2" />
<enum name="alwaysShow" value="3" />
</attr>
<attr name="rsb_indicator_height" format="dimension">
<enum name="wrap_content" value="-1" />
</attr>
<attr name="rsb_indicator_width" format="dimension">
<enum name="wrap_content" value="-1" />
</attr>
<!--indicator margin bottom to progress bar-->
<attr name="rsb_indicator_margin" format="dimension" />
<attr name="rsb_indicator_text_size" format="dimension" />
<attr name="rsb_indicator_text_color" format="color" />
<!--indicator arrow size, it just work when you not use rsb_indicator_drawable -->
<attr name="rsb_indicator_arrow_size" format="dimension" />
<!-- must use 9 path !!!-->
<attr name="rsb_indicator_drawable" format="reference" />
<attr name="rsb_indicator_background_color" format="color" />
<attr name="rsb_indicator_padding_left" format="dimension" />
<attr name="rsb_indicator_padding_right" format="dimension" />
<attr name="rsb_indicator_padding_top" format="dimension" />
<attr name="rsb_indicator_padding_bottom" format="dimension" />
<attr name="rsb_indicator_radius" format="dimension" />
<attr name="rsb_thumb_drawable" format="reference" />
<!--the thumb inactivated is when you don't touch the thumb button-->
<attr name="rsb_thumb_inactivated_drawable" format="reference" />
<attr name="rsb_thumb_width" format="dimension" />
<attr name="rsb_thumb_height" format="dimension" />
<attr name="rsb_thumb_scale_ratio" format="float" />
<!--steps SeekBar-->
<attr name="rsb_steps" format="integer" />
<attr name="rsb_step_color" format="color" />
<attr name="rsb_step_width" format="dimension" />
<attr name="rsb_step_height" format="dimension" />
<attr name="rsb_step_radius" format="dimension" />
<attr name="rsb_step_auto_bonding" format="boolean" />
<attr name="rsb_step_drawable" format="reference" />
</declare-styleable>
<declare-styleable name="VerticalRangeSeekBar">
<!--the vertical RangeSeekBar draw orientation-->
<attr name="rsb_orientation" format="enum">
<enum name="left" value="1" />
<enum name="right" value="2" />
</attr>
<attr name="rsb_tick_mark_orientation" format="enum">
<enum name="vertical" value="1" />
<enum name="horizontal" value="2" />
</attr>
<attr name="rsb_indicator_text_orientation" format="enum">
<enum name="vertical" value="1" />
<enum name="horizontal" value="2" />
</attr>
</declare-styleable>
</resources>
...@@ -45,9 +45,4 @@ ...@@ -45,9 +45,4 @@
<item name="android:windowNoTitle">true</item> <item name="android:windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item> <item name="android:windowIsTranslucent">true</item>
</style> </style>
<array name="initial_slider_values">
<item>100</item>
<item>200</item>
</array>
</resources> </resources>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment