看到了一个App自定义的SeekBar很有趣,尝试着自己做一个。
不废话,先上图,看是否是你期望的:

增加了背景图片的自定义,增加了Thumb的自定义。当然还有动画效果。会动画滑动到附近的点。具体代码稍后奉上...
啊。。。乱七八糟事儿太多,这几天都没时间整理
这个demo的点可以自定义数量,并增加回调。我就不写原理了...直接把代码传上去...有时间再写...
也可以尝试IndicatorSeekBar这个,GitHub上的,2017年出的,写这篇博客是2016年。那时候还没有....
源码已发布,地址:https://download.csdn.net/download/explorerqp/11373353
首先需要画出seekbar的背景drawable:
package com.zkbc.tougu.demo;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
/**
* 自定义seekBar背景图
* Created by Rock Lee on 2016/6/21.
*/
public class MySeekBarDrawable extends Drawable {
int pointCount;
int startColor;
int centerColor;
int endColor;
private Paint paint;//画笔(背景)
LinearGradient shader;
private Paint paintCircle;//画笔-外圆
private Paint paintCircleCenter;//画笔-内圆
RectF rectF;//矩形上下左右的坐标
int colors[] = new int[3];//三个颜色渐变(shader)
float positions[] = new float[3];//渐变色的三个点(shader)
public MySeekBarDrawable(int pointCount, int startColor, int centerColor, int endColor) {
this.pointCount = pointCount * 2;//增加两点之间的中线
this.startColor = startColor;
this.centerColor = centerColor;
this.endColor = endColor;
paint = new Paint();
paintCircle = new Paint();
paintCircleCenter = new Paint();
rectF = new RectF();
}
@Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
// 第1个点
colors[0] = startColor;
positions[0] = 0;
// 第2个点
colors[1] = centerColor;
positions[1] = 0.5f;
// 第3个点
colors[2] = endColor;
positions[2] = 1;
//是否有中间色
if (centerColor == 0) {
shader = new LinearGradient(0f, 0f, bounds.width(), bounds.height(), startColor, endColor, Shader.TileMode.MIRROR);
} else {
shader = new LinearGradient(0f, 0f, bounds.width(), bounds.height(), colors, positions, Shader.TileMode.MIRROR);
}
paint.setShader(shader);
paint.setStrokeCap(Paint.Cap.ROUND);// 圆角
paint.setAntiAlias(true); // 消除锯齿
paintCircle.setShader(shader);
// paintCircle.setStyle(Paint.Style.STROKE); // 设置空心
// paintCircle.setStrokeWidth(bounds.height()/2); // 设置笔画的宽度
paintCircle.setAntiAlias(true); // 消除锯齿
paintCircleCenter.setAntiAlias(true); // 消除锯齿
paintCircleCenter.setColor(Color.WHITE);
float lineHeight = bounds.height()/4.0f;
rectF.set(0, bounds.centerY() - lineHeight, bounds.width(), bounds.centerY() + lineHeight);
//绘制圆角矩形
canvas.drawRoundRect(rectF, lineHeight, lineHeight, paint);
float section = (float) bounds.width() / pointCount;
for (int i = 1; i < pointCount; i++) {
paint.setShader(null);
paint.setColor(Color.WHITE);
// paint.setStrokeWidth(1);
float cx = section * i;//X轴圆心坐标
if (i % 2 == 0) {
canvas.drawLine(cx, bounds.centerY() - lineHeight, cx, bounds.centerY() + lineHeight, paint);
} else {
canvas.drawCircle(cx, bounds.centerY(), lineHeight*2, paintCircle);
canvas.drawCircle(cx, bounds.centerY(), lineHeight, paintCircleCenter);
}
}
}
@Override
public void setAlpha(int alpha) {
paint.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
paint.setColorFilter(colorFilter);
}
@Override
public int getOpacity() {
return 1 - paint.getAlpha();
}
/**
* MySeekBarDrawable Builder
*/
public static class Builder {
/**
* 分割段数
*/
int pointCount;
/**
* 起始颜色
*/
int startColor;
/**
* 中间色
*/
int centerColor;
/**
* 结束颜色
*/
int endColor;
/**
* Sets the seekBar point count.
*
* @returns This Builder
*/
public Builder setPointCount(int pointCount) {
this.pointCount = pointCount;
return this;
}
/**
* Sets the seekBar start color.
*
* @param startColor start color in #AARRGGBB format.
* @returns This Builder
*/
public Builder setStartColor(int startColor) {
this.startColor = startColor;
return this;
}
/**
* Sets the seekBar center color.
*
* @param centerColor center color in #AARRGGBB format.
* @returns This Builder
*/
public Builder setCenterColor(int centerColor) {
this.centerColor = centerColor;
return this;
}
/**
* Sets the seekBar end color.
*
* @param endColor end color in #AARRGGBB format.
* @returns This Builder
*/
public Builder setEndColor(int endColor) {
this.endColor = endColor;
return this;
}
/**
* Creates a new MySeekBarDrawable with the requested parameters
*
* @return New MySeekBarDrawableInstance
*/
public MySeekBarDrawable create() {
return new MySeekBarDrawable(pointCount, startColor, centerColor, endColor);
}
}
}
第二步:自定义seekbar,并将我们的自定义drawable附上:
package com.zkbc.tougu.demo;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.TextView;
import com.zkbc.tougu.R;
/**
* 自定义seekBar
* Created by Rock Lee on 2016/6/21.
*/
public class MyNiceSeekBar extends SeekBar implements SeekBar.OnSeekBarChangeListener {
private int pointCount;
private int startColor;
private int centerColor;
private int endColor;
public final int oneLength = 100;//每个隔断的刻度
int risk;
TextView centerText;
Drawable thumbDrawable;
View thumb;
MySeekBarDrawable drawable;
SeekBarInterface barInterface;//参数回调
SeekBarOnDrawListener onDrawListener;//seekBar绘画监听
private boolean mPopupStyle;//是否显示pop
private boolean mThumbStyle;
private int xOffset;
private PopupWindow mPopup;
private TextView mPopupTextView;
private int mYLocationOffset;
public MyNiceSeekBar(Context context) {
this(context, null);
}
public MyNiceSeekBar(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyNiceSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.MySeekBar);
pointCount = mTypedArray.getInteger(R.styleable.MySeekBar_pointCount, 5);
startColor = mTypedArray.getColor(R.styleable.MySeekBar_startColor, Color.GREEN);
centerColor = mTypedArray.getColor(R.styleable.MySeekBar_centerColor, 0);
endColor = mTypedArray.getColor(R.styleable.MySeekBar_endColor, Color.RED);
mPopupStyle = mTypedArray.getBoolean(R.styleable.MySeekBar_popupStyle, false);
mThumbStyle = mTypedArray.getBoolean(R.styleable.MySeekBar_popupStyle, false);
xOffset = (int) mTypedArray.getDimension(R.styleable.MySeekBar_xOffset, 0);
mYLocationOffset = (int) mTypedArray.getDimension(R.styleable.MySeekBar_yOffset, 0);
mTypedArray.recycle();
setMax(pointCount * 2 * oneLength);
setProgress(3 * oneLength);
setOnSeekBarChangeListener(this);
drawable = new MySeekBarDrawable.Builder()
.setPointCount(pointCount)
.setStartColor(startColor)
.setCenterColor(centerColor)
.setEndColor(endColor).create();
setProgressDrawable(drawable);
initThumb((getProgress() / (2 * oneLength) + 1) + "");
initHintPopup();
}
public void initThumb(String risk) {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
thumb = inflater.inflate(R.layout.seekbar_thumb, null);
centerText = (TextView) thumb.findViewById(R.id.text1);
if (mThumbStyle) {
centerText.setText(risk);
}
thumbDrawable = convertViewToDrawable(thumb);
setThumb(thumbDrawable);
}
public static Drawable convertViewToDrawable(View view) {
view.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
view.layout(0, 0, view.getMeasuredHeight(), view.getMeasuredHeight());
view.setDrawingCacheEnabled(true);
Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache(true));
Drawable drawable = new BitmapDrawable(null, bitmap);
view.destroyDrawingCache();
view.setDrawingCacheEnabled(false);
return drawable;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), getMeasuredHeight());
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (onDrawListener != null) {
onDrawListener.onDrawListener();
}
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
//进度改变时调用
risk = getTargetProgress(seekBar) / (2 * oneLength) + 1;
String popupText;
if (barInterface != null) {
int targetProgress = getTargetProgress(seekBar);
popupText = barInterface.progressChangeCallBack(this, getProgress(), targetProgress);
mPopupTextView.setText(popupText != null ? popupText : String.valueOf(targetProgress));
}
if (mPopupStyle && mPopup != null) {
showPopup();
mPopup.update(this, (int) getXPosition(getProgress(), mPopup.getContentView()), -(this.getHeight() + mPopup.getContentView().getMeasuredHeight() + mYLocationOffset), -1, -1);
} else {
hidePopup();
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
//进度条开始拖动的时候调用
showPopup();
centerText.setText("");
thumbDrawable = convertViewToDrawable(thumb);
setThumb(thumbDrawable);
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
//进度条停止拖动的时候调用
int targetProgress = getTargetProgress(seekBar);
dodo(seekBar.getProgress(), targetProgress);
if (mThumbStyle) {
centerText.setText(risk + "");
}
thumbDrawable = convertViewToDrawable(thumb);
setThumb(thumbDrawable);
}
/**
* 赋值+执行动画
*
* @param progressText 当前滑动的点
* @param targetProgress 自动滑动到目标点
*/
public void dodo(int progressText, int targetProgress) {
AnimatorSet animation = new AnimatorSet();
ObjectAnimator progressAnimation = ObjectAnimator.ofInt(this, "progress",
progressText, targetProgress);
progressAnimation.setDuration(300);
progressAnimation.setInterpolator(new AccelerateDecelerateInterpolator());
//增加动画监听
progressAnimation.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationStart(Animator animation) {
}
@Override
public void onAnimationEnd(Animator animation) {
hidePopup();
}
@Override
public void onAnimationCancel(Animator animation) {
hidePopup();
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
animation.playTogether(progressAnimation);
animation.start();
}
/**
* 选中回调
*
* @param barInterface 回调监听
*/
public void setSeekBarCallBack(SeekBarInterface barInterface) {
this.barInterface = barInterface;
}
/**
* 绘画监听接口
*
* @param onDrawListener 绘画监听实现接口
*/
public void setOnDrawListener(SeekBarOnDrawListener onDrawListener) {
this.onDrawListener = onDrawListener;
}
/**
* 是否显示pop
* @param style 是否显示pop
*/
public void setPopupStyle(boolean style) {
mPopupStyle = style;
}
private void initHintPopup() {
String popupText = null;
if (barInterface != null) {
int targetProgress = getTargetProgress(this);
popupText = barInterface.progressChangeCallBack(this, getProgress(), targetProgress);
}
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View undoView = inflater.inflate(R.layout.popup, null);
mPopupTextView = (TextView) undoView.findViewById(R.id.text);
mPopupTextView.setText(popupText != null ? popupText : String.valueOf(getProgress()));
mPopup = new PopupWindow(undoView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, false);
mPopup.getContentView().measure(0, 0);//获取测量后的popWindow高度
mPopup.setAnimationStyle(R.style.fade_animation);
}
private void showPopup() {
if (mPopupStyle) {
mPopup.showAsDropDown(this, (int) getXPosition(getProgress(), mPopup.getContentView()), -(this.getHeight() + mPopup.getContentView().getMeasuredHeight() + mYLocationOffset));
}
}
private void hidePopup() {
if (mPopup != null && mPopup.isShowing()) {
mPopup.dismiss();
}
}
/**
* 获取X坐标
*
* @param progress 进度
* @param v 在seekBar上显示的View
* @return X
*/
private float getXPosition(int progress, View v) {
//seekBar总宽度包含ThumbOffset两边
float val = (((float) progress * ((float) getWidth())) / (float) getMax());
int textWidth = v.getMeasuredWidth();
float textCenter = (textWidth / 2.0f) + xOffset;
return val - textCenter;
}
/**
* 获取目标进度
*
* @return 返回目标进度
*/
private int getTargetProgress(SeekBar seekBar) {
int targetProgress;//进度参数回调默认值
int proNow = seekBar.getProgress();//当前点
int yu = proNow % oneLength;//取余数
if ((proNow / oneLength) % 2 == 0) {
//是否为满进度,满进度回退一格
targetProgress = proNow == getMax() ? proNow - oneLength : proNow + oneLength - yu;
} else {
targetProgress = proNow - yu;
}
return targetProgress;
}
/**
* 设置推荐坐标
*
* @param recommendPosition 显示在第几个点,从0开始
* @param ll_recommend
*/
public void setRecommendPosition(int recommendPosition, View ll_recommend) {
int progress = oneLength + recommendPosition * 2 * oneLength;
float x = getXPosition(progress, ll_recommend);
LinearLayout.LayoutParams pa = (LinearLayout.LayoutParams) ll_recommend.getLayoutParams();
pa.setMargins((int) x, 0, 0, 0);
ll_recommend.setLayoutParams(pa);
}
public int getRisk() {
return risk;
}
public void setRisk(int risk) {
this.risk = risk;
}
/**
* 是否显示Thumb数字
* @param mThumbStyle
*/
public void setThumbStyle(boolean mThumbStyle) {
this.mThumbStyle = mThumbStyle;
}
}
使用方式:
private void initSeekbar() {
recommend = 3;
mSeekBar1.initThumb((mSeekBar1.getProgress() / (2 * mSeekBar1.oneLength)) + "");
mSeekBar1.setSeekBarCallBack(new SeekBarInterface() {
@Override
public String progressChangeCallBack(MyNiceSeekBar myNiceSeekBar, int progress, int targetProgress) {
risk = myNiceSeekBar.getRisk();
myNiceSeekBar.setRisk(risk);
//返回Pop上需要显示的字符串
return "节点" + risk;
}
});
mSeekBar1.setOnDrawListener(new SeekBarOnDrawListener() {
@Override
public void onDrawListener() {
mSeekBar1.setRecommendPosition(recommend, ll_recommend);
}
});
}
本文介绍如何自定义一个带有背景图片、Thumb和动画效果的SeekBar,包括点的数量自定义和回调功能。作者分享了实现过程,并提供了源码下载链接。
410

被折叠的 条评论
为什么被折叠?



