雷达效果,

Android仿新浪微博雷达扫描效果

[复制链接]

   

MR.甲

签到天数: 4 天

连续签到: 1 天

[LV.2]偶尔看看I


3

主题

9

帖子

151

e币

电梯直达

楼主

 发表于
2015-8-31 15:18:14 | 只看该作者 

之前在新浪微博看到一个雷达效果,可以查看图

先说说这个效果

1.程序默认提示点击雷达,开始探索

2.当点击雷达,提示正在探索周边的人,同时展示雷达扫描效果,即雷达按钮绕中心旋转,展现波纹达到扫描效果,

3.当触摸雷达时,雷达按钮会缩小,然后手指离开时,雷达按钮会缩回原位

4.最后如果扫描到周边的人,卡片显示周边人的信息,如果没有扫描到,则提示未能探索到周边的人,请稍后再试,同时关闭扫描效果

这里就不卡片展示周边人这个效果,毕竟是仿制,我们只需要关注动画效果,所以做个每两秒执行扫描效果一周,执行3遍显示为没有搜索到周边的人就行了

说说当中的技术点

1.首先是要熟悉canvas,paint,,其中canvas的画圆,画图片(之所以要熟悉画图片,是因为我们有四张图来显示成按钮,这四张图从新浪微博的app里获取,估计它也是这么干的),画文字等要熟悉。

2.雷达按钮绕中心旋转,这就要涉及到矩阵了,因为雷达按钮绕中心旋转,实际上也是对图片进行旋转操作,这样的话Matrix就比较合适了,至于旋转的角度怎么得到,用ValueAnimator比较合适,它可以在你设定的时间内,获取到你设定的两个值(这里指旋转角度,0,到360)之间的速率的变化值,然后刷新View的旋转角度就可以达到旋转的效果了

3.雷达按钮的缩放,原理同旋转,只不过我们关注的是缩放的比率而已

4.雷达的扫描效果,这个可以具体看看微博的效果,它是首先启动灰色波纹效果,没多久白色波纹效果与灰色效果同时抵达,就像波浪,后边的波浪把前一次波浪冲抵,当到达一定程度的时候显示灰色线波纹,达到波浪消失的效果,这里灰色波浪与白色波浪的冲抵效果,我用到了ValueAnimator里的Interpolator,其中分别为灰色波纹为减速DecelerateInterpolator,白色为AccelerateInterpolator,灰色线波纹为LinearInterpolator,一个减速,一个加速,达到追击的效果,一个线性,达到逐渐消失的效果

来看看我们的效果图,请查看附件:

gif录制效果不是很好,大家会给大家提供链接,自行下载查看效果

