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); } }