欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android仿微信、QQ附近好友雷达扫描效果

程序员文章站 2024-03-03 20:44:10
1.概述   最近一直到在带实习生,因为人比较多,所以很长一段时间没有更新博客了,今天更新一篇雷达扫描附近好友效果,以后尽量每周更新一篇,先看一下效果: 2....

1.概述

  最近一直到在带实习生,因为人比较多,所以很长一段时间没有更新博客了,今天更新一篇雷达扫描附近好友效果,以后尽量每周更新一篇,先看一下效果:

Android仿微信、QQ附近好友雷达扫描效果

2.实现 

1、效果分析

效果分为两个部分,一个是上半部分的自定义radarview,还有就是下半部分的viewpager,至于怎么做到缩放和背景虚化的效果大家可以去看看lazyviewpager这里不详细介绍,这里主要实现扫描效果部分。

2、扫描效果实现

2.1自定义radarview在ondraw()方法中画六个圆圈,至于圆圈的半径是多少我们需要通过onmeasure(int widthmeasurespec, int heightmeasurespec)测量方法获取控件的宽高来确定圆的半径,每个圆的半径是宽度的1 / 13f, 2 / 13f, 3 / 13f, 4 / 13f, 5 / 13f, 6 / 13f,这只是自己测试出来感觉比较舒适的效果,下面请看代码:

//每个圆圈所占的比例
private static float[] circleproportion = {1 / 13f, 2 / 13f, 3 / 13f, 4 / 13f, 5 / 13f, 6 / 13f};
private paint mpaintcircle;//画圆需要用到的paint

public class radarview extends view {
 public radarview(context context) {
 this(context, null);
 }

 public radarview(context context, attributeset attrs) {
 this(context, attrs, 0);
 }

 public radarview(context context, attributeset attrs, int defstyleattr) {
 super(context, attrs, defstyleattr);
 init();
 }


 private void init() {
 mpaintcircle = new paint();
 mpaintcircle.setcolor(color.white);
 mpaintcircle.setantialias(true);
 }

 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 // 获取控件的宽高
 setmeasureddimension(measuresize(widthmeasurespec), measuresize(widthmeasurespec));
 mwidth = getmeasuredwidth();
 mheight = getmeasuredheight();
 mwidth = mheight = math.min(mwidth, mheight);
 }

 @override
 protected void ondraw(canvas canvas) {
 // 绘制六个白色圆圈
 drawcircle(canvas);
 }

 /**
 * 绘制圆线圈
 *
 * @param canvas
 */
 private void drawcircle(canvas canvas) {
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[1], mpaintline); // 绘制最小圆
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[1], mpaintline); // 绘制小圆
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[2], mpaintline); // 绘制中圆
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[3], mpaintline); // 绘制中大圆
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[4], mpaintline); // 绘制大圆
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[5], mpaintline); // 绘制大大圆
 }
}

2.2下面需要去画中间的用户图像,可以运行看看中间的六个圆圈有没有达到效果,这里就不看了直接在ondraw()方法中画中间图像:

 private bitmap centerbitmap;//最中间icon

 private void init(){
 // 通过bitmap工厂区获取用户图像的bitmap
 centerbitmap = bitmapfactory.decoderesource(getresources(), r.drawable.circle_photo);
 }

 @override
 protected void ondraw(canvas canvas) {
 drawcentericon(canvas);
 }

 /**
 * 绘制最中间的图标
 *
 * @param canvas
 */
 private void drawcentericon(canvas canvas) {
 int iconwidth = mwidth * circleproportion[0];
 canvas.drawbitmap(centerbitmap, 0,0,iconwidth ,iconwidth , null);
 }

2.3最后只需要实现这个扫描的效果这个控件基本就完成了,第一需要开启线程不断调用invalidate()去更新ondraw()方法,第二需要熟悉扫描渲染sweepgradient这个类,如果这两个都没问题那么大功告成:

private paint mpaintscan;//画扫描需要用到的paint
private matrix matrix = new matrix();//旋转需要的矩阵
private int mrotedegree;//扫描旋转的角度
private shader scanshader;//扫描渲染shader

public runnable run = new runnable() {
 @override
 public void run() {
  mrotedegree +=2;
  mrotematrix.postrotate(mrotedegree,cx,cy);
  invalidate();
  postdelayed(run,60);
 }
 };

