自定义圆形空调温度计

 注意: 写该文章主要帮助自己记忆,贴出来希望可以给有同样问题的人解惑,不喜勿喷,可以提意见哦。

一、圆形温度计效果图如下:
1、当滑块在最低端的时候温度表示18摄氏度,当滑块在最顶端的时候温度表示32摄氏度,滑块每移动一小格,温度增加0.5摄氏度。


1510552606(1).png
说明:该图的背景不是代码画出来的,是UI切的图,主要做的就是勾画小滑块,让小滑块沿着有颜色的半圆弧滑动。

二、自定义控件相应方法讲解

   onMeasure
   根据视图的长宽来确定内圆半径,外圆半径,圆心点。
   onDraw
   正真绘制小滑块的方法,根据旋转的角度,绘制滑块的位置。
   onTouchEvent
   当人为的去滑滑动条的时候,就会调用该方法。
   setDegree
   通过该方法设置温度示数,来计算滑块需要旋转的角度。
   flag变量
   用于表示是否要更新滑块的位置,更新滑块的几个条件如下:
   (1)抬手或者取消,都需要更新视图。
   (2)当移动的过程中,超过规定的范围也需要更新视图。

2、自定义圆形温度计实现代码:

 public class TempCircularSeekBar extends View {

private Paint circleColorpaint;
//绘制滑块的画笔
private Paint innerColor;
//滑块需要旋转的角度
private int angle = 0;
    //圆环的宽度
private int barWidth = 50;
   //内部圆的半径
private float innerRadius;
   //外部圆的半径
private float outerRadius;
   //圆心的x坐标
private float cx;
    //圆心的y坐标
private float cy;
    //The left bound for the circle RectF
private float left;
//The right bound for the circle RectF
private float right;
//The top bound for the circle RectF
private float top;
// The bottom bound for the circle RectF
private float bottom;



//The adjustment factor. This adds an adjustment of the specified             
     //size to
private float adjustmentFactor = 100;
/** The rectangle containing our circles and arcs. */
private RectF rectcenter = new RectF();
    private TempDegreeCallback degreecallback;
/**
 * Instantiates a new circular seek bar.
 * 
 * @param context
 *            the context
 * @param attrs
 *            the attrs
 * @param defStyle
 *            the def style
 */
public TempCircularSeekBar(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    barWidth = ToolUtils.dip2px(context, 25);
    adjustmentFactor = 80;
    circleColorpaint = new Paint();
    circleColorpaint.setStyle(Paint.Style.STROKE);
    circleColorpaint.setColor(Color.parseColor("#00ffffff"));
    circleColorpaint.setAntiAlias(true);
    innerColor = new Paint();
    innerColor.setStyle(Paint.Style.FILL);
    innerColor.setColor(Color.parseColor("#00ffffff"));                                                 
    innerColor.setAntiAlias(true);
    innerColor.setStrokeWidth(10);
    innerColor.setColor(Color.parseColor("#00ffffff"));
}

/**
 * Instantiates a new circular seek bar.
 * 
 * @param context
 *            the context
 * @param attrs
 *            the attrs
 */
public TempCircularSeekBar(Context context, AttributeSet attrs)     
   {
    this(context, attrs,0);
}

/**
 * Instantiates a new circular seek bar.
 * 
 * @param context
 *            the context
 */
public TempCircularSeekBar(Context context) {
    this(context,null);
}

public void setCallback(TempDegreeCallback callback) {
    this.degreecallback = callback;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    final int width = MeasureSpec.getSize(widthMeasureSpec);
    final int height = MeasureSpec.getSize(heightMeasureSpec);

    int size = (width > height) ? height : width; // Choose the smaller

    cx = width / 2; // Center X for circle
    cy = height / 2; // Center Y for circle
    outerRadius = (size / 2) - 25; // Radius of the outer circle

    innerRadius = outerRadius - barWidth; // Radius of the inner circle

    left = cx - outerRadius; // Calculate left bound of our rect
    right = cx + outerRadius;// Calculate right bound of our rect
    top = cy - outerRadius;// Calculate top bound of our rect
    bottom = cy + outerRadius;// Calculate bottom bound of our rect
        rectcenter.set(left, top, right, bottom);

}

@Override
protected void onDraw(Canvas canvas) {

    circleColorpaint.setStrokeWidth(barWidth);
    if (angle > 201) {
        angle = 201;
    }
    // 画内部填充的圆形
    canvas.drawArc(rectcenter, 0, 360, false, circleColorpaint);
    canvas.restore();
    if (angle <= 201 && angle >= 0) {
        float toCenter = outerRadius;
        double miniDegree = Math.PI * (270 - angle) / 180;
        float leftMini = (int) (toCenter * Math.cos(miniDegree)) + left + toCenter;
        float topMini = -(int) (toCenter * Math.sin(miniDegree)) + top + toCenter;
        // 滑动圆心坐标
        // 根据滑动圆心坐标 计算长方形四个角的坐标(设 长方形 长度为l 高度为h)
        int lenth = 28;//小滑块的高
        int width = 13;//小滑块的宽
        double pathDegree;
        if (angle >= 90) {
            pathDegree = Math.PI * (angle - 90) / 180;
        } else {
            pathDegree = Math.PI * (angle + 90) / 180;
        }

        float x1 = (int) (leftMini - lenth * Math.cos(pathDegree) - width * Math.sin(pathDegree));
        float y1 = (int) (topMini - lenth * Math.sin(pathDegree) + width * Math.cos(pathDegree));
        float x2 = (int) (leftMini - lenth * Math.cos(pathDegree) + width * Math.sin(pathDegree));
        float y2 = (int) (topMini - lenth * Math.sin(pathDegree) - width * Math.cos(pathDegree));
        float x3 = (int) (leftMini + lenth * Math.cos(pathDegree) + width * Math.sin(pathDegree));
        float y3 = (int) (topMini + lenth * Math.sin(pathDegree) - width * Math.cos(pathDegree));
        float x4 = (int) (leftMini + lenth * Math.cos(pathDegree) - width * Math.sin(pathDegree));
        float y4 = (int) (topMini + lenth * Math.sin(pathDegree) + width * Math.cos(pathDegree));

        Log.e("rectcenterminicircle", "滑动角度 :" + angle + " 滑块偏移角度 :" + miniDegree * 180 / Math.PI + " 圆心 :" + leftMini
                + "---" + topMini + "------长方形 :" + "(" + x1 + "," + y1 + ")" + "  (" + x2 + "," + y2 + ")" + "  (" + x3
                + "," + y3 + ")" + "  (" + x4 + "," + y4 + ")");
        // 由于画布不能直接画长方形 只能通过画线包裹成一个长方形
        Path path = new Path();
        path.moveTo(x1, y1);
        path.lineTo(x2, y2);
        path.lineTo(x3, y3);
        path.lineTo(x4, y4);
        path.moveTo(x1, y1);
        path.close();

        innerColor.setStyle(Paint.Style.FILL);
        innerColor.setColor(Color.parseColor("#99F7F9Fd"));
        canvas.drawPath(path, innerColor);
    }
    canvas.save();
    canvas.translate(cx, cy);
    float roteangle = 180;//
    canvas.rotate(roteangle);
    canvas.restore();
    super.onDraw(canvas);
}

/**
 * Set the degree.
 * 
 * @param degree
 *            the new degree
 */
public void setDegree(float degree) {
    int angle = (int) ((degree - 18) * 2 * 7);
    Log.d("Tests", "setDegree angle="+angle);
    //防止滑块滑不到最顶部
    if(angle==196){
        this.angle=201;
    }else {
        this.angle = angle;
    }
    invalidate();
}


@Override
public boolean onTouchEvent(MotionEvent event) {
    flag=false;
    float x = event.getX();
    float y = event.getY();
    switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
        flag=false;
        //当抬手的时候更新
        moved(x, y);
        break;
    case MotionEvent.ACTION_MOVE:
        flag=false;
        float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2));
        if (distance > outerRadius + (adjustmentFactor-15) || distance < innerRadius - (adjustmentFactor-15)){
            flag=true;          
        }
        moved(x, y);
        break;
    case MotionEvent.ACTION_UP:
        flag=true;
        moved(x, y);
        break;

    case MotionEvent.ACTION_CANCEL:
        flag=true;
        moved(x, y);
        break;
    }
    return true;
}

/**
 * Moved.
 * 
 * @param x
 *            the x
 * @param y
 *            the y
 */
private void moved(float x, float y) {
    float distance = (float) Math.sqrt(Math.pow((x - cx), 2) + Math.pow((y - cy), 2));
    if (distance < outerRadius + adjustmentFactor && distance > innerRadius - adjustmentFactor) {
        float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(cx - x, y - cy)) + 360.0)) % 360.0);
        if (degrees < 0) {
            degrees += 2 * Math.PI;
        }
        if (degrees <= 250||degrees>=315&&degrees<=360) {
            
            if(degrees>=315&&degrees<=360){
                degrees=0;
                flag=true;
            }else if(degrees>=200&&degrees<=250){
                degrees=200;
                flag=true;
            }           
            int angle = Math.round(degrees);
            if (angle < 0)
                angle = 0;
            // 0-210°是滑动范围 根据自己要分割的刻度 一等份角度数 text是刻度 452行有刻度的画法和分割规律
            float temp = (float)(((angle + 3) / 7) * 0.5 + 18);
            if(temp>32){
                temp=32;
            }
            //根据温度计算滑动的角度
            setDegree(temp);
            //当抬手或者滑出了边际则调到该函数
            if (this.degreecallback != null&&flag) {
                this.degreecallback.Degree(temp);
                flag=false;
            }
        }   
    }
}
   //更加温度值,设置角度
public abstract interface TempDegreeCallback {
    public abstract void Degree(float degree);

}}

布局文件:

  <?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/temperature_bg" >
<com.hwatong.kongtiaoxiugai_12a.TempCircularSeekBar
    android:id="@+id/temp_seekbar"
    android:layout_width="279dp"
    android:layout_height="280dp"
    android:background="@drawable/temperature_bg" />

<TextView
    android:id="@+id/temperature_txt"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:text="@string/mintempnum"
    android:textColor="@color/white_air"
    android:textSize="25sp" />

<ImageButton
    android:id="@+id/temperature_up_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginTop="-40dp"
    android:background="@drawable/up_selector" />

<ImageButton
    android:id="@+id/temperature_down_btn"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:layout_marginTop="40dp"
    android:background="@drawable/down_selector" />
</FrameLayout>

使用自定义控件的文件

public class MainActivity extends Activity{
private TempCircularSeekBar mTempCircularSeekBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.airconditional_main);
            mTempCircularSeekBar = (TempCircularSeekBar)          findViewById(R.id.temp_seekbar);
                   
        mTempCircularSeekBar.setCallback(mTempDegreeCallback);
        //根据温度改变滑块的位置。
         mTempCircularSeekBar.setDegree(32);
    }

TempDegreeCallback mTempDegreeCallback = new         TempDegreeCallback() {
    @Override
    public void Degree(float degree) {
           //在这里可以根据滑块的角度设置温度值
        }};

}

推荐阅读更多精彩内容