Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

android自定义view之汽车仪表盘增强版

$
0
0

下面是改进后的效果图
这里写图片描述
原文链接:
http://blog.csdn.net/lxk_1993/article/details/51373269
绘图流程总结:
onLayout() 主要是组合排列一些包含的控件(通常是已有控件)进行布局,以及子view布局
onMeasure() 测绘,控件大小测量,子view测绘控件大小(viewGroup)
onDraw() 图形绘制,主要是对画布canvas,画笔paint ;图形绘制,这个会涉及许多API,许多绘图技巧:圆形的绘制,矩形绘制,刻度绘制,文字绘制,bitmap图形绘制,以及复杂的精密图形的绘制(一个摩托车的绘制需要很多小部件,就像PS,图形分层绘制的思想)
前两个都是对view本身或者子view进行整体处理,onDraw() 处理针对的对象是一个view的内容,是局部的处理
当然了如果你需要对自定义的view实现触摸响应,事件分发还需要实现onTouchEvent()
改进点:
1 仪表盘 速度speed字体调整,调大
2 加油启动时 仪表盘亮灯(检测speed处理速度刻度线颜色)
3 对运行速度检测,不同速度表盘文字颜色有不同的变化,用来警示用户是否超速。

SpeedControlView

public class SpeedControlView extends View implements Runnable {

    //画笔
    //mPaint:绘制刻度
    //textPaint 表盘所有文字画笔
    //speedAreaPaint 速度指针扫描弧
    private Paint mPaint, textPaint, speedAreaPaint;
    private Context mContext;
    //屏幕宽高
    private int screenWidth, screenHeight;
    //仪表盘圆的半径
    private float raduis, sRaduis;
    //圆心
    private int pointX, pointY;
    //文字的偏移量
    private float textScale;
    //速度指针变化的位置
    private float linePointerX, linePointerY;
    //速度
    private int speed;
    //速度范围的2个扇形外切矩形
    private RectF speedRectF, speedRectFInner;
    //速度控制模式  1 加速  2 减速  3 手刹
    private int type;
    // 速度文字 绘制的XY坐标
    private int baseX, baseY;
    //屏幕密度
    private float mDensityDpi;
    //设置速度控制模式
    public void setType(int type) {
        this.type = type;
    }
    //开始重绘
    private boolean start = true;

    public void setStart(boolean start) {
        this.start = start;
    }
    // 设置速度 并重绘视图
    public void setSpeed(int speed) {
        this.speed = speed;
        if(speed<120){
            textPaint.setColor(Color.WHITE);
        }
        else if (speed>=120&&speed<=210)
            textPaint.setColor(Color.YELLOW);
        else
            textPaint.setColor(Color.RED);
        //
        postInvalidate();
    }
    public SpeedControlView(Context context) {
        this(context, null);
    }
    public SpeedControlView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public SpeedControlView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        //获取屏幕宽高
//        screenWidth = ((Activity) context).getWindowManager().getDefaultDisplay().getWidth();
//        screenHeight = ((Activity) context).getWindowManager().getDefaultDisplay().getHeight();
        //获取屏幕宽高 和 屏幕密度dpi
        DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
        screenWidth = displayMetrics.widthPixels;
        screenHeight = displayMetrics.heightPixels;
        //像素密度相对于标准320的比例
        mDensityDpi = displayMetrics.densityDpi / 320;
        //开启硬件加速
        //setLayerType(View.LAYER_TYPE_HARDWARE, null);
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        //设置抗锯齿
        mPaint.setAntiAlias(true);
        //设置画笔样式 实心圆
        mPaint.setStyle(Paint.Style.FILL);
        //设置空心线宽
        mPaint.setStrokeWidth(5 * mDensityDpi);
        //初始化  圆心左边 和 半径
        raduis = screenWidth / 3;
        //圆心位于中心点
        pointX = pointY = screenWidth / 2;
//        pointY = screenHeight / 4;
        //设置抗锯齿
        textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        textPaint.setAntiAlias(true);
        //设置画笔颜色
        textPaint.setColor(Color.WHITE);
        // 获取字体并设置画笔字体
       // Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "kt.ttf");
        //为速度文字设置画笔风格
       // textPaint.setTypeface(typeface);
        //设置抗锯齿
        speedAreaPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        speedAreaPaint.setAntiAlias(true);
        //设置画笔样式
        speedAreaPaint.setStyle(Paint.Style.FILL);
        // 设置速度范围扇形的渐变颜色
        //public LinearGradient (float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile);
        Shader mShader = new LinearGradient(pointX - raduis, pointY, pointX + raduis, pointY,
                new int[]{0xFF445EED, 0xFF072AE9, 0xFF0625CE}, null, Shader.TileMode.CLAMP);
        speedAreaPaint.setShader(mShader);
        // 初始化速度范围的2个扇形外切矩形
        speedRectF = new RectF(
                pointX - raduis + 10 * mDensityDpi,
                pointY - raduis + 10 * mDensityDpi,
                pointX + raduis - 10 * mDensityDpi,
                pointY + raduis - 10 * mDensityDpi);
        speedRectFInner = new RectF(
                pointX - raduis / 2,
                pointY - raduis / 2,
                pointX + raduis / 2,
                pointY + raduis / 2);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.BLACK);
        //绘制外层圆
        drawCicle(canvas);
        //绘制速度范围扇形区域
        speedAreaPaint.setColor(0x7E3F51B5);
        drawSpeedArea(canvas);
        //变换画笔颜色 绘制刻度
        mPaint.setColor(0xBF3F6AB5);
        drawScale(canvas);
        //变换画笔颜色 绘制速度标识文字
        textPaint.setTextSize(25 * mDensityDpi);
        mPaint.setColor(Color.WHITE);
        sRaduis = raduis - 50 * mDensityDpi;
        textScale = Math.abs(textPaint.descent() + textPaint.ascent()) / 2;
