1.概述


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

2.实现 


  1. 效果分析
  效果分为两个部分,一个是上半部分的自定义RadarView,还有就是下半部分的ViewPager,至于怎么做到缩放和背景虚化的效果大家可以去看看LazyViewPager这里不详细介绍,也可以去慕课网看看http://www.imooc.com/view/226,这里主要实现扫描效果部分
  
  

  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;//画圆需要用到的paintpublic 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);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// 获取控件的宽高setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(widthMeasureSpec));mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();mWidth = mHeight = Math.min(mWidth, mHeight);}@Overrideprotected 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;//最中间iconprivate void init(){// 通过bitmap工厂区获取用户图像的bitmapcenterBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.circle_photo);}@Overrideprotected 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;//扫描渲染shaderpublic Runnable run = new Runnable() {@Overridepublic void run() {mRoteDegree +=2;mRoteMatrix.postRotate(mRoteDegree,cx,cy);invalidate();postDelayed(run,60);}};@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//设置扫描渲染的shaderscanShader = new SweepGradient(mWidth / 2, mHeight / 2,new int[]{Color.TRANSPARENT, Color.parseColor("#84B5CA")}, null);}@Overrideprotected 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<Float> scanAngleList = new SparseArray<>();//记录展示的item所在的扫描位置角度private SparseArray<Info> mDatas;//数据源private int dataLength;//数据源长度private int minItemPosition;//最小距离的item所在数据源中的位置private CircleView currentShowChild;//当前展示的itemprivate CircleView minShowChild;//最小距离的itemprivate 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);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec));mWidth = getMeasuredWidth();mHeight = getMeasuredHeight();mWidth = mHeight = Math.min(mWidth, mHeight);//测量每个childrenmeasureChildren(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;}}}@Overrideprotected 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() {@Overridepublic void onClick(View v) {resetAnim(currentShowChild);currentShowChild = (CircleView) child;//因为雷达图是childAt(0),所以这里需要作-1才是正确的CirclestartAnim(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;//找到距离的最大值,最小值对应的minItemPositionfor (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);}//根据数据源信息动态添加CircleViewfor (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.832circleView.setProportion((mDatas.get(i).getDistance() / max + 0.6f) * 0.52f);if (minItemPosition == i) {minShowChild = circleView;}addView(circleView);}}/*** 雷达图没有扫描完毕时回调** @param position* @param scanAngle*/@Overridepublic void onScanning(int position, float scanAngle) {if (scanAngle == 0) {scanAngleList.put(position, 1f);} else {scanAngleList.put(position, scanAngle);}requestLayout();}/*** 雷达图扫描完毕时回调*/@Overridepublic 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://download.csdn.net/detail/z240336124/9590748

