首页 Java java教程 Android动画之雷达扫描效果

Android动画之雷达扫描效果

Jan 16, 2017 pm 05:18 PM

我们首先看一下效果图,有个整体的印象

Android动画之雷达扫描效果

好了,为了便于理解,这里就按照动画所见内容依次展开来说

准备

这里决定采用canvas(画布)和paint(画笔)实现了这个简单动画控件。

由图片可以看到有两条交叉的十字线、几个圆圈和一些白点,那么首先定义一下所需的画笔,画布及一些数据

setBackgroundColor(Color.TRANSPARENT);
 
 //宽度=5,抗锯齿,描边效果的白色画笔
 mPaintLine = new Paint();
 mPaintLine.setStrokeWidth(5);
 mPaintLine.setAntiAlias(true);
 mPaintLine.setStyle(Style.STROKE);
 mPaintLine.setColor(Color.WHITE);
 
 //宽度=5,抗锯齿,描边效果的浅绿色画笔
 mPaintCircle = new Paint();
 mPaintCircle.setStrokeWidth(5);
 mPaintCircle.setAntiAlias(true);
 mPaintCircle.setStyle(Style.FILL);
 mPaintCircle.setColor(0x99000000);
 
 //暗绿色的画笔
 mPaintSector = new Paint();
 mPaintSector.setColor(0x9D00ff00);
 mPaintSector.setAntiAlias(true);
 //定义一个暗绿色的梯度渲染
 mShader = new SweepGradient(viewSize / 2, viewSize / 2,
Color.TRANSPARENT, Color.GREEN);
 mPaintSector.setShader(mShader);
 
 //白色实心画笔
 mPaintPoint=new Paint();
 mPaintPoint.setColor(Color.WHITE);
 mPaintPoint.setStyle(Style.FILL);
 
 //随机生成一些数组点,模拟雷达扫描结果
 point_x = UtilTools.Getrandomarray(15, 300);
 point_y = UtilTools.Getrandomarray(15, 300);
登录后复制

这里说一下这个SweepGradient

SweepGradient的构造函数:

public SweepGradient(float cx, float cy, int[] colors, float[] positions)
登录后复制
public SweepGradient(float cx, float cy, int color0, int color1)
登录后复制

其中cx,cy 指定圆心, color1,color0 或 colors 指定渐变的颜色 ,对于使用多于两种颜色时,还可以通过positions 指定每种颜色的相对位置,positions 设为NULL时表示颜色均匀分布。

绘制基本图形

canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintCircle);
canvas.drawCircle(viewSize / 2, viewSize / 2, 255, mPaintLine);
canvas.drawCircle(viewSize / 2, viewSize / 2, 125, mPaintLine);
canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintLine);
//绘制两条十字线
canvas.drawLine(viewSize / 2, 0, viewSize / 2, viewSize, mPaintLine);
canvas.drawLine(0, viewSize / 2, viewSize, viewSize / 2, mPaintLine);
登录后复制

这样就绘制除了整个UI,接下来加上动画,就可以实现整体的效果。

动画实现

这里实现动画的时候,用到了Matrix这个东西,也就是矩阵。上学的时候,线性代数老师讲各种线性变换时,脑子里在想,这玩意是干嘛使得,现在总算是遇上了,现在看起来也是云里雾里。总的来说就是可以使用Matrix实现强大的图形动画,包括位移、旋转、缩放及透明变化等效果,matrix有着一系列的setTranslate,setRotate,setScale等方法。很方便的实现图形各种变换,主要还是需要理解各种变换。

动画实现线程

protected class ScanThread extends Thread {
 
  private RadarView view;
 
  public ScanThread(RadarView view) {
   // TODO Auto-generated constructor stub
   this.view = view;
  }
 