@override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 //设置扫描渲染的shader
 scanshader = new sweepgradient(mwidth / 2, mheight / 2,
  new int[]{color.transparent, color.parsecolor("#84b5ca")}, null);
 }

 @override
 protected void ondraw(canvas canvas) {
 drawscan(canvas);
 }

 /**
 * 绘制扫描
 *
 * @param canvas
 */
 private void drawscan(canvas canvas) {
 canvas.save();
 mpaintscan.setshader(scanshader);
 canvas.concat(matrix);
 canvas.drawcircle(mwidth / 2, mheight / 2, mwidth * circleproportion[4], mpaintscan);
 canvas.restore();
 }


2.4.到这里我们来看一下扫描radarview的效果

Android仿微信、QQ附近好友雷达扫描效果

3. 实现添加数据效果radarviewgroup,我们的图像附近点需要加入viewgroup这里又需要自定义了,这里简单说一下自定viewgroup:
1).onmeasure()测量方法这里就不说了
2).只要搞清楚onlayout()方法是干嘛的就ok,viewgroup里面的子view都显示在什么位置就是写在这个方法里面的,换句话说有的隔得近有的隔得远都是由 child.layout(int l, int t, int r, int b)决定的,下面我们看一下代码:

 public class radarviewgroup extends viewgroup implements radarview.iscanninglistener {
 private int mwidth, mheight;//viewgroup的宽高
 private sparsearray<float> scananglelist = new sparsearray<>();//记录展示的item所在的扫描位置角度
 private sparsearray<info> mdatas;//数据源
 private int datalength;//数据源长度
 private int minitemposition;//最小距离的item所在数据源中的位置
 private circleview currentshowchild;//当前展示的item
 private circleview minshowchild;//最小距离的item
 private iradarclicklistener iradarclicklistener;//雷达图中点击监听circleview小圆点回调接口

 public void setiradarclicklistener(iradarclicklistener iradarclicklistener) {
 this.iradarclicklistener = iradarclicklistener;
 }

 public radarviewgroup(context context) {
 this(context, null);
 }

 public radarviewgroup(context context, attributeset attrs) {
 this(context, attrs, 0);
 }

 public radarviewgroup(context context, attributeset attrs, int defstyleattr) {
 super(context, attrs, defstyleattr);
 }


 @override
 protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
 setmeasureddimension(measuresize(widthmeasurespec), measuresize(heightmeasurespec));
 mwidth = getmeasuredwidth();
 mheight = getmeasuredheight();
 mwidth = mheight = math.min(mwidth, mheight);
 //测量每个children
 measurechildren(widthmeasurespec, heightmeasurespec);
 for (int i = 0; i < getchildcount(); i++) {
  view child = getchildat(i);
  if (child.getid() == r.id.id_scan_circle) {
  //为雷达扫描图设置需要的属性
  ((radarview) child).setscanninglistener(this);
  //考虑到数据没有添加前扫描图在扫描,但是不会开始为circleview布局
  if (mdatas != null && mdatas.size() > 0) {
   ((radarview) child).setmaxscanitemcount(mdatas.size());
   ((radarview) child).startscan();
  }
  continue;
  }
 }
 }

 @override
 protected void onlayout(boolean changed, int l, int t, int r, int b) {
 int childcount = getchildcount();
 //首先放置雷达扫描图
 view view = findviewbyid(r.id.id_scan_circle);
 if (view != null) {
  view.layout(0, 0, view.getmeasuredwidth(), view.getmeasuredheight());
 }
 //放置雷达图中需要展示的item圆点
 for (int i = 0; i < childcount; i++) {
  final int j = i;
  final view child = getchildat(i);
  if (child.getid() == r.id.id_scan_circle) {
  //如果不是circleview跳过
  continue;
  }
  //设置circleview小圆点的坐标信息
  //坐标 = 旋转角度 * 半径 * 根据远近距离的不同计算得到的应该占的半径比例
  ((circleview) child).setdisx((float) math.cos(math.toradians(scananglelist.get(i - 1) - 5))
   * ((circleview) child).getproportion() * mwidth / 2);
  ((circleview) child).setdisy((float) math.sin(math.toradians(scananglelist.get(i - 1) - 5))
   * ((circleview) child).getproportion() * mwidth / 2);
  //如果扫描角度记录sparsearray中的对应的item的值为0,
  // 说明还没有扫描到该item,跳过对该item的layout
  //(scananglelist设置数据时全部设置的value=0,
  // 当onscanning时,value设置的值始终不会0,具体可以看onscanning中的实现)
  if (scananglelist.get(i - 1) == 0) {
  continue;
  }
  //放置circle小圆点
  child.layout((int) ((circleview) child).getdisx() + mwidth / 2, (int) ((circleview) child).getdisy() + mheight / 2,
   (int) ((circleview) child).getdisx() + child.getmeasuredwidth() + mwidth / 2,
   (int) ((circleview) child).getdisy() + child.getmeasuredheight() + mheight / 2);
  //设置点击事件
  child.setonclicklistener(new onclicklistener() {
  @override
  public void onclick(view v) {
   resetanim(currentshowchild);
   currentshowchild = (circleview) child;
   //因为雷达图是childat(0),所以这里需要作-1才是正确的circle
   startanim(currentshowchild, j - 1);
   if (iradarclicklistener != null) {
   iradarclicklistener.onradaritemclick(j - 1);

   }
  }
  });
 }


 }

 private int measuresize(int measurespec) {
 int result = 0;
 int specmode = measurespec.getmode(measurespec);
 int specsize = measurespec.getsize(measurespec);
 if (specmode == measurespec.exactly) {
  result = specsize;
 } else {
  result = 300;
  if (specmode == measurespec.at_most) {
  result = math.min(result, specsize);
  }
 }
 return result;

 }

 /**
 * 设置数据
 *
 * @param mdatas
 */
 public void setdatas(sparsearray<info> mdatas) {
 this.mdatas = mdatas;
 datalength = mdatas.size();
 float min = float.max_value;
 float max = float.min_value;
 //找到距离的最大值,最小值对应的minitemposition
 for (int j = 0; j < datalength; j++) {
  info item = mdatas.get(j);
  if (item.getdistance() < min) {
  min = item.getdistance();
  minitemposition = j;
  }
  if (item.getdistance() > max) {
  max = item.getdistance();
  }
  scananglelist.put(j, 0f);
 }
 //根据数据源信息动态添加circleview
 for (int i = 0; i < datalength; i++) {
  circleview circleview = new circleview(getcontext());
  if (mdatas.get(i).getsex()) {
  circleview.setpaintcolor(getresources().getcolor(r.color.bg_color_pink));
  } else {
  circleview.setpaintcolor(getresources().getcolor(r.color.bg_color_blue));
  }
  //根据远近距离的不同计算得到的应该占的半径比例 0.312-0.832
  circleview.setproportion((mdatas.get(i).getdistance() / max + 0.6f) * 0.52f);
  if (minitemposition == i) {
  minshowchild = circleview;
  }
  addview(circleview);
 }
 }

 /**
 * 雷达图没有扫描完毕时回调
 *
 * @param position
 * @param scanangle
 */
 @override
 public void onscanning(int position, float scanangle) {
 if (scanangle == 0) {
  scananglelist.put(position, 1f);
 } else {
  scananglelist.put(position, scanangle);
 }
 requestlayout();
 }

 /**
 * 雷达图扫描完毕时回调
 */
 @override
 public void onscansuccess() {
 logutil.m("完成回调");
 resetanim(currentshowchild);
 currentshowchild = minshowchild;
 startanim(currentshowchild, minitemposition);
 }

 /**
 * 恢复circleview小圆点原大小
 *
 * @param object
 */
 private void resetanim(circleview object) {
 if (object != null) {
  object.clearportaiticon();
  objectanimator.offloat(object, "scalex", 1f).setduration(300).start();
  objectanimator.offloat(object, "scaley", 1f).setduration(300).start();
 }

 }

 /**
 * 放大circleview小圆点大小
 *
 * @param object
 * @param position
 */
 private void startanim(circleview object, int position) {
 if (object != null) {
  object.setportraiticon(mdatas.get(position).getportraitid());
  objectanimator.offloat(object, "scalex", 2f).setduration(300).start();
  objectanimator.offloat(object, "scaley", 2f).setduration(300).start();
 }
 }

 /**
 * 雷达图中点击监听circleview小圆点回调接口
 */
 public interface iradarclicklistener {
 void onradaritemclick(int position);
 }

 /**
 * 根据position,放大指定的circleview小圆点
 *
 * @param position
 */
 public void setcurrentshowitem(int position) {
 circleview child = (circleview) getchildat(position + 1);
 resetanim(currentshowchild);
 currentshowchild = child;
 startanim(currentshowchild, position);
 }
}

源码下载:http://xiazai.jb51.net/201611/yuanma/androidradarscan(jb51.net).rar

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。