热门标签 | HotTags
当前位置:  开发笔记 > Android > 正文

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

这篇文章主要为大家详细介绍了Android仿微信、QQ附近好友雷达扫描效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

1.概述

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

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的效果

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 scanAngleList = new SparseArray<>();//记录展示的item所在的扫描位置角度
 private SparseArray 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  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  mDatas) {
 this.mDatas = mDatas;
 dataLength = mDatas.size();
 float min = Float.MAX_VALUE;
 float max = Float.MIN_VALUE;
 //找到距离的最大值,最小值对应的minItemPosition
 for (int j = 0; j  max) {
  max = item.getDistance();
  }
  scanAngleList.put(j, 0f);
 }
 //根据数据源信息动态添加CircleView
 for (int i = 0; i 

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

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


推荐阅读
  • 华为捐赠欧拉操作系统,承诺不推商用版
    华为近日宣布将欧拉开源操作系统捐赠给开放原子开源基金会,并承诺不会推出欧拉的商用发行版。此举旨在推动欧拉和鸿蒙操作系统的全场景融合与生态发展。 ... [详细]
  • 本文介绍了多种开源数据库及其核心数据结构和算法,包括MySQL的B+树、MVCC和WAL,MongoDB的tokuDB和cola,boltDB的追加仅树和mmap,levelDB的LSM树,以及内存缓存中的一致性哈希。 ... [详细]
  • MySQL 数据库连接方法
    本文介绍了如何使用 MySQL 命令行工具连接到指定的数据库。 ... [详细]
  • 在 Mac 上配置 NDK
    本文详细介绍了如何在 Mac 上配置 Android NDK,包括设置环境变量和解决常见问题的方法。 ... [详细]
  • 如何解决8080端口被占用问题
    本文介绍了如何通过命令行和任务管理器查找并终止占用8080端口的进程,以确保该端口能够正常使用。 ... [详细]
  • Excel 数据分析基础
    Excel 是数据分析中最基本且强大的工具之一,具备多种实用功能和操作方法。本文将简要介绍 Excel 的不同版本及其兼容性问题,并探讨在处理大数据时的替代方案。 ... [详细]
  • 本文介绍了如何在 ASP.NET 中设置 Excel 单元格格式为文本,获取多个单元格区域并作为表头,以及进行单元格合并、赋值、格式设置等操作。 ... [详细]
  • LDAP服务器配置与管理
    本文介绍如何通过安装和配置SSSD服务来统一管理用户账户信息,并实现其他系统的登录调用。通过图形化交互界面配置LDAP服务器,确保用户账户信息的集中管理和安全访问。 ... [详细]
  • Android 自定义 RecycleView 左滑上下分层示例代码
    为了满足项目需求,需要在多个场景中实现左滑删除功能,并且后续可能在列表项中增加其他功能。虽然网络上有很多左滑删除的示例,但大多数封装不够完善。因此,我们尝试自己封装一个更加灵活和通用的解决方案。 ... [详细]
  • 如果应用程序经常播放密集、急促而又短暂的音效(如游戏音效)那么使用MediaPlayer显得有些不太适合了。因为MediaPlayer存在如下缺点:1)延时时间较长,且资源占用率高 ... [详细]
  • 本文详细介绍了 Charles 工具的下载、安装、配置及使用方法,特别针对 HTTP 和 HTTPS 协议的数据抓取进行了说明。 ... [详细]
  • 网络爬虫的规范与限制
    本文探讨了网络爬虫引发的问题及其解决方案,重点介绍了Robots协议的作用和使用方法,旨在为网络爬虫的合理使用提供指导。 ... [详细]
  • 本文介绍了 AngularJS 中的 $compile 服务及其用法,通过示例代码展示了如何使用 $compile 动态编译和链接 HTML 元素。 ... [详细]
  • javax.mail.search.BodyTerm.matchPart()方法的使用及代码示例 ... [详细]
  • 本文详细介绍了 Python 中 thread 和 threading 模块的使用方法,并提供了丰富的示例和解释。文章首发于 HURUWO 的博客小站,本平台进行同步备份发布。如遇图片加载失败或有任何疑问,欢迎前往原博客留言。 ... [详细]
author-avatar
FF小小女人
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有