preface
SeekBar, which comes with Android, feels cumbersome to use, requires a separate XML file to tweak colors and the like, and the content feels wordy. As we needed SeekBar for our whiteboard Demo development, we implemented a SeekBar that met the basic requirements of specifying various color attributes in an XML layout and dynamically setting colors in code. It works better.
SeekBar code please see Github address: whiteboard Demo, Demo address please: click here to use, very convenient; P (remember to copy all the attributes in res/values/styles.xml)
The effect
implementation
Since no animation is involved, this custom View is fairly simple to make. First define the attributes you want in res/values/styles. XML. I’ll paste the attributes I define here:
<declare-styleable name="SeekBarWidget">
<attr name="seek_maxProgress" format="integer" /><! -- Maximum progress -->
<attr name="seek_minProgress" format="integer" /><! -- Minimum progress -->
<attr name="seek_progress" format="integer" /><! -- Current progress(default) -->
<attr name="seek_circleRadius" format="dimension" /><! -- seekBar Radius of middle circle -->
<attr name="seek_circleStrokeWidth" format="dimension" /><! -- seekBar border -->
<attr name="seek_lineHeight" format="dimension" /><! -- The height of the progress bar -->
<attr name="seek_backgroundColor" format="color" /><! -- Progress bar background color -->
<attr name="seek_circleStrokeColor" format="color" /><! -- border color -->
<attr name="seek_maxColor" format="color" /><! -- Foreground color of progress bar -->
<attr name="seek_startColor" format="color" /><! -- Set this property if gradient is needed, otherwise leave it empty -->
</declare-styleable>
Copy the code
Once the property is defined, parse it in the View’s construct:
public SeekBarWidget(Context context) {
this(context, null);
}
public SeekBarWidget(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public SeekBarWidget(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (null! = attrs) { TypedArray typedArray = getResources().obtainAttributes(attrs, R.styleable.SeekBarWidget); minProgress = typedArray.getInt(R.styleable.SeekBarWidget_seek_minProgress,0);
maxProgress = typedArray.getInt(R.styleable.SeekBarWidget_seek_maxProgress, 100) - minProgress;
progress = typedArray.getInt(R.styleable.SeekBarWidget_seek_progress, 0) - minProgress;
if (progress < 0) progress = minProgress;
circleRadius = typedArray.getDimension(R.styleable.SeekBarWidget_seek_circleRadius, 20f);
circleStrokeWidth = typedArray.getDimension(R.styleable.SeekBarWidget_seek_circleStrokeWidth, 5f);
lineHeight = typedArray.getDimension(R.styleable.SeekBarWidget_seek_lineHeight, 5f);
backgroundColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_backgroundColor, Color.parseColor("#F0F0F0"));
circleStrokeColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_circleStrokeColor, Color.WHITE);
int maxColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_maxColor, Color.RED);
int startColor = typedArray.getColor(R.styleable.SeekBarWidget_seek_startColor, maxColor);
colorTransition = new ColorTransition(startColor, maxColor);
typedArray.recycle();
percentage = progress * 1.0 f / maxProgress;
horizontalPadding = circleRadius * 2 + circleStrokeWidth * 2 + getPaddingStart() + getPaddingEnd();
mPaddingLeft = horizontalPadding - getPaddingEnd() - circleRadius - circleStrokeWidth;
return;
}
maxProgress = 100;
minProgress = 0;
circleRadius = 20;
circleStrokeWidth = 5;
lineHeight = 5;
backgroundColor = Color.parseColor("#F0F0F0");
colorTransition = new ColorTransition(Color.WHITE, Color.RED);
circleStrokeColor = Color.WHITE;
horizontalPadding = circleRadius * 2 + circleStrokeWidth * 2 + getPaddingStart() + getPaddingEnd();
mPaddingLeft = horizontalPadding - getPaddingEnd() - circleRadius - circleStrokeWidth;
}
Copy the code
OnMeasure and onDraw are next. Since the width and height of my application scene have been fixed, I didn’t write onMeasure. If you need it, you can do it by yourself.
- Start by drawing a line with a background color, the height of which is externally specified. (Set StrokeCap to ROUND)
- Draw a new line for the foreground color, the height of which is also specified externally. Here I am reading the same property
lineHeight
, you can also set different heights according to different requirements; p - Next draw the seekBar circle, where border is also used directly
drawCircle
To do this, draw the border first. (circleRadius + circleStrokeWidth / 2f) - Finally draw the middle circle
onDraw:
@Override
protected void onDraw(Canvas canvas) {
final int width = (int) (getMeasuredWidth() - horizontalPadding);
final int height = getMeasuredHeight();
paint.setColor(backgroundColor);
paint.setStrokeWidth(lineHeight);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawLine(mPaddingLeft, height >> 1, mPaddingLeft + width, height >> 1, paint);
//float percentage = progress / maxProgress;
int currColor = colorTransition.getValue(percentage);
paint.setColor(currColor);
canvas.drawLine(mPaddingLeft, height >> 1, mPaddingLeft + width * percentage, height >> 1, paint);
// draw circle border
paint.setColor(circleStrokeColor);
canvas.drawCircle(mPaddingLeft + width * percentage, height >> 1, circleRadius + (circleStrokeWidth / 2f), paint);
// draw circle inside color
paint.setColor(currColor);
canvas.drawCircle(mPaddingLeft + width * percentage, height >> 1, circleRadius, paint);
}
Copy the code
Finally, deal with the onTouch event. If so, calculate the distance, change the progress progress and notify the View to redraw it.
private float downX;
private float downY;
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean intercept = false;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
downX = event.getX();
downY = event.getY();
intercept = true;
break;
case MotionEvent.ACTION_MOVE:
float moveX = event.getX();
float moveY = event.getY();
float xMove = Math.abs(moveX - downX) - Math.abs(moveY - downY);
if (xMove > 0f) {
float hX = moveX - downX;
boolean toLeft = hX < 0.0 f;
float movePercent = Math.abs(hX) / getMeasuredWidth();
if (percentage < 1.0 f && !toLeft) {
percentage += movePercent;
} else if (percentage > 0f && toLeft) {
percentage -= movePercent;
}
if (percentage < 0f) percentage = 0f;
if (percentage > 1f) percentage = 1f;
progress = (int) Math.floor(percentage * maxProgress);
}
intercept = true;
downX = moveX;
downY = moveY;
postInvalidate();
if (null! = mListener && beforeProgress ! = getProgress()) { beforeProgress = getProgress(); mListener.onProgress(getProgress()); }break;
case MotionEvent.ACTION_UP:
break;
}
return intercept || super.onTouchEvent(event);
//return true;
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(event);
}
Copy the code
OK, a ready-to-use custom SeekBar is complete. Much easier to use than the official SeekBar (” ω ·).”
Welcome to download the Demo to play! The Demo address)