  @Override
  public void run() {
   // TODO Auto-generated method stub
   while (threadRunning) {
    if (isstart) {
     view.post(new Runnable() {
      public void run() {
       start = start + 1;
       matrix = new Matrix();
       //设定旋转角度,制定进行转转操作的圆心
//       matrix.postRotate(start, viewSize / 2, viewSize / 2);
//       matrix.setRotate(start,viewSize/2,viewSize/2);
       matrix.preRotate(direction*start,viewSize/2,viewSize/2);
       view.invalidate();
 
      }
     });
     try {
      Thread.sleep(5);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }
登录后复制

首先,这里在一个独立线程中不断的对start做累加,作为旋转角度。然后将其和matrix关联。这里尝试使用了matrix的三个方法,暂时没有发现区别。

动画绘制

接下来在onDraw方法中不断绘制图形即可

//根据matrix中设定角度,不断绘制shader,呈现出一种扇形扫描效果
canvas.concat(matrix);
canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintSector);
登录后复制

最终实现

好了,最终整体的代码如下:

public class RadarView extends FrameLayout {
 
 private Context mContext;
 private int viewSize = 800;
 private Paint mPaintLine;
 private Paint mPaintCircle;
 private Paint mPaintSector;
 public boolean isstart = false;
 private ScanThread mThread;
 private Paint mPaintPoint;
 //旋转效果起始角度
 private int start = 0;
 
 private int[] point_x;
 private int[] point_y;
 
 private Shader mShader;
 
 private Matrix matrix;
 
 public final static int CLOCK_WISE=1;
 public final static int ANTI_CLOCK_WISE=-1;
 
 @IntDef({ CLOCK_WISE, ANTI_CLOCK_WISE })
 public @interface RADAR_DIRECTION {
 
 }
 //默认为顺时针呢
 private final static int DEFAULT_DIERCTION=CLOCK_WISE;
 
 //设定雷达扫描方向
 private int direction=DEFAULT_DIERCTION;
 
 private boolean threadRunning = true;
 
 public RadarView(Context context, AttributeSet attrs) {
  super(context, attrs);
  // TODO Auto-generated constructor stub
  mContext = context;
  initPaint();
 }
 
 public RadarView(Context context) {
  super(context);
  // TODO Auto-generated constructor stub
  mContext = context;
  initPaint();
 
 }
 
 private void initPaint() {
  // TODO Auto-generated method stub
  setBackgroundColor(Color.TRANSPARENT);
 
  //宽度=5,抗锯齿,描边效果的白色画笔
  mPaintLine = new Paint();
  mPaintLine.setStrokeWidth(5);
  mPaintLine.setAntiAlias(true);
  mPaintLine.setStyle(Style.STROKE);
  mPaintLine.setColor(Color.WHITE);
 
  //宽度=5,抗锯齿,描边效果的浅绿色画笔
  mPaintCircle = new Paint();
  mPaintCircle.setStrokeWidth(5);
  mPaintCircle.setAntiAlias(true);
  mPaintCircle.setStyle(Style.FILL);
  mPaintCircle.setColor(0x99000000);
 
  //暗绿色的画笔
  mPaintSector = new Paint();
  mPaintSector.setColor(0x9D00ff00);
  mPaintSector.setAntiAlias(true);
  mShader = new SweepGradient(viewSize / 2, viewSize / 2, Color.TRANSPARENT, Color.GREEN);
  mPaintSector.setShader(mShader);
 
  //白色实心画笔
  mPaintPoint=new Paint();
  mPaintPoint.setColor(Color.WHITE);
  mPaintPoint.setStyle(Style.FILL);
 
  //随机生成的点,模拟雷达扫描结果
  point_x = UtilTools.Getrandomarray(15, 300);
  point_y = UtilTools.Getrandomarray(15, 300);
 
 }
 
 public void setViewSize(int size) {
  this.viewSize = size;
  setMeasuredDimension(viewSize, viewSize);
 }
 
 @Override
 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  // TODO Auto-generated method stub
  setMeasuredDimension(viewSize, viewSize);
 }
 
 public void start() {
  mThread = new ScanThread(this);
  mThread.setName("radar");
  mThread.start();
  threadRunning = true;
  isstart = true;
 }
 
 public void stop() {
  if (isstart) {
   threadRunning = false;
   isstart = false;
  }
 }
 
 @Override
 protected void onDraw(Canvas canvas) {
  // TODO Auto-generated method stub
  canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintCircle);
  canvas.drawCircle(viewSize / 2, viewSize / 2, 255, mPaintLine);
  canvas.drawCircle(viewSize / 2, viewSize / 2, 125, mPaintLine);
  canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintLine);
  //绘制两条十字线
  canvas.drawLine(viewSize / 2, 0, viewSize / 2, viewSize, mPaintLine);
  canvas.drawLine(0, viewSize / 2, viewSize, viewSize / 2, mPaintLine);
 
 
  //这里在雷达扫描过制定圆周度数后,将随机绘制一些白点,模拟搜索结果
  if (start > 100) {
   for (int i = 0; i < 2; i++) {
    canvas.drawCircle(viewSize / 2 + point_x[i], viewSize / 2 + point_y[i], 10, mPaintPoint);
   }
  }
  if (start > 200) {
   for (int i = 2; i < 5; i++) {
    canvas.drawCircle(viewSize / 2 + point_x[i], viewSize / 2 + point_y[i], 10, mPaintPoint);
   }
  }
  if (start > 300) {
   for (int i = 5; i < 9; i++) {
    canvas.drawCircle(viewSize / 2 + point_x[i], viewSize / 2 + point_y[i], 10, mPaintPoint);
   }
  }
  if (start > 500) {
   for (int i = 9; i < 11; i++) {
    canvas.drawCircle(viewSize / 2 + point_x[i], viewSize / 2 + point_y[i], 10, mPaintPoint);
   }
  }
  if (start > 800) {
   for (int i = 11; i < point_x.length; i++) {
    canvas.drawCircle(viewSize / 2 + point_x[i], viewSize / 2 + point_y[i], 10, mPaintPoint);
   }
  }
 
  //根据matrix中设定角度,不断绘制shader,呈现出一种扇形扫描效果
  canvas.concat(matrix);
  canvas.drawCircle(viewSize / 2, viewSize / 2, 350, mPaintSector);
  super.onDraw(canvas);
 }
 