//        Log.e("textScale", textScale + "");
        for (int i = 0; i < 8; i++) {
            drawText(canvas, 30 * i);
        }
        //绘制中间文字内容
        drawCenter(canvas);

    }
    /**
     * 绘制外层圆
     */
    private void drawCicle(Canvas canvas) {
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(0xFF343434);
        canvas.drawCircle(pointX, pointY, raduis, mPaint);
        //外圈2个圆
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(0xBF3F6AB5);
        //设置空心线宽
        mPaint.setStrokeWidth(4 * mDensityDpi);
        canvas.drawCircle(pointX, pointY, raduis, mPaint);
        //设置空心线宽
        mPaint.setStrokeWidth(3 * mDensityDpi);
        canvas.drawCircle(pointX, pointY, raduis - 10 * mDensityDpi, mPaint);
        //内圈2个圆
        mPaint.setStrokeWidth(5 * mDensityDpi);
        mPaint.setColor(0xE73F51B5);
        canvas.drawCircle(pointX, pointY, raduis / 2, mPaint);
        mPaint.setColor(0x7E3F51B5);
        canvas.drawCircle(pointX, pointY, raduis / 2 + 5 * mDensityDpi, mPaint);
        mPaint.setStrokeWidth(3 * mDensityDpi);
    }
    /**
     * 绘制速度区域扇形
     */
    private void drawSpeedArea(Canvas canvas) {
        int degree;
        if (speed < 210) {
            degree = speed * 36 / 30;
        } else {
           // degree = speed * 36 / 30;
            //速度达到一定后表盘指针不变化
            degree = 210 * 36 / 30;
        }
        canvas.drawArc(speedRectF, 144, degree, true, speedAreaPaint);
        // TODO: 2016/5/12
        //不显示中间的内圈的扇形区域
        speedAreaPaint.setColor(0xFF343434);
        mPaint.setColor(0xFF343434);
        mPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(speedRectFInner, 144, degree, true, mPaint);
        mPaint.setStyle(Paint.Style.STROKE);
    }
    /**
     * 绘制刻度
     */
    //需要调整刻度线 在此重写,这里使用mPaint
    private void drawScale(Canvas canvas) {
        if (speed>0)
        mPaint.setColor(Color.GREEN);
        for (int i = 0; i < 60; i++) {
            if (i % 6 == 0) {
                canvas.drawLine(pointX - raduis + 10 * mDensityDpi, pointY, pointX - raduis + 50 * mDensityDpi, pointY, mPaint);
            } else {
                canvas.drawLine(pointX - raduis + 10 * mDensityDpi, pointY, pointX - raduis + 30 * mDensityDpi, pointY, mPaint);
            }
            canvas.rotate(6, pointX, pointY);
        }
    }
    /**
     * 绘制速度标识文字
     */
    // 这里是绘制速度文字,使用画笔textPaint
    private void drawText(Canvas canvas, int value) {
        String TEXT = String.valueOf(value);
        switch (value) {
            case 0:
                // 计算Baseline绘制的起点X轴坐标
                baseX = (int) (pointX - sRaduis * Math.cos(Math.PI / 5) + textPaint.measureText(TEXT) / 2 + textScale / 2);
                // 计算Baseline绘制的Y坐标
                baseY = (int) (pointY + sRaduis * Math.sin(Math.PI / 5) + textScale / 2);
                break;
            case 30:
                baseX = (int) (pointX - raduis + 50 * mDensityDpi + textPaint.measureText(TEXT) / 2);
                baseY = (int) (pointY + textScale);
                break;
            case 60:
                baseX = (int) (pointX - sRaduis * Math.cos(Math.PI / 5) + textScale);
                baseY = (int) (pointY - sRaduis * Math.sin(Math.PI / 5) + textScale * 2);
                break;
            case 90:
                baseX = (int) (pointX - sRaduis * Math.cos(2 * Math.PI / 5) - textScale / 2);
                baseY = (int) (pointY - sRaduis * Math.sin(2 * Math.PI / 5) + 2 * textScale);
                break;
            case 120:
                baseX = (int) (pointX + sRaduis * Math.sin(Math.PI / 10) - textPaint.measureText(TEXT) / 2);
                baseY = (int) (pointY - sRaduis * Math.cos(Math.PI / 10) + 2 * textScale);
                break;
            case 150:
                baseX = (int) (pointX + sRaduis * Math.cos(Math.PI / 5) - textPaint.measureText(TEXT) - textScale / 2);
                baseY = (int) (pointY - sRaduis * Math.sin(Math.PI / 5) + textScale * 2);
                break;
            case 180:
                baseX = (int) (pointX + sRaduis - textPaint.measureText(TEXT) - textScale / 2);
                baseY = (int) (pointY + textScale);
                break;
            case 210:
                baseX = (int) (pointX + sRaduis * Math.cos(Math.PI / 5) - textPaint.measureText(TEXT) - textScale / 2);
                baseY = (int) (pointY + sRaduis * Math.sin(Math.PI / 5) - textScale / 2);
                break;
        }
        canvas.drawText(TEXT, baseX, baseY, textPaint);
    }
    /**
     * 绘制中间文字内容
     */
    //这里是最里面实时的速度值,和速度单位的绘制,使用画笔textPaint
    //measureText() 获取速度文本的坐标位置
    private void drawCenter(Canvas canvas) {
        //速度
        textPaint.setTextSize(60 * mDensityDpi);
        float tw = textPaint.measureText(String.valueOf(speed));
        baseX = (int) (pointX - tw / 2);
        baseY = (int) (pointY + Math.abs(textPaint.descent() + textPaint.ascent()) / 4);
        canvas.drawText(String.valueOf(speed), baseX, baseY, textPaint);

        //单位
        textPaint.setTextSize(30 * mDensityDpi);
        tw = textPaint.measureText("Km/h");
        baseX = (int) (pointX - tw / 2);
        baseY = (int) (pointY + raduis / 4 + Math.abs(textPaint.descent() + textPaint.ascent()) / 4);
        canvas.drawText("Km/h", baseX, baseY, textPaint);
    }

    @Override
    public void run() {
        int speedChange;
        //view内部实现一个线程接口,view加载完成 开启线程,这个线程做无限循环,无限的改变绘图参数,并调用view.postInvalied 进行重绘
        //GameLoop  游戏循环模型,无限循环,不断检测用户动作,根据不同动作类型,
        while (start) {
            //循环检测信号量type  这个信号量来源于用户按键动作
            switch (type) {
                case 1://油门
                    speedChange = 2;
                    break;
                case 2://刹车
                    speedChange = -5;
                    break;
                case 3://手刹
                    speed = 0;
                    speedAreaPaint.setColor(0x7E3F51B5);
                    textPaint.setColor(Color.WHITE);
                default:
                    speedChange = -2;
                    break;
            }
            //速度改变模型
            speed += speedChange;

            if (speed < 1) {
                speed = 0;
            }
            try {
                Thread.sleep(50);
                //间接调用 postinvalid
                setSpeed(speed);
            } catch (InterruptedException e) {
                e.printStackTrace();
                break;
            }
        }
    }
}

