这次来弄的是自定义view的滑动进度条,继承于AppCompatSeekBar的基础上进行二次开发。先看效果图
做到以上的效果图,就可以知道,两个模式,1:就是常用的seekbar模式,然后只不过安装自己的要求改了下滑动块而已,不多说,2:就是带有刻度点的seekbar模式,并且带有粘性数值处理,实现滑到刻度周边滑块自动吸引过去的效果。
重点说下第二种模式的,拆分下工作点:因为继承的是SeekBar,所以线和滑块我们就不用再处理了,只需要通过代码块去控制线的颜色和滑动块的背景就OK,还有就是根据数值在线上绘制相应的点。说白了就是重要点。
首先,定义相应的自定义属性,额,没办法,上头要求,所以规范点
新建一个bar_attrs.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="SegmentBar">
<attr name="max_length" format="integer"/>
<!--刻度数量-->
<attr name="node" format="integer"/>
<!--默认选中的刻度 -->
<attr name="check_item" format="integer"/>
<!--滑动块的样式-->
<attr name="block_bg" format="reference"/>
<!--线的颜色-->
<attr name="line_color" format="reference"/>
<!--刻度点的颜色-->
<attr name="spot_color" format="reference"/>
<!--刻度点的大小-->
<attr name="spot_size" format="integer"/>
<!--滑线的背景,没有滑到的线的颜色-->
<attr name="line_bg" format="reference"/>
<!--def模式是普通的滑动模式,sca是刻度模式,设置这个模式需要同时设置node参数-->
<attr name="bar_type" format="enum">
<enum name="default_type" value="0"/>
<enum name="scale_type" value="1"/>
</attr>
</declare-styleable>
</resources>
然后就是代码中取得相应的属性值和设置默认值
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SegmentBar);
maxLength = typedArray.getInteger(R.styleable.SegmentBar_max_length,100);
node = typedArray.getInteger(R.styleable.SegmentBar_node,0);
blockBg = typedArray.getResourceId(R.styleable.SegmentBar_block_bg,R.drawable.bar_back);
spotColor = typedArray.getResourceId(R.styleable.SegmentBar_spot_color,R.color.bar_point);
type = typedArray.getInteger(R.styleable.SegmentBar_bar_type,0);
lineColor = typedArray.getResourceId(R.styleable.SegmentBar_line_color,R.color.bar_line);
lineBg = typedArray.getResourceId(R.styleable.SegmentBar_line_bg,R.color.bar_line_bg);
checkItem = typedArray.getInteger(R.styleable.SegmentBar_check_item,1);
spotSize = typedArray.getInteger(R.styleable.SegmentBar_spot_size,15);
lineColor = ContextCompat.getColor(getContext(),lineColor);
lineBg = ContextCompat.getColor(getContext(),lineBg);
spotColor = ContextCompat.getColor(getContext(),spotColor);
typedArray.recycle();
initColor();
然后修改seekbar的线的颜色和滑动块的属性,通过代码定义一个Drawable属性来进行处理
public void initColor(){
this.setMax(maxLength);
this.setProgressDrawable(ContextCompat.getDrawable(getContext(),
R.drawable.bar_background));
/*Drawable thumbDraw = zoomDrawable(),2,2);*/
this.setThumb(ContextCompat.getDrawable(getContext(),
blockBg));
Rect bounds = this.getProgressDrawable().getBounds();
Drawable[] drawables = new Drawable[3];
drawables[0] = new PaintDrawable(lineBg);
drawables[2] = new ClipDrawable(new PaintDrawable(lineColor),Gravity.LEFT,ClipDrawable.HORIZONTAL);
drawables[1] = new ClipDrawable(new PaintDrawable(lineBg),Gravity.LEFT,ClipDrawable.HORIZONTAL);
LayerDrawable layerDrawable1 = new LayerDrawable(drawables);
this.setProgressDrawable(layerDrawable1);
this.getProgressDrawable().setBounds(bounds);
if (type == DEFAULT){
this.setProgress(getProgress()-1);
this.setProgress(getProgress());
}else {
int tmp = maxLength / (node+1);
int pro = checkItem * tmp;
if (checkItem == 0){
this.setProgress(1);
this.setProgress(0);
}else if (checkItem == node){
this.setProgress(maxLength-1);
this.setProgress(maxLength);
}else {
this.setProgress(pro-1);
this.setProgress(pro);
}
}
invalidate();
}
然后就是在onDraw里面绘制点
绘制点之前,需要获取几个数据点,1、seekbar的线的x,y轴,和点的xy轴。
线xy轴可以就是view的高度的对半点就是y轴的点,x轴的起始点一般就是0和view的宽度
至于点的话,可以通过线的长度/点的数量(得到平均刻度值)然后乘以相应的点的位置数量值就是得到了各个点的相应的x轴坐标,拿到了相应的xy轴,就可以绘制相应的点了
/**
* 在刻度模式下,进行绘制刻度点
* @param canvas
*/
@Override
protected synchronized void onDraw(Canvas canvas) {
float startX = 0 ;
float startY ;
float stopX ;
float stopY ;
if (type == SCALE){
//间隔宽度
int nodeWidth = 0;
if (node >= 1){
nodeWidth = (viewWidth-mPaddingLeft-mPaddingRight)/(node+1);
}
if (!isActtonUp){
checkItem = this.getProgress()/(maxLength / (node+1));
}
for (int i = 0;i <= (node+1);i++){
if (i == checkItem){
//正在处于的刻度点
if (isActtonUp){
linePaint.setColor(tran);
}else {
linePaint.setColor(lineColor);
}
}else if (i < checkItem){
//已经划过的刻度点
linePaint.setColor(lineColor);
}else {
//未划过的刻度点
linePaint.setColor(spotColor);
}
stopX = mPaddingLeft+(nodeWidth*i);
startX = stopX;
canvas.drawCircle(startX,getMeasuredHeight()/2,spotSize,linePaint);
}
}
super.onDraw(canvas);
}
做好以上的处理,基本的view的操作就OK了,但是呢,体验感不是很ok,就是没有滑动块的粘性处理。这可不行,所以弄下粘性处理吧,让滑动块滑到点的附近,根据距离点的值来进行吸引滑动块,这个可以在 onTouchEvent里面进行事件监听和拦截,当用户的手指离开屏幕时,获取当前滑块的位置,对比左右点的相差距离,决定滑动时向左点自动滑动还是向右点自动滑动
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
isActtonUp = true;
if (type == SCALE){
//粘性处理,通过滑动的数值计算出最近的点
int tmp = maxLength / (node+1);
int checkPro = getProgress();
int tmpLength = checkItem*tmp-checkPro;
if (tmpLength < 0){
if (Math.abs(tmpLength) < ( tmp /2)){
setProgress(checkItem*tmp);
}else {
int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
if (t > 1){
checkItem += t;
}else {
checkItem += 1;
}
setProgress(checkItem*tmp);
}
}else {
if (tmpLength < ( tmp /2)){
setProgress(checkItem*tmp);
}else {
int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
if (t > 1){
checkItem -= t;
}else {
checkItem -= 1;
}
setProgress(checkItem*tmp);
}
}
invalidate();
if (onItemCheckBarListener != null){
onItemCheckBarListener.itemCheckBarListener(this);
}
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
isActtonUp = false;
}
return super.onTouchEvent(event);
}
注意的一点是,在监听到用户手势离开屏幕的事件后,得消耗此事件,别让此事件分发继续下去,不然,seekbar则会重新绘制,覆盖以上的效果,在消耗的此事件之后,seekbar的滑动停止监听事件也就无效了,到时候我们就自己最佳一个接口,然使用者可以随时拿到停止滑动的线的数值
public interface OnItemCheckBarListener {
void itemCheckBarListener(SegmentBar segmentBar);
}
private OnItemCheckBarListener onItemCheckBarListener;
public void setOnItemCheckBarListener(OnItemCheckBarListener onItemCheckBarListener){
this.onItemCheckBarListener = onItemCheckBarListener;
}
嗯,以上就是达到效果图的效果了
完整代码贴出:
/*
*
* 自定义滑动进度条
* Created by caisongliang on 2019/7/24 14:29
*/
public class SegmentBar extends AppCompatSeekBar {
private static final int DEFAULT = 0;//默认模式
private static final int SCALE = 1;//有刻度模式
private int maxLength = 100;
private int minLength = 0;
//滑块背景
private int blockBg = 0;
//刻度
private int node = 4;
private int type = DEFAULT;
private int checkItem = 0;
//刻度的点的颜色
private int spotColor = 0 ;
//线的颜色
private int lineColor = 0;
//刻度点的大小
private int spotSize = 15;
private int lineBg = 0;
private int maxHeight = 0;
public SegmentBar(Context context) {
super(context);
initView(null);
}
public SegmentBar(Context context, AttributeSet attrs) {
super(context, attrs);
initView(attrs);
}
public int getMaxLength() {
return maxLength;
}
public void setMaxLength(int maxLength) {
this.maxLength = maxLength;
this.setProgress(maxLength);
}
public int getBlockBg() {
return blockBg;
}
public void setBlockBg(int blockBg) {
this.blockBg = blockBg;
}
public int getNode() {
return node;
}
public void setNode(int node) {
this.node = node;
initColor();
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getSpotColor() {
return spotColor;
}
public void setSpotColor(int spotColor) {
this.spotColor = ContextCompat.getColor(getContext(),spotColor);
invalidate();
}
public int getLineColor() {
return lineColor;
}
public void setLineColor(int lineColor) {
this.lineColor = ContextCompat.getColor(getContext(),lineColor);
initColor();
}
public int getSpotSize() {
return spotSize;
}
public void setSpotSize(int spotSize) {
this.spotSize = spotSize;
invalidate();
}
public int getLineBg() {
return lineBg;
}
public void setLineBg(int lineBg) {
this.lineBg = ContextCompat.getColor(getContext(),lineBg);
initColor();
}
private boolean isActtonUp = false;
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP) {
isActtonUp = true;
if (type == SCALE){
//粘性处理,通过滑动的数值计算出最近的点
int tmp = maxLength / (node+1);
int checkPro = getProgress();
int tmpLength = checkItem*tmp-checkPro;
if (tmpLength < 0){
if (Math.abs(tmpLength) < ( tmp /2)){
setProgress(checkItem*tmp);
}else {
int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
if (t > 1){
checkItem += t;
}else {
checkItem += 1;
}
setProgress(checkItem*tmp);
}
}else {
if (tmpLength < ( tmp /2)){
setProgress(checkItem*tmp);
}else {
int t = (Math.abs(tmpLength)+( tmp /2))/tmp;
if (t > 1){
checkItem -= t;
}else {
checkItem -= 1;
}
setProgress(checkItem*tmp);
}
}
invalidate();
if (onItemCheckBarListener != null){
onItemCheckBarListener.itemCheckBarListener(this);
}
return true;
}
} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
isActtonUp = false;
}
return super.onTouchEvent(event);
}
public interface OnItemCheckBarListener {
void itemCheckBarListener(SegmentBar segmentBar);
}
private OnItemCheckBarListener onItemCheckBarListener;
public void setOnItemCheckBarListener(OnItemCheckBarListener onItemCheckBarListener){
this.onItemCheckBarListener = onItemCheckBarListener;
}
public SegmentBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(attrs);
}
public int getCheckItem() {
return checkItem;
}
public void setCheckItem(int checkItem) {
this.checkItem = checkItem;
}
private void initView(AttributeSet attrs) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SegmentBar);
maxLength = typedArray.getInteger(R.styleable.SegmentBar_max_length,100);
node = typedArray.getInteger(R.styleable.SegmentBar_node,0);
blockBg = typedArray.getResourceId(R.styleable.SegmentBar_block_bg,R.drawable.bar_back);
spotColor = typedArray.getResourceId(R.styleable.SegmentBar_spot_color,R.color.bar_point);
type = typedArray.getInteger(R.styleable.SegmentBar_bar_type,0);
lineColor = typedArray.getResourceId(R.styleable.SegmentBar_line_color,R.color.bar_line);
lineBg = typedArray.getResourceId(R.styleable.SegmentBar_line_bg,R.color.bar_line_bg);
checkItem = typedArray.getInteger(R.styleable.SegmentBar_check_item,1);
spotSize = typedArray.getInteger(R.styleable.SegmentBar_spot_size,15);
lineColor = ContextCompat.getColor(getContext(),lineColor);
lineBg = ContextCompat.getColor(getContext(),lineBg);
spotColor = ContextCompat.getColor(getContext(),spotColor);
typedArray.recycle();
initColor();
}
public void initColor(){
this.setMax(maxLength);
this.setProgressDrawable(ContextCompat.getDrawable(getContext(),
R.drawable.bar_background));
/*Drawable thumbDraw = zoomDrawable(),2,2);*/
this.setThumb(ContextCompat.getDrawable(getContext(),
blockBg));
Rect bounds = this.getProgressDrawable().getBounds();
Drawable[] drawables = new Drawable[3];
drawables[0] = new PaintDrawable(lineBg);
drawables[2] = new ClipDrawable(new PaintDrawable(lineColor),Gravity.LEFT,ClipDrawable.HORIZONTAL);
drawables[1] = new ClipDrawable(new PaintDrawable(lineBg),Gravity.LEFT,ClipDrawable.HORIZONTAL);
LayerDrawable layerDrawable1 = new LayerDrawable(drawables);
this.setProgressDrawable(layerDrawable1);
this.getProgressDrawable().setBounds(bounds);
if (type == DEFAULT){
this.setProgress(getProgress()-1);
this.setProgress(getProgress());
}else {
int tmp = maxLength / (node+1);
int pro = checkItem * tmp;
if (checkItem == 0){
this.setProgress(1);
this.setProgress(0);
}else if (checkItem == node){
this.setProgress(maxLength-1);
this.setProgress(maxLength);
}else {
this.setProgress(pro-1);
this.setProgress(pro);
}
}
invalidate();
}
private Drawable zoomDrawable(Drawable drawable, int w, int h) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap oldbmp = drawableToBitmap(drawable);
Matrix matrix = new Matrix();
float scaleWidth = ((float) w / width);
float scaleHeight = ((float) h / height);
matrix.postScale(scaleWidth, scaleHeight);
Bitmap newbmp = BitmapUtil.scaleImage(oldbmp,w,h);
return new BitmapDrawable(null, newbmp);
}
private Bitmap drawableToBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
Bitmap.Config config = drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888
: Bitmap.Config.RGB_565;
Bitmap bitmap = Bitmap.createBitmap(width, height, config);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, width, height);
drawable.draw(canvas);
return bitmap;
}
private int viewWidth = 0;
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
viewWidth = getMeasuredWidth();
initSegmentBar();
}
private int mPaddingLeft;
private int mPaddingRight;
private Context mContext;
public void initSegmentBar(){
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
mPaddingLeft = getPaddingLeft();
mPaddingRight = getPaddingRight();
} else {
mPaddingLeft = getPaddingStart();
mPaddingRight = getPaddingEnd();
}
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener l) {
super.setOnSeekBarChangeListener(l);
}
Paint linePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
int tran = getContext().getResources().getColor(R.color.bar_tran);
/**
* 在刻度模式下,进行绘制刻度点
* @param canvas
*/
@Override
protected synchronized void onDraw(Canvas canvas) {
float startX = 0 ;
float startY ;
float stopX ;
float stopY ;
if (type == SCALE){
//间隔宽度
int nodeWidth = 0;
if (node >= 1){
nodeWidth = (viewWidth-mPaddingLeft-mPaddingRight)/(node+1);
}
if (!isActtonUp){
checkItem = this.getProgress()/(maxLength / (node+1));
}
for (int i = 0;i <= (node+1);i++){
if (i == checkItem){
//正在处于的刻度点
if (isActtonUp){
linePaint.setColor(tran);
}else {
linePaint.setColor(lineColor);
}
}else if (i < checkItem){
//已经划过的刻度点
linePaint.setColor(lineColor);
}else {
//未划过的刻度点
linePaint.setColor(spotColor);
}
stopX = mPaddingLeft+(nodeWidth*i);
startX = stopX;
canvas.drawCircle(startX,getMeasuredHeight()/2,spotSize,linePaint);
}
}
super.onDraw(canvas);
}
}
------------------------------------转发请声明出处-----------------------------------------------