作为一个也算是写过代码的人(虽然是个菜鸟),一直想写下自己的博客,之前由于上班的原因(其实是因为本人太懒)一直没机会写。现在辞完职休息了一个多星期后,就动手写写博客装装逼吧,也是对自己过去一年工作的总结。第一次写博客还是有点小激动的,写得不好也是情有可原(那能怎么办,我水平就这样)。废话说了一大堆,接下来就进入正文吧。
当时项目中有个涂鸦画笔的功能,需要有个seekbar去动态调整涂鸦画笔的大小,同时还要能从seekbar表示出当前画笔的大小。效果图如下:
图中有绿色圆和绿色区域的控件就为自定义的seekbar控件。绿色圆的大小可以表示出当前涂鸦画笔的大小。滑动过的区域设置为绿色,未滑动过的区域设置为灰色。
下面介绍该控件的实现原理:
如图,图中黑色矩形框为自定义控件时的画布的大小,假设画布的宽高分别为W,H。让红色圆的上部 ( B点 )、下部(C点)分别于与画布的上边缘、下边缘相切,那么红色圆的直径就为H,可以算出红色圆的圆心坐标为(W-H/2,H/2),画出不移动的最大圆。A、B、C的坐标分别为(0,H/2)、(W-H/2,0)、(W-H/2,H)。然后新建路径backgroundPath,画三角形ABC,这时可以设置画路径backgroundPath的画笔的颜色,这就是未滑过区域的颜色。代码如下:
backgroundPath.reset();
backgroundPath.moveTo(mHeight/2,mHeight/2);
backgroundPath.lineTo(r,0);
backgroundPath.lineTo(r,mHeight);
backgroundPath.close();
canvas.drawPath(backgroundPath,backgroundPaint);
这就画出了基本的控件框架。接下来就是要画移动的黄色圆,这需要动态计算黄色圆的半径。
上图中的黄色圆是分别于直线AB、AC相切的,所以当我们左右移动黄色圆的时候黄色圆的圆心即G点的X坐标发生改变且其值是知道的(其实就是seekbar的值),所以G点坐标为(X,H/2)。由图形的几何关系很明显可以看出三角形AEG和三角形ADB是等比关系,所以可以算出黄色圆的半径EG =AG/AB*BD(可以在三角形ABD 中根据勾股定理先算出AB 的长度)。这样知道了黄色圆的圆心坐标和半径,动态画出黄色圆就行了。
最后是画出黄色圆滑动过的区域。跟上面的画backgroundPath路径一样,画三角形AEF这时设置的画笔颜色就是滑动过区域的颜色。到此这个自定义的seekbar就画完了。还有很重要的一步是给这个seekbar控件设置滑动监听事件,可以根据自己的需要实现自己的监听事件,这里就不在继续写下去了,毕竟不同的项目有不同需求,需要实现的监听也不一样。
就这第一篇博客就写完了,当然这个控件还有很多可以完善的地方,比如可以加上其他一些属性,然后配置一下,让其在使用的时候可以直接在xml布局文件中直接设置属性值,就像使用android原生控件一样(具体方法我也不太记得了,到时要用到的时候问下度娘应该也挺快可以弄好的)。在最后会给出我当时写的源码,如果你在翻博客的时候翻到了这片博客,且对你有点用处的话给我点个赞,或者评论下让我听听您的高见。
/**
* Created by lyh on 2017/11/7.
*/
public class MySeekBarView extends View {
private Paint mPaint;//移动的外圈圆的画笔
private Paint pressPaint;//滑动时的圆和滑动过的区域的画笔
private Paint backgroundPaint;//背景圆的画笔
private int mWidth;//控件的宽
private int mHeight;//空间的高
private Path pressPath;//用于控制滑动过的区域的颜色
private Path backgroundPath;//用于控制背景颜色
private int mMax = 200;//默认的最大值
private int mMin = 0;//默认的最小值
private int mProgress = 0;//当前的进度条大小
private OnProgressChangedListener mProgressListener;//滑动监听接口
public MySeekBarView(Context context) {
super(context);
init();
}
public MySeekBarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MySeekBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
mPaint = new Paint();
mPaint.setColor(0xa8ffffff);
mPaint.setDither(true);
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
pressPaint = new Paint();
pressPaint.setDither(true);
pressPaint.setAntiAlias(true);
pressPaint.setStyle(Paint.Style.FILL);
pressPaint.setColor(0xff24c092);
backgroundPaint = new Paint();
backgroundPaint.setDither(true);
backgroundPaint.setAntiAlias(true);
backgroundPaint.setStyle(Paint.Style.FILL);
backgroundPaint.setColor(0xff7a7979);
pressPath = new Path();
backgroundPath = new Path();
}
//设置移动外圈圆的颜色
public void setmPaintColor(int color){
mPaint.setColor(color);
}
//设置滑动过的区域的颜色
public void setPressPaintColor(int color){
pressPaint.setColor(color);
}
//设置背景区域的颜色
public void setBackgroundPaint(int color){
backgroundPaint.setColor(color);
}
public void setMax(int max){
if (max>mMax|maxmMax|minmMax){
mProgress = mMax;
}else{
mProgress = progress;
}
x = (int)((mProgress*1f/mMax) * (r-mHeight/2)) + mHeight/2;
r1 = getR(x);
if(mProgressListener != null) {
mProgressListener.onProgressChanged(this, mProgress, false);
}
invalidate();
}
public int getProgress(){
if (mMax!=0){
mProgress = (int) ((x-mHeight/2)*1f/(r-mHeight/2) *mMax);
}
return mProgress;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
x = (int) event.getX();
checkX();
r1 = getR(x);
if (mProgressListener!=null){
mProgressListener.onStartTrackingTouch(this);
}
break;
case MotionEvent.ACTION_MOVE:
x = (int) event.getX();
checkX();
r1 = getR(x);
if (mProgressListener!=null){
mProgressListener.onProgressChanged(this,getProgress(),true);
}
break;
case MotionEvent.ACTION_UP:
x = (int) event.getX();
checkX();
r1 = getR(x);
setProgress(getProgress());//点击时也改变进度值
if (mProgressListener!=null){
mProgressListener.onStopTrackingTouch(this);
}
break;
}
invalidate();
return true;
}
//限定x的范围
private void checkX() {
if (x <= mHeight/2){
x = mHeight/2;
}
if (x > r){
x = r;
}
if (x > r){
x = r;
}
}
//获得移动的圆的半径
private int getR(int x){
// int r = x*mHeight/2/this.r;
int r = (int) ((x-mHeight/2)*1f/(mWidth-mHeight/2-mHeight/2) * mHeight/2);
return r;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
requestLayout();
invalidate();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
private int r =mHeight/2+1;//进度条的大小,默认设为不等于零,防止有些地方控件还没显示出来先调用了setProgress()方法导致除数为零异常
private int r1;//移动时的圆的半径,可变
private int x;//移动时的x坐标(距控件最左边的距离)
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
mHeight = bottom-top;
mWidth = right-left;
r = Math.abs(mWidth-mHeight/2);
x = (int) (mProgress*1f/mMax*(r-mHeight/2)) + mHeight/2;
r1 = getR(x);
pressPath.moveTo(0,mHeight/2);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG|Paint.FILTER_BITMAP_FLAG));
canvas.drawCircle(r,mHeight/2,mHeight/2,backgroundPaint);//外圈的背景圆
if(backgroundPath!=null){//背景区域
backgroundPath.reset();
backgroundPath.moveTo(mHeight/2,mHeight/2);
backgroundPath.lineTo(r,0);
backgroundPath.lineTo(r,mHeight);
backgroundPath.close();
canvas.drawPath(backgroundPath,backgroundPaint);
}
if (pressPath!=null){//滑动过的区域
pressPath.reset();
pressPath.moveTo(mHeight/2,mHeight/2);
pressPath.lineTo(x,mHeight/2-r1);
pressPath.lineTo(x,mHeight/2+r1);
pressPath.close();
canvas.drawPath(pressPath,pressPaint);
}
if (r1==mHeight/2){
canvas.drawCircle(x,mHeight/2,r1,pressPaint);//滑动时大小变化的圆
canvas.drawCircle(x,mHeight/2,mHeight/2,mPaint);//滑动时大小不变得外圈圆
}else{
canvas.drawCircle(x,mHeight/2,mHeight/2,mPaint);//滑动时大小不变得外圈圆
canvas.drawCircle(x,mHeight/2,r1,pressPaint);//滑动时大小变化的圆
}
}
//设置滑动监听
public void setOnProgressChangedListener(OnProgressChangedListener onProgressChangedListener){
this.mProgressListener = onProgressChangedListener;
}
/**
* 滑动监听接口
*/
public interface OnProgressChangedListener {
void onProgressChanged(MySeekBarView mySeekBar, int progress, boolean fromUser);
void onStartTrackingTouch(MySeekBarView mySeekBar);
void onStopTrackingTouch(MySeekBarView mySeekBar);
}
}