扫描数据可视化:以逼真的雷达屏幕显示您的数据






4.33/5 (5投票s)
使用简单的代码绘制逼真的雷达屏幕。
引言
这是一个 Android 应用程序,用于使用用户的数据(例如来自传感器的数据)绘制雷达屏幕。 您可以使用此控件简单地绘制逼真的雷达屏幕。
此控件将显示三种数据,第一种是先前扫描阶段的数据,第二种是当前扫描阶段的数据,最后一种是先前值和当前值的平均值。 如果需要,它可以更改为控制形状,即具有 0~180 度操作范围的半圆屏幕或具有 0~360 度的全圆。
背景
在我的项目中构建移动机器人系统,我需要可视化来自传感器的数据。 同时,我在互联网上寻找类似的项目。 我发现的是 Larry 的主页 (http://luckylarry.co.uk) 但该示例对我没有用处,因为该程序是基于 PC 的代码,带有 processing (由非营利基金会开发的编译器)。 所以,我决定新开发一个基于 Android 系统的数据可视化程序,因为计划开发一个由移动设备远程控制的机器人系统。 但是,由于 Larry 的主页提供了很多帮助,我向 Larry 先生表示感谢。
Using the Code
此代码可简单地用于您的项目。 首先,将RadarView.java添加到您的项目。 然后,您编辑要显示的活动文件的资源文件。
定义
|
这是用于设置扫描值的颜色的enum 值。 |
|
颜色变量将通过Color.argb() 设置,它具有 alpha、red、green、blue 的属性。 |
|
这是三种数据的每个值是否可见的选项。bPreSweep :启用或禁用先前的数据可视化bCurSweep :启用或禁用当前的数据可视化bAvrSweep :启用或禁用平均数据可视化 [平均值 = (先前数据+当前数据)/2] |
void setHalfSweep(boolean ck) |
如果输入变量为 true,则显示屏幕将为半圆,否则将为全圆。 半圆的显示数据范围为 0 到 180 度,全圆的显示数据范围为 0 到 360 度。[input: true] 半圆模式[input: false] 全圆模式 |
void setMaxDistance(int val) |
此函数用于设置检测距离的最大指示值。val :最大距离。 |
void setValue(int index, int val) |
这是用于设置当前值的函数。index :扫描角度,以度为单位val :距离的检测值 |
Resource
在您的layout.xml文件中添加如下代码的资源。 您必须更改此包名称 (com.example.bskim
)。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
....
tools:context="com.example.myradarctrlapp.MainActivity">
<com.example.bskim.RadarView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/RadarView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true">
</RelativeLayout>
实现
RadarView 类 (RadarView.java)
扫描数据由从原点到传感点的实线描述。 线长是传感器和任何物体之间的距离,线的角度是从基线(x 轴)检测到的方向。
检测物体的扫描运动由扇形实现。
public class RadarView extends View {
......
void DrawDisplay(Canvas canvas, int radius, int centerx, int centery) {
float x, y;
boolean clockwise = false;
float ratio = (float)radius/ (float)widthp*3.0f;
int textsize = (int)(25.0* ratio);
if ( (dectAngle - oldDectAngle) >= 0) {
clockwise = false;
} else {
clockwise = true;
}
Paint paint=new Paint();
paint.setStyle(Paint.Style.FILL_AND_STROKE);
// previous sweep
if(mbPrevSweep == true) {
paint.setARGB(64, 0, 32, 0);
Path path1st = new Path();
path1st.reset();
path1st.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) oldDistValue[i]));
y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) oldDistValue[i]));
path1st.lineTo(x, y);
}
canvas.drawPath(path1st, paint);
}
// current sweep
if(mbCurSweep == true) {
paint.setARGB(128, 0, 200, 0);
Path path2nd = new Path();
path2nd.reset();
path2nd.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) * ((float) distValue[i]));
y = centery + (int) (Math.sin(Math.toRadians((-i))) * ((float) distValue[i]));
path2nd.lineTo(x, y);
}
canvas.drawPath(path2nd, paint);
}
// average
if(mbAvrSweep == true) {
paint.setStyle(Paint.Style.STROKE);
paint.setARGB(255, 0, 0, 255);
Path pathavg = new Path();
pathavg.reset();
pathavg.moveTo(centerx, centery);
for (int i = 0; i < sweepAngle; i++) {
x = centerx + (int) (Math.cos(Math.toRadians((-i))) *
((float) ((distValue[i] + oldDistValue[i]) / 2)));
y = centery + (int) (Math.sin(Math.toRadians((-i))) *
((float) ((distValue[i] + oldDistValue[i]) / 2)));
pathavg.lineTo(x, y);
}
canvas.drawPath(pathavg, paint);
}
//sweep motion
if(mbSweepMotion==true) {
//sweep motion
paint.setAntiAlias(true);
paint.setStrokeWidth(7);
int gradationAngle = 30;
if(dectAngle < gradationAngle) gradationAngle = dectAngle;
if(dectAngle> (sweepAngle-gradationAngle)) gradationAngle = sweepAngle-dectAngle;
//scan motion drawing
if (clockwise == false) {
for (int i = gradationAngle; i >= 0; i--) {
if(i==0)
paint.setColor(mLayColor);
else paint.setARGB(128, 150+(100/gradationAngle * i),
150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
canvas.drawLine(centerx, centery, centerx +
(int)(Math.cos(Math.toRadians(-dectAngle + (i))) *
(float)radius), centery + (int) (Math.sin(Math.toRadians(-dectAngle + (i))) *
(float)radius), paint);
}
} else {
for (int i = 0; i <= gradationAngle; i++) {
if(i==0)
paint.setColor(mLayColor);
else paint.setARGB(128, 150+(100/gradationAngle * i),
150+(100/gradationAngle * i), 150+(100/gradationAngle * i));
canvas.drawLine(centerx, centery, centerx +
(int) (Math.cos(Math.toRadians(-dectAngle + (-i))) * (float) radius),
centery + (int) (Math.sin(Math.toRadians(-dectAngle + (-i))) *
(float) radius), paint);
}
}
}
......
}
}
通过更改扫描方向,扫描运动的阴影方向将发生变化。
顺时针扫描 (180o,179o,....,2o,1o,0o) 逆时针扫描 (0o,1o,2o,....,179o,180o)
创建视图和此用法的示例
创建RadarView
的对象实例并配置其属性。
例如,全圆模式,扫描线颜色为绿色。 扫描距离范围由常量值MAX_DISTANCE
设置,我们可以使用findViewById()
从预定义的 XML 文件中获取资源。 R.id.RadarView
在layout.xml中定义为android:id="@+id/RadarView"
。
RadarView mRadarView;
mRadarView = (RadarView) findViewById(R.id.RadarView);
mRadarView.setHalfSweep(false);
mRadarView.setDisplayColor(RadarView.radarColor.LAY, Color.argb(255,0,255,0));
mRadarView.setMaxDistance(MAX_DISTANCE);
mRadarView.setSweepMotion(true);
以下代码显示了此类的用法。 要绘制的数据缓冲区由setValue(..)
填充,并且使用处理程序,以便安全地更新视图。
TimerTask mTask;
Timer mTimer;
mTask = new TimerTask() {
@Override
public void run() {
if(mRadarView.isHalfMode()==true) {
if (angle > 180) flag = 0;
if (angle <= 0) flag = 1;
if (flag == 0) angle--;
else angle++;
}else {
if(angle>360) angle = 0;
else angle++;
}
int distance = mRandVal.nextInt(MAX_DISTANCE);
mRadarView.setValue(angle, distance);
RadarViewInvalidate();
}
};
mTimer = new Timer();
mTimer.schedule(mTask, 500, 100); //every 100ms after 500ms.
在使用setValue(..)
从传感器输入新数据后,应调用RadaViewInvalidate()
以更新视图。
Handler mDrawHandler = new Handler();
private void RadarViewInvalidate() {
mDrawHandler.post(new Runnable() {
@Override
public void run() {
mRadarView.invalidate();
}
});
}
就是这样!我希望你能轻松地理解我。 感谢大家阅读本文。
关注点
我想注意创建一个新的图形视图类和一个资源。
历史
本文将很快更新。