最近在项目需要用到用到一个垂直方向的seekbar,在网上找了下都是通过修改系统seekbar的样式以及触摸方法来实现,实现起来也比较麻烦
所以就想着自己实现,现在给大家分享一下我的实现思路
首先从获取xml自定义属性
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalSeekBar, defStyleAttr, 0);
intrinsicHeight = (int) a.getDimension(R.styleable.VerticalSeekBar_thumbSize, -1); //图片大小 此处保持矩形为正方 形所以设置成宽高一样
intrinsicWidth = (int) a.getDimension(R.styleable.VerticalSeekBar_thumbSize, -1); //图片大小
progress=a.getInteger(R.styleable.VerticalSeekBar_progress,30);
maxProgress=a.getInteger(R.styleable.VerticalSeekBar_max,100);
orientation=a.getBoolean(R.styleable.VerticalSeekBar_orientation,false);
unSelectColor = a.getColor(R.styleable.VerticalSeekBar_barbg, 0xcc888888); //进度条背景色
selectColor = a.getColor(R.styleable.VerticalSeekBar_progressbg, getContext().getResources().getColor(R.color.main_bg_blue)); //进度条颜色
a.recycle();
init(context, attrs, defStyleAttr);
}
根据获取的属性初始化控件
/**
* 初始化控件
* @param context
*/
private void init(Context context) {
this.context = context;
paint = new Paint();
iamgePaint=new Paint();
mThumb = BitmapFactory.decodeResource(getResources(), R.drawable.seekbarring);
if (intrinsicHeight==-1) {//没有设置则使用图片本身大小
intrinsicHeight = mThumb.getHeight();
intrinsicWidth = mThumb.getWidth();
}
mDestRect = new RectF(0, 0, intrinsicWidth, intrinsicHeight);
mInnerProgressWidthPx = Dp2PxUtils.dip2px(context, mInnerProgressWidth);
}
此处我用了一个一圆圈图片作为seek的指针实际上可以通过自己绘制圆实现(偷了个懒)
@Override
protected void onDraw(Canvas canvas) {
if (!orientation ) { 判断滑动方向
locationY = (int) (intrinsicHeight * 0.5f + (maxProgress - progress) * (height - intrinsicHeight) / maxProgress);
} else {
locationY = (int) (intrinsicHeight * 0.5f + (progress) * (height - intrinsicHeight) / maxProgress);
}
paint.setColor(!orientation ? unSelectColor : selectColor);
canvas.drawRect(width / 2 - mInnerProgressWidthPx / 2, mDestRect.height() / 2, width / 2 + mInnerProgressWidthPx / 2, locationY, paint);//绘制背景
paint.setColor(!orientation ? selectColor : unSelectColor);
canvas.drawRect(width / 2 - mInnerProgressWidthPx / 2, locationY, width / 2 + mInnerProgressWidthPx / 2, height - mDestRect.height() / 2, paint);//绘制进度
canvas.save();
canvas.translate(width / 2 - mDestRect.width() / 2, locationY - mDestRect.height() / 2);
canvas.drawBitmap(mThumb, null, mDestRect, iamgePaint); //绘制指针
canvas.restore();
super.onDraw(canvas);
}
上面图形已经绘制完成接下来处理触摸事件
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//判断点击点是否在圈圈上
isInnerClick = isClickthum(event);
if (isInnerClick) {
if (listener != null) {
listener.onStart(this, progress);
}
}
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (isInnerClick) {//点击在指针上才算有效点击 才能滑动
locationY = (int) event.getY();
fixLocationY();
//计算进度值
progress = (int) (maxProgress - (locationY - intrinsicHeight * 0.5) / (height - intrinsicHeight) * maxProgress);
if (orientation) {
progress = maxProgress - progress;
}
downY = event.getY();
downX = event.getX();
if (listener != null) {
listener.onProgress(this, progress);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (isInnerClick) {
if (listener != null) { //手指松开表示滑动停止
listener.onStop(this, progress);
}
}
break;
}
return true;
}
此处只看处理步骤。有部分判断代码没有贴出,会在后面给出完整代码
最后资源回收
@Override
protected void onDetachedFromWindow() {
if (mThumb != null) {
mThumb.recycle();
}
super.onDetachedFromWindow();
}
至此自定义竖向seekbar大功告成
完整代码
package com.zwh.myapplication;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
public class VerticalSeekBar extends View {
private Context context;
private int height;
private int width;
private Paint paint;
private Paint iamgePaint;
private int maxProgress = 100; //进图条最大值
private int progress = 30; //当前进度
protected Bitmap mThumb; //seekbar中的头部圆圈
private int intrinsicHeight; //圆圈高
private int intrinsicWidth; //圆圈宽
private boolean isInnerClick;
private float downX;
private float downY;
private int locationX;
private int locationY = -1;
private int mInnerProgressWidth = 4;
private int mInnerProgressWidthPx;
private int unSelectColor ;
private RectF mDestRect;
/**
* 滑动方向,
* false代表从下向上滑
* true代表从上向下滑
*/
private boolean orientation=false;
/**
* 设置未选中的颜色
*
* @param uNSelectColor
*/
public void setUnSelectColor(int uNSelectColor) {
this.unSelectColor = uNSelectColor;
}
/**
* 设置滑动方向,
* false代表从下向上滑
* true代表从上向下滑
* @param orientation
*/
public void setOrientation(boolean orientation) {
this.orientation = orientation;
invalidate();
}
private int selectColor ;
/**
* 设置选中线条的颜色
*
* @param selectColor
*/
public void setSelectColor(int selectColor) {
this.selectColor = selectColor;
}
/**
* 设置进度条的宽度 单位是px
*
* @param mInnerProgressWidthPx
*/
public void setmInnerProgressWidthPx(int mInnerProgressWidthPx) {
this.mInnerProgressWidthPx = mInnerProgressWidthPx;
}
/**
* 设置进度条的宽度 ,单位是dp;默认是4db
*
* @param mInnerProgressWidth
*/
public void setmInnerProgressWidth(int mInnerProgressWidth) {
this.mInnerProgressWidth = mInnerProgressWidth;
//此出有一个dp转px的方法我就不贴出来了项目上相信大家都有用到自行替换
mInnerProgressWidthPx = Dp2PxUtils.dip2px(context, mInnerProgressWidth);
}
/**
* 设置图片
*
* @param id
*/
public void setThumb(int id) {
mThumb = BitmapFactory.decodeResource(getResources(), id);
intrinsicHeight = mThumb.getHeight();
intrinsicWidth = mThumb.getWidth();
mDestRect.set(0, 0, intrinsicWidth, intrinsicHeight);
invalidate();
}
/**
* 设置滑动图片的大小 单位是dp
*
* @param width
* @param height
*/
public void setThumbSize(int width, int height) {
setThumbSizePx(Dp2PxUtils.dip2px(context, width), Dp2PxUtils.dip2px(context, height));
}
/**
* 设置滑动图片的大小 单位是px
*
* @param width
* @param height
*/
public void setThumbSizePx(int width, int height) {
intrinsicHeight = width;
intrinsicWidth = height;
mDestRect.set(0, 0, width, height);
invalidate();
}
public VerticalSeekBar(Context context) {
this(context,null,0);
}
public VerticalSeekBar(Context context, AttributeSet attrs) {
this(context,attrs,0);
}
public VerticalSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.VerticalSeekBar, defStyleAttr, 0);
intrinsicHeight = (int) a.getDimension(R.styleable.VerticalSeekBar_thumbSize, -1); //图片大小 此处保持矩形为正方形所以设置成宽高一样
intrinsicWidth = (int) a.getDimension(R.styleable.VerticalSeekBar_thumbSize, -1); //图片大小
progress=a.getInteger(R.styleable.VerticalSeekBar_progress,30);
maxProgress=a.getInteger(R.styleable.VerticalSeekBar_max,100);
orientation=a.getBoolean(R.styleable.VerticalSeekBar_orientation,false);
unSelectColor = a.getColor(R.styleable.VerticalSeekBar_barbg, 0xcc888888); //进度条背景色
selectColor = a.getColor(R.styleable.VerticalSeekBar_progressbg, getContext().getResources().getColor(R.color.main_bg_blue)); //进度条颜色
a.recycle();
init(context);
}
/**
* 初始化控件
* @param context
*/
private void init(Context context) {
this.context = context;
paint = new Paint();
iamgePaint=new Paint();
mThumb = BitmapFactory.decodeResource(getResources(), R.drawable.seekbarring);
if (intrinsicHeight==-1) {
intrinsicHeight = mThumb.getHeight();
intrinsicWidth = mThumb.getWidth();
}
mDestRect = new RectF(0, 0, intrinsicWidth, intrinsicHeight);
mInnerProgressWidthPx = Dp2PxUtils.dip2px(context, mInnerProgressWidth);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height = getMeasuredHeight();
width = getMeasuredWidth();
if (locationY == -1) {
locationX = width / 2;
locationY = height / 2;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//判断点击点是否在圈圈上
isInnerClick = isClickthum(event);
if (isInnerClick) {
if (listener != null) {
listener.onStart(this, progress);
}
}
downX = event.getX();
downY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
if (isInnerClick) {//点击在指针上才算有效点击 才能滑动
locationY = (int) event.getY();
fixLocationY();
//计算进度值
progress = (int) (maxProgress - (locationY - intrinsicHeight * 0.5) / (height - intrinsicHeight) * maxProgress);
if (orientation) {
progress = maxProgress - progress;
}
downY = event.getY();
downX = event.getX();
if (listener != null) {
listener.onProgress(this, progress);
}
invalidate();
}
break;
case MotionEvent.ACTION_UP:
if (isInnerClick) {
if (listener != null) { //手指松开表示滑动停止
listener.onStop(this, progress);
}
}
break;
}
return true;
}
private void fixLocationY() {//留出显示图标半径位置
if (locationY <= intrinsicHeight / 2) {
locationY = intrinsicHeight / 2;
} else if (locationY >= height - intrinsicHeight / 2) {
locationY = height - intrinsicHeight / 2;
}
}
/**
* 是否点击了图片
*
* @param event
* @return
*/
private boolean isClickthum(MotionEvent event) {
return event.getX() >= width / 2 - intrinsicWidth / 2 -20&& event.getX() <= width / 2 + intrinsicWidth / 2+20 && event.getY() >= locationY - intrinsicHeight / 2-20 && event.getY() <= locationY + intrinsicHeight / 2+20;
}
@Override
protected void onDraw(Canvas canvas) {
if (!orientation ) {
locationY = (int) (intrinsicHeight * 0.5f + (maxProgress - progress) * (height - intrinsicHeight) / maxProgress);
} else {
locationY = (int) (intrinsicHeight * 0.5f + (progress) * (height - intrinsicHeight) / maxProgress);
}
paint.setColor(!orientation ? unSelectColor : selectColor);
canvas.drawRect(width / 2 - mInnerProgressWidthPx / 2, mDestRect.height() / 2, width / 2 + mInnerProgressWidthPx / 2, locationY, paint);
paint.setColor(!orientation ? selectColor : unSelectColor);
canvas.drawRect(width / 2 - mInnerProgressWidthPx / 2, locationY, width / 2 + mInnerProgressWidthPx / 2, height - mDestRect.height() / 2, paint);
canvas.save();
canvas.translate(width / 2 - mDestRect.width() / 2, locationY - mDestRect.height() / 2);
canvas.drawBitmap(mThumb, null, mDestRect, iamgePaint);
canvas.restore();
super.onDraw(canvas);
}
public void setProgress(int progress) {
if (height == 0) {
height = getMeasuredHeight();
}
this.progress = progress;
invalidate();
}
public int getProgress() {
return progress;
}
@Override
protected void onDetachedFromWindow() {
if (mThumb != null) {
mThumb.recycle();
}
super.onDetachedFromWindow();
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress;
}
public int getMaxProgress() {
return maxProgress;
}
private SlideChangeListener listener;
public void setOnSlideChangeListener(SlideChangeListener listener) {
this.listener = listener;
}
//添加监听接口
public interface SlideChangeListener {
/**
* 开始滑动
*
* @param slideView
* @param progress
*/
void onStart(VerticalSeekBar slideView, int progress);
/**
* 滑动过程中
*
* @param slideView
* @param progress
*/
void onProgress(VerticalSeekBar slideView, int progress);
/**
* 停止滑动
*
* @param slideView
* @param progress
*/
void onStop(VerticalSeekBar slideView, int progress);
}
}
attrs中的自定义属性