自定义View 实现多塔机监控类似星轨雷达扫描

效果

最近公司业务要实现塔机监控功能 自己写了一个自定义view 它会根据你设置 的数据大小自动画圆 数据list. size是1画一个圆 这里我传了4组数据 里面用到了弧度计算 和角度计算都在代码里 它的基本原理是根据ValueAnimator 动画动态绘制图形 废话不多说 直接上代码 代码不多直接复制下面的view就可以了

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.List;

/**
 * @since 1.0
 * <p>自定义View
 * chendu  2018/6/1
 */
public class DemoTowerView extends View {

    /**
     * 边框的宽度 字体大小和颜色
     */
    private int mBorderWidth = 2;
    private int mTextColor = Color.GRAY;
    private int mTextSize = 12;

    /**
     * 画笔
     */
    private Paint mPaint;

    /**
     * 区域的宽度和高度
     */
    private int mWidth;
    private int mHeight;

    private float[] currentAngle = new float[6];//线动态移动角度 最多同时展示6个塔机
    private int[] greenx = new int[6];//绿点动态距离x
    private int[] greeny = new int[6];//绿点动态距离y

    //下面这三个参数是为了第二次传参过来时 图形能接着上一次结束的状态在移动 而不是从0开始
    private float[] currentAnglelast = new float[6];//记录线动态移动角度结束后最后的角度
    private int[] greenxlast = new int[6];//记录绿点移动结束后最后的距离x
    private int[] greenylast = new int[6];//记录绿点移动结束后最后的距离y
    private int[] mwidthlast = new int[6];//记录上一个塔吊移动的距离
    private int[] prevmovewidth = new int[6];//记录上一个塔吊移动的距离

    List<Towerbean> list = new ArrayList<>();

    private int cx, cy, leve;
    private int width = this.getResources().getDisplayMetrics().widthPixels / 6;   //getMeasuredWidth()/6;根据控件大小取宽 或者直接获取屏幕大小

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

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

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

        mPaint = new Paint();
        
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        if (widthMode == MeasureSpec.EXACTLY) {
            mWidth = widthSize;
        } else {
            int desire = getPaddingLeft() + getPaddingRight() + (int) TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
            mWidth = Math.min(desire, widthSize);
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            mHeight = heightSize;
        } else {
            int desire = getPaddingTop() + getPaddingBottom() + (int) TypedValue.applyDimension(
                    TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
            mHeight = Math.min(desire, heightSize);
        }
        mWidth = Math.min(mWidth, mHeight);//取最小值  防止绘制内容出错   以最小的边来为基准进行相关的绘制
        setMeasuredDimension(mWidth, mWidth);
    }