微信QQ附近好友雷达扫描相关推荐

  1. android脚本 附近的人,Android仿微信、QQ附近好友雷达扫描效果

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

  2. QRCode 扫描二维码、扫描条形码、相册获取图片后识别、生成带 Logo 二维码、支持微博微信 QQ 二维码扫描样式...

    QRCode 扫描二维码.扫描条形码.相册获取图片后识别.生成带 Logo 二维码.支持微博微信 QQ 二维码扫描样式 参考链接:https://github.com/bingoogolapple/B ...

  3. 扫描二维码、扫描条形码、相册获取图片后识别、生成带 Logo 二维码、支持微博微信 QQ 二维码扫描样式

    GitHub项目的链接地址 目录 功能介绍 常见问题 效果图与示例 apk Gradle 依赖 布局文件 自定义属性说明 接口说明 关于我 功能介绍 ZXing 生成可自定义颜色.带 logo 的二维 ...

  4. ReactNative从相册选取二维码 进行扫描识别、扫描条形码、生成带 Logo 二维码、支持微博微信 QQ 二维码扫描样式

    我的demo 地址 http://github.com/yrjwcharm/react-native-scanner-qrcode 博友直接 下载压缩包 或者git clone 因为我的版本比较高.所 ...

  5. Ipad全线涨价;马斯克计划未来数月裁掉推特 75% 员工;支付宝已支持给微信QQ好友转账 | EA周报...

    EA周报 2022年10月21日 每个星期7分钟,元宝带你喝一杯IT人的浓缩咖啡,了解天下事.掌握IT核心技术. 周报看点 1.马斯克计划未来数月裁掉推特 75% 员工 2.新款 iPad Pro 售 ...

  6. 支付宝已支持给微信QQ好友转账

    我是卢松松,点点上面的头像,欢迎关注我哦! 支付宝一直想打开与微信支付之间的转账壁垒,这对用户来说是好事,但腾讯一直不予理会.这不,最近支付宝又做了一个功能:支付宝向已支持给微信QQ好友转账. 昨天看 ...

  7. 一个炫酷的仿雷达扫描和扩散效果——自定义View就是这么简单

    高仿雷达扫描效果和仿水波纹中心扩散效果,手把手教你撸一个炫酷的自定义view. 于亚豪的博客地址: blog.csdn.net/androidstar- 我们先看效果图吧 Markdown Markd ...

  8. 前端vue js 高德地图实现雷达扫描加载,借鉴百度地图等方法,采用Canvas解决雷达背景透明度问题,解决Canvas动态指针扫描造成浏览器卡顿问题

    前端vue js 高德地图实现雷达扫描加载,借鉴百度地图等方法,采用Canvas解决雷达背景透明度问题,解决Canvas动态指针扫描造成浏览器卡顿问题 经过3天的尝试,借鉴了好几个博客的思路,开发了一 ...

  9. 微信删除的好友如何找回来?3个妙招帮你解决

    案例:错删了微信好友怎么办? [怎么办?手贱不小心把好友删除了,怎么样可以把微信删除的好友加回来呀?] 微信是一个非常方便的聊天工具,不仅可以与朋友们进行沟通,还可以和好友们分享生活点滴.但有时候不小 ...

最新文章

  1. 弱电工程网络视频监控系统联网方式及接地要求
  2. 直接插入排序(Straight Insertion Sort)
  3. 了解如何使用Flutter构建iOS和Android应用
  4. linux php c 扩展,linux下编写php5.6的C扩展模块(双向链表)
  5. MySQL到MsSQL的迁移工具——SSMA
  6. 如何安装与连接MySQL?
  7. python数据分析之(7)简单绘图pylab
  8. 中软高科WEB前端面试题
  9. 微信小程序生成海报工具Painter
  10. UE使用EditorUtilityWidget完成简单的编辑器内工具
  11. 《Linux内核 学习笔记》--- 第二章 内存管理 2.9 mmap
  12. Lycoris Recoil再现!Unity实现Sakana~,代码思路解析,代码开源,Unity弹簧效果
  13. Learning a Deep Single Image Contrast Enhancer from Multi-Exposure Images阅读札记
  14. mysql的一些介绍
  15. jsp 将java对象转json对象 (自定义EL函数)
  16. 数据分析---论文数据统计
  17. 《统计学习方法》学习笔记 第二十一章 PageRank算法
  18. iptables 一键设置/清除脚本
  19. 计算机科学与技术高校学科评估排名,教育部06高校学科评估排名:0812 计算机科学与技术...
  20. NPC五电平逆变器。 并网逆变器PQ控制。 通过功率闭环控制,实现并网单位功率因数

热门文章

  1. 这10张图拿去,别再说学不会RecyclerView的缓存复用机制了!
  2. 基于HTML5+CSS+JacaScript和Java实现的校园校友APP前后台实现
  3. Vuex页面刷新数据丢失的问题
  4. VSCode——文件存在但出现FileNotFoundError错误的解决方法
  5. 【我为之而活的三种激情】by 伯特兰·罗素
  6. PS制作圆角icon
  7. 彩虹云任务新版引导页模板
  8. 【SQL刷题】Day9----SQL过滤数据专项练习
  9. 番外1:射频功放晶体管的重要参数
  10. 如何在谷歌云上快速搭建跨境电商平台?