现在上自定义源码:

  1. package com.RadarScanView.app;
  2. import android.animation.Animator;
  3. import android.animation.AnimatorListenerAdapter;
  4. import android.animation.ValueAnimator;
  5. import android.content.Context;
  6. import android.graphics.Bitmap;
  7. import android.graphics.BitmapFactory;
  8. import android.graphics.Canvas;
  9. import android.graphics.Color;
  10. import android.graphics.Matrix;
  11. import android.graphics.Paint;
  12. import android.graphics.Rect;
  13. import android.os.Looper;
  14. import android.util.AttributeSet;
  15. import android.view.MotionEvent;
  16. import android.view.View;
  17. import android.view.animation.AccelerateInterpolator;
  18. import android.view.animation.DecelerateInterpolator;
  19. import android.view.animation.LinearInterpolator;
  20. /**
  21. * Created by Administrator on 2015/8/31.
  22. */
  23. public class RadarScanView extends View {
  24. //默认扫描图标
  25. private Bitmap mIconScanBitmap;
  26. //扫描时图标
  27. private Bitmap mIconScaningBitmap;
  28. //白色扫描图
  29. private Bitmap mScanBitmap;
  30. //黑色扫描背景
  31. private Bitmap mScanBackgroundBitmap;
  32. //扫描按钮区域
  33. private Rect mButtonArea = new Rect();
  34. //缩放矩阵
  35. private Matrix mScaleMatrix = new Matrix();
  36. //旋转矩阵
  37. private Matrix mRotateMatrix = new Matrix();
  38. //扫描图标旋转动画
  39. private ValueAnimator mRotateAnimator = new ValueAnimator();
  40. //手指点击时白色扫描图片缩小动画
  41. private ValueAnimator mScaleMinAnimator = new ValueAnimator();
  42. //手指放开时白色扫描图片放大动画
  43. private ValueAnimator mScaleMaxAnimator = new ValueAnimator();
  44. //扫描波纹灰色线动画
  45. private ValueAnimator mOutGrayAnimator = new ValueAnimator();
  46. //扫描波纹白色动画
  47. private ValueAnimator mInnerWhiteAnimator = new ValueAnimator();
  48. //扫描波纹灰色动画
  49. private ValueAnimator mBlackAnimator = new ValueAnimator();
  50. //画笔
  51. private Paint mOutGrayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  52. private Paint mInnerWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  53. private Paint mBlackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  54. private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
  55. //扫描图标旋转角度
  56. private float mRotateDegree;
  57. //缩放比例,默认1:1
  58. private float mScaleRatio = 1;
  59. //设定自定义View半径
  60. private int mRadius;
  61. //扫描波纹灰色线半径
  62. private float mOutGrayRadius = 0;
  63. //扫描波纹白色部分半径
  64. private float mInnerWhiteRadius = 0;
  65. //扫描波纹灰色部分半径
  66. private float mBlackRadius = 0;
  67. //默认扫描文字提示
  68. private String mTipText = "点击雷达,开始探索";
  69. //测量扫描文字提示边界
  70. private Rect mTextBound = new Rect();
  71. //是否点击按钮,默认没有点击
  72. private boolean isButtonClick = false;
  73. public RadarScanView(Context context) {
  74. this(context, null);
  75. }
  76. public RadarScanView(Context context, AttributeSet attrs) {
  77. this(context, attrs, 0);
  78. }
  79. public RadarScanView(Context context, AttributeSet attrs, int defStyleAttr) {
  80. super(context, attrs, defStyleAttr);
  81. //加载图片
  82. mIconScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scan);
  83. mIconScaningBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scaning);
  84. mScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan);
  85. mScanBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan_background);
  86. //初始化画笔
  87. initViewPaint();
  88. }
  89. private void initViewPaint() {
  90. mOutGrayPaint.setStrokeWidth(4f);
  91. mOutGrayPaint.setColor(Color.rgb(228, 228, 228));
  92. mOutGrayPaint.setDither(true);
  93. mOutGrayPaint.setStyle(Paint.Style.STROKE);
  94. mInnerWhitePaint.setStrokeWidth(1f);
  95. mInnerWhitePaint.setColor(Color.rgb(241, 241, 241));
  96. mInnerWhitePaint.setDither(true);
  97. mInnerWhitePaint.setStyle(Paint.Style.FILL);
  98. mBlackPaint.setStrokeWidth(1f);
  99. mBlackPaint.setColor(Color.rgb(228, 228, 228));
  100. mBlackPaint.setDither(true);
  101. mBlackPaint.setStyle(Paint.Style.FILL);
  102. mTextPaint.setTextSize(37f);
  103. mTextPaint.setColor(Color.rgb(185, 185, 185));
  104. mTextPaint.setDither(true);
  105. mTextPaint.setStyle(Paint.Style.FILL);
  106. }
  107. @Override
  108. public boolean onTouchEvent(MotionEvent event) {
  109. switch (event.getActionMasked()) {
  110. case MotionEvent.ACTION_DOWN:
  111. //当按钮只有在图片即按钮区域内则认定为点击,不作点击
  112. isButtonClick = false;
  113. if (mButtonArea.contains((int) event.getX(), (int) event.getY())) {//手指按下,执行缩小动画
  114. if (!mScaleMinAnimator.isRunning() && !mScaleMaxAnimator.isRunning() && !mRotateAnimator.isRunning()) {//如果动画正在执行则不执行动画
  115. isButtonClick = true;
  116. //点击了按钮,启动白色图片缩小动画
  117. mScaleMinAnimator.start();
  118. }
  119. }
  120. break;
  121. case MotionEvent.ACTION_CANCEL:
  122. break;
  123. case MotionEvent.ACTION_MOVE:
  124. break;
  125. case MotionEvent.ACTION_UP:
  126. if (isButtonClick) {
  127. //当点击了按钮,启动白色图片放大动画与扫描图片旋转动画
  128. if (!mScaleMaxAnimator.isRunning()) {//如果动画正在执行则不执行动画
  129. mScaleMaxAnimator.start();
  130. }
  131. if (!mRotateAnimator.isRunning()) {//如果动画正在执行则不执行动画
  132. mRotateAnimator.start();
  133. }
  134. }
  135. break;
  136. }
  137. return true;
  138. }
  139. private void initScaleMinAnimator() {
  140. mScaleMinAnimator.setFloatValues(mRadius, mRadius * 0.87f);
  141. mScaleMinAnimator.setDuration(500);
  142. mScaleMinAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  143. @Override
  144. public void onAnimationUpdate(ValueAnimator animation) {
  145. mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
  146. invalidateView();
  147. }
  148. });
  149. }
  150. private void initScaleMaxAnimator() {
  151. mScaleMaxAnimator.setFloatValues(mRadius * 0.87f, mRadius);
  152. mScaleMaxAnimator.setDuration(500);
  153. mScaleMaxAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  154. @Override
  155. public void onAnimationUpdate(ValueAnimator animation) {
  156. mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
  157. invalidateView();
  158. }
  159. });
  160. }
  161. private void initRoateAnimator() {
  162. mRotateAnimator.setFloatValues(0, 360);
  163. mRotateAnimator.setDuration(2000);
  164. //重复三次,模仿正在扫描
  165. mRotateAnimator.setRepeatCount(3);
  166. mRotateAnimator.setInterpolator(new LinearInterpolator());
  167. mRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  168. @Override
  169. public void onAnimationUpdate(ValueAnimator animation) {
  170. mRotateDegree = (Float) animation.getAnimatedValue();
  171. invalidateView();
  172. }
  173. });
  174. mRotateAnimator.addListener(new AnimatorListenerAdapter() {
  175. @Override
  176. public void onAnimationStart(Animator animation) {
  177. super.onAnimationStart(animation);
  178. mTipText = "正在探索周边的人...";
  179. //旋转动画启动后启动扫描波纹动画
  180. mOutGrayAnimator.start();
  181. mInnerWhiteAnimator.start();
  182. mBlackAnimator.start();
  183. }
  184. @Override
  185. public void onAnimationEnd(Animator animation) {
  186. super.onAnimationEnd(animation);
  187. //取消扫描波纹动画
  188. mOutGrayAnimator.cancel();
  189. mInnerWhiteAnimator.cancel();
  190. mBlackAnimator.cancel();
  191. //重置界面要素
  192. mOutGrayRadius = 0;
  193. mInnerWhiteRadius = 0;
  194. mBlackRadius = 0;
  195. mTipText = "未能探索到周边的人,请稍后再试";
  196. invalidateView();
  197. }
  198. });
  199. }
  200. private void initOutGrayAnimator() {
  201. mOutGrayAnimator.setFloatValues(mBlackRadius, getMeasuredWidth() / 2);
  202. mOutGrayAnimator.setDuration(1000);
  203. mOutGrayAnimator.setRepeatCount(-1);
  204. mOutGrayAnimator.setInterpolator(new LinearInterpolator());
  205. mOutGrayAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  206. @Override
  207. public void onAnimationUpdate(ValueAnimator animation) {
  208. mOutGrayRadius = (Float) animation.getAnimatedValue();
  209. }
  210. });
  211. }
  212. private void initInnerWhiteAnimator() {
  213. mInnerWhiteAnimator.setFloatValues(0, getMeasuredWidth() / 3);
  214. mInnerWhiteAnimator.setDuration(1000);
  215. mInnerWhiteAnimator.setRepeatCount(-1);
  216. mInnerWhiteAnimator.setInterpolator(new AccelerateInterpolator());
  217. mInnerWhiteAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  218. @Override
  219. public void onAnimationUpdate(ValueAnimator animation) {
  220. mInnerWhiteRadius = (Float) animation.getAnimatedValue();
  221. }
  222. });
  223. }
  224. private void initBlackAnimator() {
  225. mBlackAnimator.setFloatValues(0, getMeasuredWidth() / 3);
  226. mBlackAnimator.setDuration(1000);
  227. mBlackAnimator.setRepeatCount(-1);
  228. mBlackAnimator.setInterpolator(new DecelerateInterpolator());
  229. mBlackAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
  230. @Override
  231. public void onAnimationUpdate(ValueAnimator animation) {
  232. mBlackRadius = (Float) animation.getAnimatedValue();
  233. }
  234. });
  235. }
  236. @Override
  237. protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  238. super.onSizeChanged(w, h, oldw, oldh);
  239. //扫描按钮区域,取自扫描灰色背景图片区域即可
  240. mButtonArea.set(getMeasuredWidth() / 2 - mScanBackgroundBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBackgroundBitmap.getHeight() / 2, getMeasuredWidth() / 2 +
  241. mScanBackgroundBitmap
  242. .getWidth() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2);
  243. //View半径,取自View宽高最小者
  244. mRadius = mScanBitmap.getWidth() / 2 > mScanBitmap.getHeight() / 2 ? mScanBitmap.getHeight() / 2 : mScanBitmap.getWidth() / 2;
  245. //初始化动画
  246. initScaleMinAnimator();
  247. initScaleMaxAnimator();
  248. initRoateAnimator();
  249. initBlackAnimator();
  250. initInnerWhiteAnimator();
  251. initOutGrayAnimator();
  252. }
  253. @Override
  254. protected void onDraw(Canvas canvas) {
  255. //绘制波纹
  256. canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mBlackRadius, mBlackPaint);
  257. canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mInnerWhiteRadius, mInnerWhitePaint);
  258. canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mOutGrayRadius, mOutGrayPaint);
  259. //绘制背景
  260. Bitmap mScanBgBitmap = getScanBackgroundBitmap();
  261. if (mScanBgBitmap != null) {
  262. canvas.drawBitmap(mScanBgBitmap, getMeasuredWidth() / 2 - mScanBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBgBitmap.getHeight() / 2, new Paint(Paint
  263. .ANTI_ALIAS_FLAG));
  264. }
  265. //绘制按钮背景
  266. Bitmap mButtonBgBitmap = getButtonBackgroundBitmap();
  267. canvas.drawBitmap(mButtonBgBitmap, getMeasuredWidth() / 2 - mButtonBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mButtonBgBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
  268. //绘制扫描图片
  269. Bitmap mScanBitmap = getScanBitmap();
  270. canvas.drawBitmap(mScanBitmap, getMeasuredWidth() / 2 - mScanBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
  271. //绘制文本提示
  272. mTextPaint.getTextBounds(mTipText, 0, mTipText.length(), mTextBound);
  273. //此处50为文本与按钮之间间隔,可以自己设定
  274. canvas.drawText(mTipText, getMeasuredWidth() / 2 - mTextBound.width() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2 + mTextBound.height() + 50, mTextPaint);
  275. }
  276. //绘制白色按钮背景,根据缩放矩阵与缩放比例,复制图片达到手指点击与手指放开时按钮的缩小与放大效果
  277. private Bitmap getButtonBackgroundBitmap() {
  278. mScaleMatrix.reset();
  279. mScaleMatrix.postScale(mScaleRatio, mScaleRatio);
  280. return Bitmap.createBitmap(mScanBitmap, 0, 0, mScanBitmap.getWidth(), mScanBitmap.getHeight(), mScaleMatrix, true);
  281. }
  282. //判断是否正在执行旋转动画,如果正在执行动画,则取消灰色白净
  283. private Bitmap getScanBackgroundBitmap() {
  284. if (mRotateAnimator.isRunning()) {
  285. return null;
  286. }
  287. return mScanBackgroundBitmap;
  288. }
  289. //判断是否正在执行动画,如果正在执行,根据旋转矩阵,与旋转的角度复制扫描图标,则实现图标不断旋转,如果未执行,则返回未扫描图片
  290. private Bitmap getScanBitmap() {
  291. if (mRotateAnimator.isRunning()) {
  292. mRotateMatrix.reset();
  293. mRotateMatrix.postRotate(mRotateDegree, mIconScaningBitmap.getWidth() / 2, mIconScaningBitmap.getHeight() / 2);
  294. return Bitmap.createBitmap(mIconScaningBitmap, 0, 0, mIconScaningBitmap.getWidth(), mIconScaningBitmap.getHeight(), mRotateMatrix, true);
  295. }
  296. return mIconScanBitmap;
  297. }
  298. //刷新View
  299. public void invalidateView() {
  300. if (Looper.getMainLooper() == Looper.myLooper()) {
  301. invalidate();
  302. } else {
  303. postInvalidate();
  304. }
  305. }
  306. }