    @Override
    protected void onDraw(final Canvas canvas) {

        /**
         * 监控的塔机圆心的xy和圆环的宽度
         */
        cx = getPaddingLeft() + (getMeasuredWidth() - getPaddingLeft() - getPaddingRight()) / 2;
        cy = getPaddingTop() + (getMeasuredHeight() - getPaddingTop() - getPaddingBottom()) / 2;
        width = Math.min(getWidth() / 6, getHeight() / 6);//半径
        //画十字坐标
        mPaint.setColor(Color.LTGRAY);
        canvas.drawLine(cx, 0, cx, mHeight, mPaint);
        canvas.drawLine(0, cy, mWidth, cy, mPaint);
        mPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText("0°", cx, 15, mPaint);
        canvas.drawText("90°", mWidth - 15, cy, mPaint);
        canvas.drawText("180°", cx, mWidth - 15, mPaint);
        canvas.drawText("270°", 15, cy, mPaint);

        mPaint.setAntiAlias(true);//去除边缘锯齿,优化绘制效果

        if (list != null && list.size() > 0) {

            for (int i = 0; i < list.size(); i++) {

                leve = leve != 0 ? list.get(0).twlong : width;//获取检测对象臂长 按比例设置其他塔机臂长
                int twocx = cx + (int) (width * list.get(i).distance / leve * Math.sin(Math.PI * list.get(i).angle / 180));//圆心x
                int twocy = cy - (int) (width * list.get(i).distance / leve * Math.cos(Math.PI * list.get(i).angle / 180));//圆心y

                if (i == 0) {
                    //画检测塔机内外圆
                    mPaint.setColor(Color.BLACK);
                    mPaint.setStyle(Paint.Style.STROKE); //设置空心
                    mPaint.setStrokeWidth(mBorderWidth); //设置圆环的宽度
                    canvas.drawCircle(cx, cy, width, mPaint);//外圆  黑色
                    //内圆 绿色
                    mPaint.setStyle(Paint.Style.FILL); //设置实心
                    mPaint.setColor(Color.parseColor("#B2C8F9D2"));
                    canvas.drawCircle(cx, cy, width - mBorderWidth, mPaint);
                } else {

                    //基于圆心*度的外围塔机外圆
                    mPaint.setColor(Color.BLACK);
                    mPaint.setStyle(Paint.Style.STROKE); //设置空心
                    mPaint.setStrokeWidth(mBorderWidth); //设置圆环的宽度
                    canvas.drawCircle(twocx, twocy, width * list.get(i).twlong / leve, mPaint);//外圆  黑色
                    mPaint.setStyle(Paint.Style.FILL); //设置实心
                }


                //画塔臂线2
                mPaint.setColor(Color.BLACK);
                canvas.save();//保存后面的状态
                canvas.rotate(currentAngle[i], twocx, twocy);
                canvas.drawRect(twocx - TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()),
                        twocy - width * list.get(i).twlong / leve + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()),
                        twocx + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics()),
                        twocy + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()), mPaint);
                canvas.restore();//撤销保存的状态

                //圆心,红色2
                mPaint.setColor(Color.RED);
                canvas.drawCircle(twocx, twocy, TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()), mPaint);

                //塔吊绿点
                mPaint.setColor(Color.GREEN);
                canvas.drawCircle(twocx + greenx[i], twocy - greeny[i], TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 3, getResources().getDisplayMetrics()), mPaint);//圆,绿色色


                // 塔机字体
                mPaint.setColor(mTextColor);
                mPaint.setTextSize(mTextSize);
                mPaint.setTextAlign(Paint.Align.CENTER);
                canvas.drawText(list.get(i).name, twocx, twocy - (width * list.get(i).twlong / (2 * leve)), mPaint);


            }

        }


    }


    /**
     * 设置数据
     *
     * @param list
     */
    public void setDate(List<Towerbean> list) {
        this.list = list;
        if (list != null && list.size() > 0)
            for (int i = 0; i < list.size(); i++) {
                currentAnglelast[i] = currentAngle[i];
                greenxlast[i] = greenx[i];
                greenylast[i] = greeny[i];
                mwidthlast[i] = prevmovewidth[i];
                leve = list.get(0).twlong;
                int mmovewidth = width * list.get(i).movewidth / leve;//实际值转换为等比例宽度 第一个塔吊的臂长为整个屏幕的1/6
                startCirMotion(list.get(i).twturnAngle, Math.abs(mmovewidth - mwidthlast[i]), i);

            }


    }

    /**
     * 设置塔机字体颜色 要在数据设置之前
     *
     * @param color
     */
    public void setmTextColor(int color) {
        mTextColor = color;
    }

    /**
     * 设置塔机字体大小
     *
     * @param size
     */
    public void setmTextSize(int size) {
        mTextSize = size;
    }

    /**
     * 旋转动画
     */
    private void startCirMotion(final float Angle, final int mwidth, final int i) {
        ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f);
        animator.setDuration((long) 3000);//动画时间3S
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float t = (Float) animation.getAnimatedValue();
                currentAngle[i] = t * Angle + currentAnglelast[i];
                float r = t * mwidth + mwidthlast[i];
                greenx[i] = (int) (r * Math.sin(Math.PI * currentAngle[i] / 180));
                greeny[i] = (int) (r * Math.cos(Math.PI * currentAngle[i] / 180));
//                Log.e("CD", "debug:(x,y) = " + greenx[i] + "," + greeny[i]);
                invalidate();
                prevmovewidth[i] = (int) (t * mwidth);
            }
        });
        animator.setInterpolator(new LinearInterpolator());// 匀速旋转
        animator.start();
    }


}

数据对象类如下

public class Towerbean {

    float angle;//相对检测对象塔机角度
    int twlong;//臂长
    float twturnAngle;//转动角度
    int movewidth;//塔机移动距离
    int distance;//两个塔机相对距离
    String name;//塔机名字

}

在布局中引用

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <tower.DemoTowerView
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="10dp"
        android:text="塔吊动画"/>

</LinearLayout>

Activity中使用

view = findViewById(R.id.view);

 List<Towerbean> list = new ArrayList<>();
//自己造的数据对象 你要几个圆就造几个这样的数据 
        Towerbean bean = new Towerbean();
        bean.angle = 0;
        bean.twlong = 100;
        bean.twturnAngle = -30;
        bean.movewidth = 80;
        bean.distance = 0;
        bean.name = "塔机卡萨粉红色发挥1";
        list.add(bean);
        
        view.setDate(list);

最后感想 ! 这个控件修改掉原点可以实现类似雷达扫描 去掉杠杆 多添加几个点可以实现类似 星图环绕 自己想到的就这么多 希望上面的代码对你有用!

猜你喜欢

转载自juejin.im/post/5b109918e51d4506ab41967a