activity处理部分

    speedControlView = (SpeedControlView) findViewById(R.id.speed_control);
    //实体化
    speedUp = (Button) findViewById(R.id.speed_up);
    speedDown = (Button) findViewById(R.id.speed_down);
    shutDown = (Button) findViewById(R.id.shut_down);
   //设置监听
        speedUp.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //按下的时候加速
                        speedControlView.setType(1);
                        break;
                    case MotionEvent.ACTION_UP:
                        //松开做自然减速
                        speedControlView.setType(0);
                        break;
                }
                return true;
            }
        });
        speedDown.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //按下的时候减速
                        speedControlView.setType(2);
                        break;
                    case MotionEvent.ACTION_UP:
                        //松开做自然减速
                        speedControlView.setType(0);
                        break;
                }
                return true;
            }
        });
        shutDown.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //按下的时候拉手刹
                        speedControlView.setType(3);
                        break;
                    case MotionEvent.ACTION_UP:
                        //松开做自然减速
                        speedControlView.setType(0);
                        break;
                }
                return true;
            }
        });

    }

另外还要在activity的生命周期中添加处理,自定义view注销,速度初始化,状态清零
布局文件

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white"
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
<app.example.com.viewmeasure.view.SpeedControlView
    android:id="@+id/speed_control"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@android:color/white"/>
<Button
    android:id="@+id/speed_up"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="油门"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true" />
    <Button
        android:id="@+id/speed_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="刹车"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true" />
<Button
    android:id="@+id/shut_down"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="手刹"
    android:layout_alignBaseline="@+id/speed_down"
    android:layout_alignBottom="@+id/speed_down"
    android:layout_toStartOf="@+id/speed_down"
    android:layout_marginEnd="41dp" />
</RelativeLayout>

页面布局可以使用android studio提供的拖拽方案,可以快速布局

后期迭代想法

1 仪表盘 背景绘制,提供背景设置接口供用户调用
2 通过传感器,模拟汽车启动,加速,减速,刹车 来实现仪表盘状态的改变
3 当仪表盘检测到速度超过阀值,提示方案:语音,震动器
4 绘图性能优化

作者:u010129985 发表于2016/10/17 12:33:56 原文链接
阅读:11 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles