注意: 写该文章主要帮助自己记忆,贴出来希望可以给有同样问题的人解惑,不喜勿喷,可以提意见哦。
一、圆形温度计效果图如下:
1、当滑块在最低端的时候温度表示18摄氏度,当滑块在最顶端的时候温度表示32摄氏度,滑块每移动一小格,温度增加0.5摄氏度。
说明:该图的背景不是代码画出来的,是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&°rees<=360) {
if(degrees>=315&°rees<=360){
degrees=0;
flag=true;
}else if(degrees>=200&°rees<=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);
}}
布局文件:
使用自定义控件的文件
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) {
//在这里可以根据滑块的角度设置温度值
}};
}