最近公司業務要實現塔機監控功能 自己寫了一個自定義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);
複製程式碼
最後感想
! 這個控制元件修改掉原點可以實現類似雷達掃描 去掉槓桿 多新增幾個點可以實現類似 星圖環繞 自己想到的就這麼多 希望上面的程式碼對你有用!