android自定義調節器控件 —— RegulatorView

RegulatorView效果圖:

RegulatorView實現步驟:

    1.新建java類RegulatorView.java,繼承View類

    2.定義必要基礎屬性,及爲其附初始值

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()方法,實例化畫筆等屬性

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)方法,測量控件的高度和寬度(由於是橫向調節器,所以只需重新測量高度值就可以了,高度值和拖動按鈕的半徑有關)

@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()方法,根據相應的屬性值繪製界面

        繪製分爲4步進行,具體實現請看文章後面的完整代碼;這一步的實現已經可以看到控件的顯示效果

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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章