• Android为TV端助力 转载:android自定义view实战(温度控制表)!


    效果图

    package cn.ljuns.temperature.view;

    import com.example.mvp.R;

    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.RectF;
    import android.graphics.Typeface;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.util.TypedValue;
    import android.view.LayoutInflater;
    import android.view.View;


    /**
    * 步骤:
    * 1、整个背景圆(可有可无)
    * 2、进度弧(分为三段,颜色分别为绿黄红)
    * 3、进度弧上的文字(正常,预警,警告)
    * 4、刻度弧(紧靠着进度弧内侧的黑色弧)
    * 5、刻度
    * 6、中间的圆
    * 7、指针
    * 8、当前温度
    */
    public class TemperatureView extends View {

    private float progressWidth;
    private String tempText;
    private float tempTextSize;

    private Paint outCirclePaint; // 整个背景圆
    private Paint progressPaint; // 进度
    private Paint scaleArcPaint; // 刻度弧
    private Paint scalePaint; // 刻度
    private Paint panelTextPaint; // 表盘文字
    private Paint progressTextPaint; // 进度条上的文字
    private Paint pointPaint; // 中心圆
    private Paint leftPointerPaint; // 表针左半部分
    private Paint rightPointerPaint; // 表针右半部分
    private Paint pointerCirclePaint; // 表针的圆轴

    private int mSize; // 最终的大小
    private static final int PADDING = 15; // 进度的宽度
    private static final int OFFSET = 5;
    private String scale; // 刻度数值
    private int mTikeCount = 40; // 40条刻度(包括长短)
    private int mLongTikeHeight = dp2px(10); // 长刻度
    private int mShortTikeHeight = dp2px(5); // 短刻度
    private int progressRadius; // 进度弧的半径
    private int scaleArcRadius = 123; // 刻度弧的半径
    private int pointRadius = dp2px(17); // 中心圆半径

    private float currentTemp;

    public TemperatureView(Context context) {
    this(context, null);
    }

    public TemperatureView(Context context, AttributeSet attrs) {
    this(context, attrs, 0);
    }

    public TemperatureView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);

    // 获取自定义属性
    TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.temperatureProgress);
    //Dimension 指尺寸
    progressWidth = ta.getDimension(R.styleable.temperatureProgress_progressWidth, PADDING);
    //基本数据类型
    tempText = ta.getString(R.styleable.temperatureProgress_tempText);
    tempTextSize = ta.getDimension(R.styleable.temperatureProgress_tempTextSize, sp2px(15));
    ta.recycle();

    initPaint();
    }
    /**
    * 测试的宽和高,如果测试的时候设置的宽或者高的属性不是match_parent,那就把宽高设为默认的200dp
    */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int realWidth = startMeasure(widthMeasureSpec);
    int realHeight = startMeasure(heightMeasureSpec);
    Log.i("TAG", "realWidth:"+realWidth);
    Log.i("TAG", "realHeight:"+realHeight);
    /**
    * 因为是以正方形为基础
    */
    mSize = Math.min(realHeight, realWidth);
    setMeasuredDimension(mSize, mSize);
    }

    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    // 将画布移到中央
    canvas.translate(mSize /2, mSize /2);
    // 画最外面的圆
    drawOutCircle(canvas);
    // // 画进度
    drawProgress(canvas);
    // // 画进度上的文字
    drawProgressText(canvas);
    // // 画表盘
    drawPanel(canvas);
    }

    /**
    * 进度上的文字
    * @param canvas
    */
    private void drawProgressText(Canvas canvas) {
    canvas.save();
    String normal = "正常";
    String warn = "预警";
    String danger = "警告";
    // 因为文字在进度弧上,所以要旋转一定的角度
    canvas.rotate(-60, 0, 0);
    progressTextPaint.setTextSize(sp2px(12));
    Log.i("TAG", "scaleArcRadius:"+scaleArcRadius);
    canvas.drawText(normal, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
    canvas.rotate(90, 0, 0);
    canvas.drawText(warn, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
    canvas.rotate(60, 0, 0);
    canvas.drawText(danger, -dp2px(12), -scaleArcRadius - dp2px(4), progressTextPaint);
    canvas.rotate(-60, 0, 0);
    canvas.restore();
    }

    /**
    * 进度弧
    * @param canvas
    */
    private void drawProgress(Canvas canvas) {
    // dp2px(10):留一点位置(可有可无)
    progressRadius = mSize /2 - dp2px(10);
    //用来保存Canvas的状态。save之后,可以调用Canvas的平移、放缩、旋转、错切、裁剪等操作
    canvas.save();
    Log.i("TAG", "progressRadius;:"+progressRadius);
    RectF rectF = new RectF(-progressRadius, -progressRadius, progressRadius, progressRadius);
    // 设置为圆角
    progressPaint.setStrokeCap(Paint.Cap.ROUND);
    progressPaint.setColor(Color.GREEN);
    // 从150度位置开始,经过120度
    canvas.drawArc(rectF, 150, 120, false, progressPaint);
    progressPaint.setColor(Color.RED);
    progressPaint.setStrokeCap(Paint.Cap.ROUND);
    canvas.drawArc(rectF, 330, 60, false, progressPaint);
    progressPaint.setColor(Color.YELLOW);
    progressPaint.setStrokeCap(Paint.Cap.BUTT);
    canvas.drawArc(rectF, 270, 60, false, progressPaint);
    // restore:用来恢复Canvas之前保存的状态。防止save后对Canvas执行的操作对后续的绘制有影响
    canvas.restore();
    }

    /**
    * 表盘
    * @param canvas
    */
    private void drawPanel(Canvas canvas) {
    // 画刻度弧
    drawScaleArc(canvas);
    // 画中间圆
    drawInPoint(canvas);
    // 画指针
    drawPointer(canvas);
    // 绘制文字
    drawPanelText(canvas);
    }

    /**
    * 表盘上的文字
    * @param canvas
    */
    private void drawPanelText(Canvas canvas) {

    canvas.save();
    String text = "当前温度";
    float length = panelTextPaint.measureText(text);
    panelTextPaint.setTextSize(sp2px(15));
    canvas.drawText(text, -length/2, scaleArcRadius/2 + dp2px(20), panelTextPaint);
    String temp = currentTemp + " ℃";
    panelTextPaint.setTextSize(sp2px(15));
    // panelTextPaint.setColor(tempTextColor);
    float tempTextLength = panelTextPaint.measureText(temp);
    canvas.drawText(temp, -tempTextLength/2, scaleArcRadius, panelTextPaint);
    canvas.restore();

    }

    /**
    * 指针(这里分为左右部分是为了画出来的指针有立体感)
    * @param canvas
    */
    private void drawPointer(Canvas canvas) {
    RectF rectF = new RectF(-pointRadius/2, -pointRadius/2, pointRadius/2, pointRadius/2);
    canvas.save();
    // 先将指针与刻度0位置对齐
    canvas.rotate(60, 0, 0);
    float angle = currentTemp * 6.0f;
    canvas.rotate(angle, 0, 0);
    // 表针左半部分
    Path leftPointerPath = new Path();
    leftPointerPath.moveTo(pointRadius/2, 0);//moveTo:设置路径起始点
    leftPointerPath.addArc(rectF, 0, 360);//添加一个圆弧到路径
    leftPointerPath.lineTo(0, scaleArcRadius - mLongTikeHeight - dp2px(OFFSET) - dp2px(15));
    leftPointerPath.lineTo(-pointRadius/2, 0);
    leftPointerPath.close();//闭合路径
    // 表针右半部分
    Path rightPointerPath = new Path();
    rightPointerPath.moveTo(-pointRadius/2, 0);
    rightPointerPath.addArc(rectF, 0, -180);
    rightPointerPath.lineTo(0, scaleArcRadius - mLongTikeHeight - dp2px(OFFSET) - dp2px(15));
    rightPointerPath.lineTo(0, pointRadius/2);
    rightPointerPath.close();
    // 表针的圆
    Path circlePath = new Path();
    circlePath.addCircle(0, 0, pointRadius/4, Path.Direction.CW);
    canvas.drawPath(leftPointerPath, leftPointerPaint);
    canvas.drawPath(rightPointerPath, rightPointerPaint);
    canvas.drawPath(circlePath, pointerCirclePaint);
    canvas.restore();
    }

    /**
    * 中心圆
    * @param canvas
    */
    private void drawInPoint(Canvas canvas) {
    canvas.save();
    canvas.drawCircle(0, 0, pointRadius, pointPaint);
    canvas.restore();
    }

    /**
    * 刻度弧
    * @param canvas
    */
    private void drawScaleArc(Canvas canvas) {
    // 刻度弧紧靠进度弧
    scaleArcRadius = mSize/2 - (dp2px(15)+dp2px(PADDING)/4);
    Log.i("TAG", "aaaa:"+scaleArcRadius);
    canvas.save();
    // 画弧
    RectF rectF = new RectF(-scaleArcRadius, -scaleArcRadius,
    scaleArcRadius, scaleArcRadius);
    canvas.drawArc(rectF, 150, 240, false, scaleArcPaint);

    // 旋转的角度
    float mAngle = 240f / mTikeCount;
    // 画右半部分的刻度
    for (int i = 0; i <= mTikeCount/2; i++) {
    // 5的倍数就画长刻度,并标上刻度数值
    if (i % 5 == 0) {
    scale = 20 + i + "";
    panelTextPaint.setTextSize(sp2px(15));
    float scaleWidth = panelTextPaint.measureText(scale);
    canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+mLongTikeHeight, scalePaint);
    canvas.drawText(scale, -scaleWidth/2, -scaleArcRadius+mLongTikeHeight + dp2px(15), panelTextPaint);
    } else {
    canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+ mShortTikeHeight, scalePaint);
    }
    canvas.rotate(mAngle, 0, 0);
    }
    // 画布回正
    canvas.rotate(-mAngle * mTikeCount/2 - 6, 0, 0);
    // 画左半部分的刻度
    for (int i = 0; i <= mTikeCount/2; i++) {
    if (i % 5 == 0) {
    scale = 20 - i + "";
    panelTextPaint.setTextSize(sp2px(15));
    float scaleWidth = panelTextPaint.measureText(scale);
    canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+mLongTikeHeight, scalePaint);
    canvas.drawText(scale, -scaleWidth/2, -scaleArcRadius + mLongTikeHeight + dp2px(15), panelTextPaint);
    } else {
    canvas.drawLine(0, -scaleArcRadius, 0, -scaleArcRadius+ mShortTikeHeight, scalePaint);
    }
    canvas.rotate(-mAngle, 0, 0);
    }
    // 画布回正
    canvas.rotate(-mAngle * mTikeCount/2 + 6, 0, 0);
    canvas.restore();
    }

    /**
    * 最外面的圆
    * @param canvas
    */
    private void drawOutCircle(Canvas canvas) {
    // 已经将画布移到中心,所以圆心为(0,0)
    canvas.drawCircle(0, 0, mSize /2-dp2px(1), outCirclePaint);

    canvas.save();
    }

    /**
    * 测量大小
    * @param whSpec
    * @return
    */
    private int startMeasure(int whSpec) {
    int result = 0;
    int size = MeasureSpec.getSize(whSpec);
    int mode = MeasureSpec.getMode(whSpec);
    if (mode == MeasureSpec.EXACTLY) {
    result = size;
    } else {
    result = dp2px(200);
    }
    return result;
    }

    /**
    * 将 dp 转换为 px
    * @param dp
    * @return
    */
    private int dp2px(int dp) {
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getResources().getDisplayMetrics());
    }

    private int sp2px(int sp){
    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics());
    }

    /**
    * 初始化画笔
    */
    private void initPaint() {
    outCirclePaint = new Paint();//外圆漆
    progressPaint = new Paint();//进步涂料
    scaleArcPaint = new Paint();
    scalePaint = new Paint();
    panelTextPaint = new Paint();
    progressTextPaint = new Paint();
    pointPaint = new Paint();
    leftPointerPaint = new Paint();
    rightPointerPaint = new Paint();
    pointerCirclePaint = new Paint();
    progressPaint.setAntiAlias(true);
    progressPaint.setStrokeWidth(dp2px(PADDING));
    progressPaint.setStyle(Paint.Style.STROKE);
    progressPaint.setStrokeCap(Paint.Cap.ROUND);
    progressPaint.setStrokeJoin(Paint.Join.ROUND);
    outCirclePaint.setAntiAlias(true);
    outCirclePaint.setStrokeWidth(5);
    outCirclePaint.setColor((int) Long.parseLong("ffffffcc", 16));
    outCirclePaint.setStyle(Paint.Style.FILL_AND_STROKE);
    // outCirclePaint.setColor(getResources().getColor(R.color.temperatureBackground));
    scaleArcPaint.setAntiAlias(true);
    scaleArcPaint.setStrokeWidth(dp2px(2));
    scaleArcPaint.setStyle(Paint.Style.STROKE);
    scalePaint.setAntiAlias(true);
    scalePaint.setStrokeWidth(5);
    scalePaint.setStyle(Paint.Style.STROKE);
    panelTextPaint.setAntiAlias(true);
    panelTextPaint.setStyle(Paint.Style.FILL);
    panelTextPaint.setColor(Color.BLACK);
    progressTextPaint.setAntiAlias(true);
    progressTextPaint.setStyle(Paint.Style.FILL);
    progressTextPaint.setColor(Color.BLACK);
    progressTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
    pointPaint.setAntiAlias(true);
    pointPaint.setStyle(Paint.Style.FILL);
    pointPaint.setColor(Color.GRAY);
    leftPointerPaint.setAntiAlias(true);
    leftPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    leftPointerPaint.setColor(getResources().getColor(R.color.leftPointer));
    rightPointerPaint.setAntiAlias(true);
    rightPointerPaint.setColor(getResources().getColor(R.color.rightPointer));
    rightPointerPaint.setStyle(Paint.Style.FILL_AND_STROKE);
    pointerCirclePaint.setAntiAlias(true);
    pointerCirclePaint.setColor(Color.GRAY);
    pointerCirclePaint.setStyle(Paint.Style.FILL);
    pointerCirclePaint.setDither(true);
    }

    /**
    * 设置当前温度
    * @param currentTemp
    */
    public void setCurrentTemp(float currentTemp) {
    if (currentTemp < 0) {
    currentTemp = 0;
    } else if (currentTemp > 40) {
    currentTemp = 40;
    } else {
    this.currentTemp = currentTemp;
    postInvalidate();
    }
    }

    public float getCurrentTemp() {
    return currentTemp;
    }

    public float getProgressWidth() {
    return progressWidth;
    }

    public void setProgressWidth(float progressWidth) {
    this.progressWidth = progressWidth;
    }

    public String getTempText() {
    return tempText;
    }

    public void setTempText(String tempText) {
    this.tempText = tempText;
    }

    public float getTempTextSize() {
    return tempTextSize;
    }

    public void setTempTextSize(float tempTextSize) {
    this.tempTextSize = tempTextSize;
    }
    }

    MainActivity的

    public class MainActivity extends Activity implements ILoginView{

    private Button button1;
    private Button button2;
    private EditText edit1;
    private EditText edit2;
    private ILoginPresenter presenterCompl;
    private VelocityTracker vv;
    private TemperatureView mTemperatureView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_main);
    Log.i("TAG", "Parent:"+getParent());
    presenterCompl = new LoginPresenterCompl(this);
    // button1 = (Button)findViewById(R.id.button1);
    // button2 = (Button)findViewById(R.id.button2);
    // edit1 = (EditText)findViewById(R.id.edit1);
    // edit2 = (EditText)findViewById(R.id.edit2);
    //
    // button1.setOnClickListener(new OnClickListener() {
    // @Override
    // public void onClick(View v) {
    // presenterCompl.doLogin(edit1.getText().toString(), edit2.getText().toString());
    // }
    // });
    // button2.setOnClickListener(new OnClickListener() {
    // @Override
    // public void onClick(View v) {
    // presenterCompl.clear();
    // }
    // });
    mTemperatureView = (TemperatureView)findViewById(R.id.temperature_view);
    mTemperatureView = (TemperatureView) findViewById(R.id.temperature_view);
    new Thread(new Runnable() {
    @Override
    public void run() {
    for (float i = 0; i <=40; i ++) {
    mTemperatureView.setCurrentTemp(i);
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }
    }
    }).start();

    }

  • 相关阅读:
    反转字符串
    数组
    复杂度分析(二)
    复杂度分析(一)
    业务应该这么写--特性
    5种方法快速启动一个应用程序
    业务应该这么写--表达式树
    业务应该这么写--泛型
    业务应该这么写--异常处理
    关于关系型数据库外键,要减轻数据库压力的一些说法
  • 原文地址:https://www.cnblogs.com/xiaoxiaing/p/5846155.html
Copyright © 2020-2023  润新知