首先來(lái)看看效果圖:CSDN博客地址
這里我使用了兩種實(shí)現(xiàn)方式:
- 繼承 view 實(shí)現(xiàn)。
- 繼承 surfaceview 實(shí)現(xiàn)。
為什么會(huì)有兩種實(shí)現(xiàn)方式呢?
主要是因?yàn)槲以诶^續(xù)加入一些自定義功能的時(shí)候,如果是繼承 view ,出現(xiàn)了卡頓的現(xiàn)象,也就是說(shuō)在 UI 線(xiàn)程中做的邏輯操作太多了,導(dǎo)致 UI 線(xiàn)程失幀,最終導(dǎo)致了卡頓現(xiàn)象。又考慮到有些童鞋還沒(méi)有學(xué)習(xí) surfaceview ,所以會(huì)用兩種方式去實(shí)現(xiàn)。文章末尾會(huì)貼出 GitHub 地址,所以這里只會(huì)貼出核心內(nèi)容。
先簡(jiǎn)要說(shuō)一下這里需要涉及到的知識(shí)點(diǎn):
- 2D繪圖基礎(chǔ)
- surfaceview
- ValueAnimator (可選)
- 高中三角函數(shù) Math.sin() Math.cos()。
繪制思路
- 繪制一個(gè)圓,顏色圍繞圓心漸變。
- 讓這個(gè)圓圍繞圓心不斷旋轉(zhuǎn),就有了掃描的效果。
- 根據(jù)半徑生成隨機(jī)的紅點(diǎn),當(dāng)數(shù)量超過(guò) 5 個(gè)的時(shí)候,去掉最后一個(gè)點(diǎn),讓數(shù)量一直保持5個(gè)。
第一步:繪制一個(gè)漸變圓
ok,先看效果圖:
初始化成員變量:
private int radius;//圓半徑
private String TAG = "zoneLog";//Log 日志的 tag
private Matrix matrix;//view 的矩陣參數(shù),用于旋轉(zhuǎn)圓形
private float sweepAngle;//
private boolean isStart;//是否開(kāi)始 valueanimator
private int value1;//valueanimator 的漸變值
private int x;//紅點(diǎn)的 x 坐標(biāo)值
private int y;//紅點(diǎn)的 y 坐標(biāo)值
private int totalAngle;//總旋轉(zhuǎn)角度
private Paint redPointPaint;//紅點(diǎn)畫(huà)筆
private Paint sweepPaint;//圓形畫(huà)筆,繪制圓角漸變
private Paint strokeWhitePaint;//描邊白色畫(huà)筆,用于繪制空心圓圈
private List<SRadarSweepView.MyPoint> pointList;//記錄紅點(diǎn)的坐標(biāo)
private void init() {
matrix = new Matrix();
post(runnable);//用于實(shí)現(xiàn)圓形的不斷旋轉(zhuǎn)
handler.sendEmptyMessageDelayed(0, 1000);
isStart = true;
pointList = new ArrayList<>();
radius = 300;
sweepAngle = 8;//旋轉(zhuǎn)角度
redPointPaint = new Paint();
redPointPaint.setAntiAlias(true);
redPointPaint.setColor(Color.RED);
redPointPaint.setStyle(Paint.Style.FILL_AND_STROKE);
sweepPaint = new Paint();
sweepPaint.setAntiAlias(true);
sweepPaint.setStyle(Paint.Style.FILL_AND_STROKE);
SweepGradient sweepGradient = new SweepGradient(0, 0, new int[]{0X10000000, Color.WHITE}, null);//角度漸變,由透明變?yōu)榘咨? sweepPaint.setShader(sweepGradient);//設(shè)置 shader
strokeWhitePaint = new Paint();
strokeWhitePaint.setAntiAlias(true);
strokeWhitePaint.setColor(Color.WHITE);
strokeWhitePaint.setStyle(Paint.Style.STROKE);
strokeWhitePaint.setStrokeWidth(1);
}
用于記錄小紅點(diǎn):
class MyPoint {//用于記錄小紅點(diǎn)的圓心
int x;
int y;
float angle;
public MyPoint(int x, int y, float angle) {
this.x = x;
this.y = y;
this.angle = angle;
}
}
初始化完成后即可進(jìn)行相關(guān)的繪制工作了:
canvas.drawColor(getResources().getColor(R.color.huaweiClockView));//繪制背景顏色
canvas.save();//在另外一個(gè)圖層來(lái)繪制圓形,否則會(huì)影響到后續(xù)操作
canvas.concat(matrix);//獲取 view 的矩陣參數(shù)
canvas.translate(getWidth() / 2, getHeight() / 2);//將原點(diǎn)移動(dòng)至中心
canvas.drawCircle(0, 0, radius, sweepPaint);//繪制漸變圓
canvas.drawCircle(0, 0, radius + 80, strokeWhitePaint);//以下是繪制描邊圓圈
canvas.drawCircle(0, 0, radius - 80, strokeWhitePaint);//
canvas.drawCircle(0, 0, radius - 160, strokeWhitePaint);//
canvas.drawCircle(0, 0, radius - 240, strokeWhitePaint);//
canvas.restore();//合并之前的操作,相當(dāng)于 photoshop 中的圖層合并
第二步:讓圓轉(zhuǎn)動(dòng)起來(lái)
這里通過(guò)修改 view 的矩陣參數(shù),讓其實(shí)現(xiàn)旋轉(zhuǎn),我們?nèi)庋劭雌饋?lái),也就實(shí)現(xiàn)了掃描的效果。先看效果:
private Runnable runnable = new Runnable() {
@Override
public void run() {
totalAngle += sweepAngle;//統(tǒng)計(jì)總的旋轉(zhuǎn)角度
matrix.postRotate(sweepAngle, getWidth() / 2, getHeight() / 2);//旋轉(zhuǎn)矩陣,旋轉(zhuǎn) 8 度。
postInvalidate();//刷新
postDelayed(runnable, 200);//調(diào)用自身,實(shí)現(xiàn)不斷循環(huán)
}
};
第三步:生成小紅點(diǎn)
這里通過(guò) handler 來(lái)不斷生成小紅點(diǎn),而且讓小紅點(diǎn)有一定的停留時(shí)間。且跟隨掃描的腳步去生成。
來(lái)看 一下效果圖:
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
int currentAngle = totalAngle % 360;//計(jì)算出一個(gè)圓范圍內(nèi)的旋轉(zhuǎn)角度
int currentRadius = (int) (radius * Math.random()) + 50;//隨機(jī)取得一個(gè)半徑
x = (int) (currentRadius * Math.cos(currentAngle));//通過(guò)三角函數(shù),計(jì)算出 x y 坐標(biāo)值
y = (int) (currentRadius * Math.sin(currentAngle));
if (currentAngle > 0 && currentAngle < 90) {//計(jì)算出各個(gè)象限的情況
x = Math.abs(x);
y = Math.abs(y);
} else if (currentAngle > 90 && currentAngle < 180) {
x = -Math.abs(x);
y = Math.abs(y);
} else if (currentAngle > 180 && currentAngle < 270) {
x = -Math.abs(x);
y = -Math.abs(y);
} else if (currentAngle > 270 && currentAngle < 360) {
y = -Math.abs(y);
x = Math.abs(x);
} else if (currentAngle == 0 || currentAngle == 360) {
y = 0;
x = Math.abs(x);
} else if (currentAngle == 90) {
x = 0;
y = Math.abs(y);
} else if (currentAngle == 180) {
y = 0;
x = -Math.abs(x);
} else if (currentAngle == 270) {
x = 0;
y = -Math.abs(y);
}
pointList.add(0, new MyPoint(x, y, totalAngle));
if (pointList.size() > 5) {//超過(guò) 5 個(gè)數(shù)據(jù)時(shí),抹掉最后一個(gè)數(shù)據(jù)
pointList.remove(pointList.size() - 1);
}
handler.sendEmptyMessageDelayed(0, 1000);//發(fā)送 message 實(shí)現(xiàn)不斷循環(huán)
}
}
};
在 ondraw() 中追加以下代碼,繪制小紅點(diǎn):
canvas.translate(getWidth() / 2, getHeight() / 2);
for (int i = 0; i < pointList.size(); i++) {
canvas.drawCircle(pointList.get(i).x, pointList.get(i).y, 30, redPointPaint);
}
canvas.restore();
好的,我們一步一步完成了雷達(dá)掃描圖的繪制,路下來(lái)可能還有點(diǎn)懵逼,那么就看看整一段的代碼吧。
view實(shí)現(xiàn)
surfaceview實(shí)現(xiàn)
如果文中有什么知識(shí)點(diǎn)是錯(cuò)誤的或者更好的實(shí)現(xiàn)方法,請(qǐng)及時(shí)聯(lián)系我進(jìn)行修改,以免誤導(dǎo)別人。謝謝。
最后還有一種效果是這樣的,這里就不過(guò)多講解了,可以進(jìn)源碼看看,哈哈。