RegulatorView效果图:java
RegulatorView实现步骤:android
1.新建java类RegulatorView.java,继承View类canvas
2.定义必要基础属性,及为其附初始值api
private final static int BTN_RADIUS=20;//拖动按钮的半径 private final static int BTN_CIRCLE_RADIUS=6;//拖动按钮的圆心半径 private final static int BAR_HEIGHT=6;//进度条的高度 private String barColor="#a82894ff"; private String circleColor="#902894ff"; private String txtColor="#ff2894ff"; private float currentValue=50;//当前值 private float maxValue=100;//最大值 private float minValue=0;//最小值 private boolean isShowText=false;//是否显示文字提示 private boolean isCanAdjust=false;//是否能够调节
3.实现init()方法,实例化画笔等属性ide
private void init(){ mPaint = new Paint(); mPaint.setAntiAlias(true); // mPaint.setTextSize(16); mBound = new Rect();//用于测量文字的宽度,以便准确无误地显示文字内容 mPaint.getTextBounds(maxValue+"",0,(maxValue+"").length(),mBound); }
4.实现onMeasure(int arg0,int arg1)方法,测量控件的高度和宽度(因为是横向调节器,因此只需从新测量高度值就能够了,高度值和拖动按钮的半径有关)ui
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int height ; if (heightMode == MeasureSpec.EXACTLY){ height = heightSize; } else { height = getPaddingTop() + BTN_RADIUS*2 + getPaddingBottom(); } setMeasuredDimension(widthSize, height); }
5.实现onDraw()方法,根据相应的属性值绘制界面this
绘制分为4步进行,具体实现请看文章后面的完整代码;这一步的实现已经能够看到控件的显示效果spa
@Override @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) protected void onDraw(Canvas canvas) { drawBg(canvas);//绘制总体背景 drawBar(canvas);//绘制当前值的占比条 drawBtn(canvas);//绘制当前值得调节按钮 drawTxt(canvas);//绘制文本值(显示最大最小值及当前值) }
6.实现onTouchEvent()方法以响应事件,实现调节功能,须要注意一点,界面的刷新操做是在设置当前值时才能调用(即:界面是否刷新取决于当前值是否改变,调节最大最小值时,当前值也须要调节,以确保整个控件逻辑的正确)继承
public void setCurrentValue(float currentValue) { this.currentValue = currentValue>maxValue?maxValue:currentValue; this.currentValue = this.currentValue<minValue?minValue:this.currentValue; if(onValueChangeListener!=null) onValueChangeListener.onValueChange(this.currentValue); invalidate(); } public void setMaxValue(float maxValue) { this.maxValue = maxValue<minValue?minValue:maxValue; setCurrentValue(currentValue); mPaint.getTextBounds(this.maxValue+"",0,(this.maxValue+"").length(),mBound); } public void setMinValue(float minValue) { this.minValue = minValue>maxValue?maxValue:minValue; setCurrentValue(currentValue); }
private float preX; private boolean isMoveEvent=false;//用于判断当前事件是否为滑动事件 private boolean isDownInBtn=false;//用于判断是否是点击在滑动按钮上 @Override public boolean onTouchEvent(MotionEvent event) { if(!isCanAdjust) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: preX=event.getX(); isMoveEvent=false; isDownInBtn=isDownInBtn(preX); break; case MotionEvent.ACTION_MOVE: int disX=(int) (event.getX() - preX); if(Math.abs(disX)>3) isMoveEvent=true; preX=event.getX(); if(isDownInBtn){//响应移动事件 //计算当前值时,先计算出占比值再加上最小值,这样能够兼容负值计算 setCurrentValue((preX-getPaddingLeft()-BTN_RADIUS)/getBarWidth()*(maxValue-minValue)+minValue); } break; case MotionEvent.ACTION_UP: preX=event.getX(); if(!isMoveEvent){//响应点击事件 if(!isDownInBtn){ //计算当前值时,先计算出占比值再加上最小值,这样能够兼容负值计算 setCurrentValue((preX-getPaddingLeft()-BTN_RADIUS)/getBarWidth()*(maxValue-minValue)+minValue); } } break; default: break; } return true; }
7.实现当前值变动回掉及各个功能的开关接口接口
如下是完整代码:
package com.hss.regulator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.os.Build; import android.support.annotation.RequiresApi; import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; /** * Created by Administrator on 2017/8/14. */ public class RegulatorView extends View { private final static int BTN_RADIUS=20;//拖动按钮的半径 private final static int BTN_CIRCLE_RADIUS=6;//拖动按钮的圆心半径 private final static int BAR_HEIGHT=6;//进度条的高度 private String barColor="#a82894ff"; private String circleColor="#902894ff"; private String txtColor="#ff2894ff"; private float currentValue=50;//当前值 private float maxValue=100;//最大值 private float minValue=0;//最小值 private boolean isShowText=false;//是否显示文字提示 private boolean isCanAdjust=false;//是否能够调节 private Paint mPaint; private Rect mBound; public RegulatorView(Context context) { super(context); init(); } public RegulatorView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public RegulatorView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public RegulatorView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void init(){ mPaint = new Paint(); mPaint.setAntiAlias(true); // mPaint.setTextSize(16); mBound = new Rect();//用于测量文字的宽度,以便准确无误地显示文字内容 mPaint.getTextBounds(maxValue+"",0,(maxValue+"").length(),mBound); } private float preX; private boolean isMoveEvent=false;//用于判断当前事件是否为滑动事件 private boolean isDownInBtn=false;//用于判断是否是点击在滑动按钮上 @Override public boolean onTouchEvent(MotionEvent event) { if(!isCanAdjust) return true; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: preX=event.getX(); isMoveEvent=false; isDownInBtn=isDownInBtn(preX); break; case MotionEvent.ACTION_MOVE: int disX=(int) (event.getX() - preX); if(Math.abs(disX)>3) isMoveEvent=true; preX=event.getX(); if(isDownInBtn){//响应移动事件 //计算当前值时,先计算出占比值再加上最小值,这样能够兼容负值计算 setCurrentValue((preX-getPaddingLeft()-BTN_RADIUS)/getBarWidth()*(maxValue-minValue)+minValue); } break; case MotionEvent.ACTION_UP: preX=event.getX(); if(!isMoveEvent){//响应点击事件 if(!isDownInBtn){ //计算当前值时,先计算出占比值再加上最小值,这样能够兼容负值计算 setCurrentValue((preX-getPaddingLeft()-BTN_RADIUS)/getBarWidth()*(maxValue-minValue)+minValue); } } break; default: break; } return true; } private boolean isDownInBtn(float x){ float left=getBarWidth()*(currentValue-minValue)/(maxValue-minValue)+getPaddingLeft(); float right=getBarWidth()*(currentValue-minValue)/(maxValue-minValue)+getPaddingLeft()+BTN_RADIUS*2; if(x>=left&&x<=right) return true; return false; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // int width; int height ; if (heightMode == MeasureSpec.EXACTLY){ height = heightSize; } else { height = getPaddingTop() + BTN_RADIUS*2 + getPaddingBottom(); } setMeasuredDimension(widthSize, height); } @Override @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) protected void onDraw(Canvas canvas) { drawBg(canvas);//绘制总体背景 drawBar(canvas);//绘制当前值的占比条 drawBtn(canvas);//绘制当前值得调节按钮 drawTxt(canvas);//绘制文本值(显示最大最小值及当前值) } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void drawBg(Canvas canvas){ mPaint.setColor(Color.WHITE); float left=getPaddingLeft()+BTN_RADIUS; float top=(getMeasuredHeight()-BAR_HEIGHT)/2.0f; float right= getBarWidth()+left; float bottom=top+BAR_HEIGHT; canvas.drawRoundRect(left,top,right,bottom,2,2,mPaint); // canvas.drawRect(left,top,right,bottom,mPaint); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void drawBar(Canvas canvas){ mPaint.setColor(Color.parseColor(barColor)); float left=getPaddingLeft()+BTN_RADIUS; float top=(getMeasuredHeight()-BAR_HEIGHT)/2; float right; if(maxValue!=minValue)//处理最大和最小值相等的状况 right=getBarWidth()*(currentValue-minValue)/(maxValue-minValue)+left; else right= getBarWidth()+left; float bottom=top+BAR_HEIGHT; canvas.drawRoundRect(left,top,right,bottom,2,2,mPaint); // canvas.drawRect(left,top,right,bottom,mPaint); } private void drawBtn(Canvas canvas){ mPaint.setColor(Color.parseColor(circleColor)); float cx; if(maxValue!=minValue)//处理最大和最小值相等的状况 cx=getBarWidth()*(currentValue-minValue)/(maxValue-minValue)+getPaddingLeft()+BTN_RADIUS; else cx=getBarWidth()+getPaddingLeft()+BTN_RADIUS; float cy=getMeasuredHeight()/2; canvas.drawCircle(cx,cy,BTN_RADIUS,mPaint); mPaint.setColor(Color.parseColor(barColor)); canvas.drawCircle(cx,cy,BTN_CIRCLE_RADIUS,mPaint); } private void drawTxt(Canvas canvas){ if(!isShowText) return; mPaint.setColor(Color.parseColor(txtColor)); float x=getPaddingLeft()+BTN_RADIUS; float y=mPaint.getTextSize(); canvas.drawText(minValue+"",x,y,mPaint); float textWidth = mBound.width(); x=getWidth()-getPaddingRight()-BTN_RADIUS-textWidth; canvas.drawText(maxValue+"",x,y,mPaint); if(maxValue!=minValue)//处理最大和最小值相等的状况 x=getBarWidth()*(currentValue-minValue)/(maxValue-minValue)+getPaddingLeft()+BTN_RADIUS*2; else x=getBarWidth()+getPaddingLeft()+BTN_RADIUS*2; canvas.drawText(currentValue+"",x,y,mPaint); } private int getBarWidth(){ return getMeasuredWidth()-getPaddingLeft()-getPaddingRight()-BTN_RADIUS*2; } public float getCurrentValue() { return currentValue; } public void setCurrentValue(float currentValue) { this.currentValue = currentValue>maxValue?maxValue:currentValue; this.currentValue = this.currentValue<minValue?minValue:this.currentValue; if(onValueChangeListener!=null) onValueChangeListener.onValueChange(this.currentValue); invalidate(); } public float getMaxValue() { return maxValue; } public void setMaxValue(float maxValue) { this.maxValue = maxValue<minValue?minValue:maxValue; setCurrentValue(currentValue); mPaint.getTextBounds(this.maxValue+"",0,(this.maxValue+"").length(),mBound); } public float getMinValue() { return minValue; } public void setMinValue(float minValue) { this.minValue = minValue>maxValue?maxValue:minValue; setCurrentValue(currentValue); } public boolean isShowText() { return isShowText; } public void setShowText(boolean showText) { isShowText = showText; } public boolean isCanAdjust() { return isCanAdjust; } public void setCanAdjust(boolean canAdjust) { isCanAdjust = canAdjust; } private OnValueChangeListener onValueChangeListener=null; public OnValueChangeListener getOnValueChangeListener() { return onValueChangeListener; } public void setOnValueChangeListener(OnValueChangeListener onValueChangeListener) { this.onValueChangeListener = onValueChangeListener; } public interface OnValueChangeListener{ void onValueChange(float value); } }