自定义圆形空调温度计

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

一、圆形温度计效果图如下:
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) {
           //在这里可以根据滑块的角度设置温度值
        }};

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容