雷达效果,

来源:互联网 发布:怎么把淘宝店铺做起来 编辑:程序博客网 时间:2024/04/26 16:40

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的性能有很高的要求,如果大家有好的思路,请大家共享一下,一起学习吧

0 0
原创粉丝点击