复制代码

就整体感觉而言,个人感觉不是很好,不断刷新View对内存,CPU的性能有很高的要求,如果大家有好的思路,请大家共享一下,一起学习吧

时间: 2024-10-06 16:22:17

雷达效果,的相关文章

利用Render Texture实现游戏的小雷达效果(摄影机分屏)

最近游戏蛮牛在举办一个活动,就是要做出这样的效果: 题目:实现游戏分屏效果 要求:1.        分屏,且分割线不规则(即非水平或垂直):2.        各屏可单独操作(移动.缩放),操作指该屏整体操作:3.        左(或上)立方体顺时针自转:右(或下)立方体逆时针自转: 一开始以为又要动用我最不熟悉的Shader了,有点怕,后来仔细一想,这不就是跟雷达效果一样的么? 然后立马动手,做一个效果玩玩. 原理很简单,就是弄两个摄影机,一个作为Player的子物体,并放在Player背

360手机卫士---扫描杀雷达效果

activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <LinearLayo

iOS 实现类似雷达效果的核心代码

1 -(void)drawRect:(CGRect)rect 2 { 3 [[UIColor clearColor]setFill]; 4 UIRectFill(rect); 5 NSInteger pulsingCount = 3; 6 double animationDuration = 2; 7 8 CALayer * animationLayer = [[CALayer alloc]init]; 9 self.animationLayer = animationLayer; 10 11

雷达效果

@interface ViewController () { CALayer *_layer; CAAnimationGroup *_animaTionGroup; CADisplayLink *_disPlayLink; } @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typica

Launcher定制之 应用分发--高仿APUS桌面,上拉加载应用分发雷达效果

直接上代码, 需要图片资源请到APUS里面去提取. xml: <?xml version="1.0" encoding="utf-8"?> <merge android:layout_width="fill_parent" android:layout_height="100.0dip" xmlns:android="http://schemas.android.com/apk/res/androi

【canvas系列】canvas实现&quot;雷达扫描&quot;效果

今天来讲解"雷达扫描"效果demo,来源于QQ群里边有群友说想要个雷达效果,就尝试写了一下. 效果图: demo链接: https://win7killer.github.io/can_demo/demo/radar.html ******************************************************************** 这个东西,背景圆,坐标.圆圈都很简单实现,arc结合moveTo.lineTo就可以解决,背景色也不是问题,一句带过. 那么

[翻译]Android 5.0之应用中实现材料设计—Material Design

上午的时候在刷Google+,看到了Abraham Williams转发了一篇强文,是Android Developers网站新发的一篇博客—Implementing Material Design in your Android App.觉得很前卫,对于新发布的Android版本号Android 5.0是一个很好的学习和了解的机会,所以就花了些时间把它翻译了下来,希望对自己.对其它人有所启发. 因为翻译Android开发博客和API也只是业余爱好,水平有限,其中不免有不准确的地方,所以把原文地

Android开发优化建议

1.找一些与你想开发的功能类似的代码 2.调整它,尝试让它变成你想要的 3.回顾开发中遇到的问题 4.使用StackOverflow来解决遇到的问题 对每个你想实现的东西重复上述过程.采用这种方法能够激励你,因为你在保持不断迭代更新,在这个过程里面你会学到很多.当然,当你发布应用的时候你还要去做一些更深入的东西. 从一些能够正常编译的代码到成为一个应用程序,这是一个质的飞跃,比起iOS,Android则表现的更加明显.当iOS应用发布的时候,实际上只是在一种设备之间跳跃,对iOS很多机型而言都很

通用窗口类 Inventory Pro 2.1.2 Demo1(下续篇 ),物品消耗扇形显示功能

本篇想总结的是Inventory Pro中通用窗口的具体实现,但还是要强调下该插件的重点还是装备系统而不是通用窗口系统,所以这里提到的通用窗口类其实是通用装备窗口类(其实该插件中也有非装备窗口比如NoticeUI等). 本篇涉及的功能用加出标出,具体的功能如下: 1.实现了两个窗口,通过点击键盘I来,打开或者关闭窗口也就是Toggle功能 2.装备窗口中的物品栏空格数量动态生成可控,可以在属性窗口手动配置 3.窗口具有拖拽功能 4.窗口物品具有拖拽,及窗口间拖拽 5.可以在窗口使用物品的功能,物