 public void setDirection(@RADAR_DIRECTION int direction) {
  if (direction != CLOCK_WISE && direction != ANTI_CLOCK_WISE) {
   throw new IllegalArgumentException("Use @RADAR_DIRECTION constants only!");
  }
  this.direction = direction;
 }
 
 protected class ScanThread extends Thread {
 
  private RadarView view;
 
  public ScanThread(RadarView view) {
   // TODO Auto-generated constructor stub
   this.view = view;
  }
 
  @Override
  public void run() {
   // TODO Auto-generated method stub
   while (threadRunning) {
    if (isstart) {
     view.post(new Runnable() {
      public void run() {
       start = start + 1;
       matrix = new Matrix();
       //设定旋转角度,制定进行转转操作的圆心
//       matrix.postRotate(start, viewSize / 2, viewSize / 2);
//       matrix.setRotate(start,viewSize/2,viewSize/2);
       matrix.preRotate(direction*start,viewSize/2,viewSize/2);
       view.invalidate();
 
      }
     });
     try {
      Thread.sleep(5);
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
   }
  }
 }
}
登录后复制

说明

多余的部分就不再解释,代码里已经注释的很清楚。这个RadarView的使用也是很简单,需要停止时,调用其stop方法即可。

@Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  RadarView radarView = (RadarView) findViewById(R.id.radar);
  //设置雷达扫描方向
  radarView.setDirection(RadarView.ANTI_CLOCK_WISE);
  radarView.start();
 }
登录后复制

这里雷达ViewSize设置为800,所以在布局文件中设定大小时将不起作用,正常使用时,需根据实际需求调整viewsize大小和几个Circle的半径,从而达到更有好的UI展示效果。

总结

以上就是Android中雷达扫描效果实现的全部内容,希望本文对大家Android开发有所帮助。

更多Android动画之雷达扫描效果相关文章请关注PHP中文网!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

公司安全软件导致应用无法运行?如何排查和解决? 公司安全软件导致应用无法运行?如何排查和解决? Apr 19, 2025 pm 04:51 PM

公司安全软件导致部分应用无法正常运行的排查与解决方法许多公司为了保障内部网络安全,会部署安全软件。...

如何使用MapStruct简化系统对接中的字段映射问题? 如何使用MapStruct简化系统对接中的字段映射问题? Apr 19, 2025 pm 06:21 PM

系统对接中的字段映射处理在进行系统对接时,常常会遇到一个棘手的问题:如何将A系统的接口字段有效地映�...

如何优雅地获取实体类变量名构建数据库查询条件? 如何优雅地获取实体类变量名构建数据库查询条件? Apr 19, 2025 pm 11:42 PM

在使用MyBatis-Plus或其他ORM框架进行数据库操作时,经常需要根据实体类的属性名构造查询条件。如果每次都手动...

IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? IntelliJ IDEA是如何在不输出日志的情况下识别Spring Boot项目的端口号的? Apr 19, 2025 pm 11:45 PM

在使用IntelliJIDEAUltimate版本启动Spring...

如何将姓名转换为数字以实现排序并保持群组中的一致性? 如何将姓名转换为数字以实现排序并保持群组中的一致性? Apr 19, 2025 pm 11:30 PM

将姓名转换为数字以实现排序的解决方案在许多应用场景中,用户可能需要在群组中进行排序,尤其是在一个用...

Java对象如何安全地转换为数组? Java对象如何安全地转换为数组? Apr 19, 2025 pm 11:33 PM

Java对象与数组的转换:深入探讨强制类型转换的风险与正确方法很多Java初学者会遇到将一个对象转换成数组的�...

使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名构建查询条件? 使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名构建查询条件? Apr 19, 2025 pm 09:51 PM

在使用TKMyBatis进行数据库查询时,如何优雅地获取实体类变量名以构建查询条件,是一个常见的难题。本文将针...

如何利用Redis缓存方案高效实现产品排行榜列表的需求? 如何利用Redis缓存方案高效实现产品排行榜列表的需求? Apr 19, 2025 pm 11:36 PM

Redis缓存方案如何实现产品排行榜列表的需求?在开发过程中,我们常常需要处理排行榜的需求,例如展示一个�...

See all articles