之前在新浪微博看到一个雷达效果,可以查看图
先说说这个效果
1.程序默认提示点击雷达,开始探索
2.当点击雷达,提示正在探索周边的人,同时展示雷达扫描效果,即雷达按钮绕中心旋转,展现波纹达到扫描效果,
3.当触摸雷达时,雷达按钮会缩小,然后手指离开时,雷达按钮会缩回原位
4.最后如果扫描到周边的人,卡片显示周边人的信息,如果没有扫描到,则提示未能探索到周边的人,请稍后再试,同时关闭扫描效果
这里就不卡片展示周边人这个效果,毕竟是仿制,我们只需要关注动画效果,所以做个每两秒执行扫描效果一周,执行3遍显示为没有搜索到周边的人就行了
说说当中的技术点
1.首先是要熟悉canvas,paint,,其中canvas的画圆,画图片(之所以要熟悉画图片,是因为我们有四张图来显示成按钮,这四张图从新浪微博的app里获取,估计它也是这么干的),画文字等要熟悉。
2.雷达按钮绕中心旋转,这就要涉及到矩阵了,因为雷达按钮绕中心旋转,实际上也是对图片进行旋转操作,这样的话Matrix就比较合适了,至于旋转的角度怎么得到,用ValueAnimator比较合适,它可以在你设定的时间内,获取到你设定的两个值(这里指旋转角度,0,到360)之间的速率的变化值,然后刷新View的旋转角度就可以达到旋转的效果了
3.雷达按钮的缩放,原理同旋转,只不过我们关注的是缩放的比率而已
4.雷达的扫描效果,这个可以具体看看微博的效果,它是首先启动灰色波纹效果,没多久白色波纹效果与灰色效果同时抵达,就像波浪,后边的波浪把前一次波浪冲抵,当到达一定程度的时候显示灰色线波纹,达到波浪消失的效果,这里灰色波浪与白色波浪的冲抵效果,我用到了ValueAnimator里的Interpolator,其中分别为灰色波纹为减速DecelerateInterpolator,白色为AccelerateInterpolator,灰色线波纹为LinearInterpolator,一个减速,一个加速,达到追击的效果,一个线性,达到逐渐消失的效果
来看看我们的效果图,请查看附件:
gif录制效果不是很好,大家会给大家提供链接,自行下载查看效果
现在上自定义源码:
- package com.RadarScanView.app;
- import android.animation.Animator;
- import android.animation.AnimatorListenerAdapter;
- import android.animation.ValueAnimator;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Color;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.graphics.Rect;
- import android.os.Looper;
- import android.util.AttributeSet;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.DecelerateInterpolator;
- import android.view.animation.LinearInterpolator;
- /**
- * Created by Administrator on 2015/8/31.
- */
- public class RadarScanView extends View {
- //默认扫描图标
- private Bitmap mIconScanBitmap;
- //扫描时图标
- private Bitmap mIconScaningBitmap;
- //白色扫描图
- private Bitmap mScanBitmap;
- //黑色扫描背景
- private Bitmap mScanBackgroundBitmap;
- //扫描按钮区域
- private Rect mButtonArea = new Rect();
- //缩放矩阵
- private Matrix mScaleMatrix = new Matrix();
- //旋转矩阵
- private Matrix mRotateMatrix = new Matrix();
- //扫描图标旋转动画
- private ValueAnimator mRotateAnimator = new ValueAnimator();
- //手指点击时白色扫描图片缩小动画
- private ValueAnimator mScaleMinAnimator = new ValueAnimator();
- //手指放开时白色扫描图片放大动画
- private ValueAnimator mScaleMaxAnimator = new ValueAnimator();
- //扫描波纹灰色线动画
- private ValueAnimator mOutGrayAnimator = new ValueAnimator();
- //扫描波纹白色动画
- private ValueAnimator mInnerWhiteAnimator = new ValueAnimator();
- //扫描波纹灰色动画
- private ValueAnimator mBlackAnimator = new ValueAnimator();
- //画笔
- private Paint mOutGrayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Paint mInnerWhitePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Paint mBlackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- private Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
- //扫描图标旋转角度
- private float mRotateDegree;
- //缩放比例,默认1:1
- private float mScaleRatio = 1;
- //设定自定义View半径
- private int mRadius;
- //扫描波纹灰色线半径
- private float mOutGrayRadius = 0;
- //扫描波纹白色部分半径
- private float mInnerWhiteRadius = 0;
- //扫描波纹灰色部分半径
- private float mBlackRadius = 0;
- //默认扫描文字提示
- private String mTipText = "点击雷达,开始探索";
- //测量扫描文字提示边界
- private Rect mTextBound = new Rect();
- //是否点击按钮,默认没有点击
- private boolean isButtonClick = false;
- public RadarScanView(Context context) {
- this(context, null);
- }
- public RadarScanView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public RadarScanView(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- //加载图片
- mIconScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scan);
- mIconScaningBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_icon_scaning);
- mScanBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan);
- mScanBackgroundBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.radar_button_scan_background);
- //初始化画笔
- initViewPaint();
- }
- private void initViewPaint() {
- mOutGrayPaint.setStrokeWidth(4f);
- mOutGrayPaint.setColor(Color.rgb(228, 228, 228));
- mOutGrayPaint.setDither(true);
- mOutGrayPaint.setStyle(Paint.Style.STROKE);
- mInnerWhitePaint.setStrokeWidth(1f);
- mInnerWhitePaint.setColor(Color.rgb(241, 241, 241));
- mInnerWhitePaint.setDither(true);
- mInnerWhitePaint.setStyle(Paint.Style.FILL);
- mBlackPaint.setStrokeWidth(1f);
- mBlackPaint.setColor(Color.rgb(228, 228, 228));
- mBlackPaint.setDither(true);
- mBlackPaint.setStyle(Paint.Style.FILL);
- mTextPaint.setTextSize(37f);
- mTextPaint.setColor(Color.rgb(185, 185, 185));
- mTextPaint.setDither(true);
- mTextPaint.setStyle(Paint.Style.FILL);
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getActionMasked()) {
- case MotionEvent.ACTION_DOWN:
- //当按钮只有在图片即按钮区域内则认定为点击,不作点击
- isButtonClick = false;
- if (mButtonArea.contains((int) event.getX(), (int) event.getY())) {//手指按下,执行缩小动画
- if (!mScaleMinAnimator.isRunning() && !mScaleMaxAnimator.isRunning() && !mRotateAnimator.isRunning()) {//如果动画正在执行则不执行动画
- isButtonClick = true;
- //点击了按钮,启动白色图片缩小动画
- mScaleMinAnimator.start();
- }
- }
- break;
- case MotionEvent.ACTION_CANCEL:
- break;
- case MotionEvent.ACTION_MOVE:
- break;
- case MotionEvent.ACTION_UP:
- if (isButtonClick) {
- //当点击了按钮,启动白色图片放大动画与扫描图片旋转动画
- if (!mScaleMaxAnimator.isRunning()) {//如果动画正在执行则不执行动画
- mScaleMaxAnimator.start();
- }
- if (!mRotateAnimator.isRunning()) {//如果动画正在执行则不执行动画
- mRotateAnimator.start();
- }
- }
- break;
- }
- return true;
- }
- private void initScaleMinAnimator() {
- mScaleMinAnimator.setFloatValues(mRadius, mRadius * 0.87f);
- mScaleMinAnimator.setDuration(500);
- mScaleMinAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
- invalidateView();
- }
- });
- }
- private void initScaleMaxAnimator() {
- mScaleMaxAnimator.setFloatValues(mRadius * 0.87f, mRadius);
- mScaleMaxAnimator.setDuration(500);
- mScaleMaxAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mScaleRatio = ((Float) animation.getAnimatedValue()) / mRadius;
- invalidateView();
- }
- });
- }
- private void initRoateAnimator() {
- mRotateAnimator.setFloatValues(0, 360);
- mRotateAnimator.setDuration(2000);
- //重复三次,模仿正在扫描
- mRotateAnimator.setRepeatCount(3);
- mRotateAnimator.setInterpolator(new LinearInterpolator());
- mRotateAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mRotateDegree = (Float) animation.getAnimatedValue();
- invalidateView();
- }
- });
- mRotateAnimator.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationStart(Animator animation) {
- super.onAnimationStart(animation);
- mTipText = "正在探索周边的人...";
- //旋转动画启动后启动扫描波纹动画
- mOutGrayAnimator.start();
- mInnerWhiteAnimator.start();
- mBlackAnimator.start();
- }
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- //取消扫描波纹动画
- mOutGrayAnimator.cancel();
- mInnerWhiteAnimator.cancel();
- mBlackAnimator.cancel();
- //重置界面要素
- mOutGrayRadius = 0;
- mInnerWhiteRadius = 0;
- mBlackRadius = 0;
- mTipText = "未能探索到周边的人,请稍后再试";
- invalidateView();
- }
- });
- }
- private void initOutGrayAnimator() {
- mOutGrayAnimator.setFloatValues(mBlackRadius, getMeasuredWidth() / 2);
- mOutGrayAnimator.setDuration(1000);
- mOutGrayAnimator.setRepeatCount(-1);
- mOutGrayAnimator.setInterpolator(new LinearInterpolator());
- mOutGrayAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mOutGrayRadius = (Float) animation.getAnimatedValue();
- }
- });
- }
- private void initInnerWhiteAnimator() {
- mInnerWhiteAnimator.setFloatValues(0, getMeasuredWidth() / 3);
- mInnerWhiteAnimator.setDuration(1000);
- mInnerWhiteAnimator.setRepeatCount(-1);
- mInnerWhiteAnimator.setInterpolator(new AccelerateInterpolator());
- mInnerWhiteAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mInnerWhiteRadius = (Float) animation.getAnimatedValue();
- }
- });
- }
- private void initBlackAnimator() {
- mBlackAnimator.setFloatValues(0, getMeasuredWidth() / 3);
- mBlackAnimator.setDuration(1000);
- mBlackAnimator.setRepeatCount(-1);
- mBlackAnimator.setInterpolator(new DecelerateInterpolator());
- mBlackAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mBlackRadius = (Float) animation.getAnimatedValue();
- }
- });
- }
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- super.onSizeChanged(w, h, oldw, oldh);
- //扫描按钮区域,取自扫描灰色背景图片区域即可
- mButtonArea.set(getMeasuredWidth() / 2 - mScanBackgroundBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBackgroundBitmap.getHeight() / 2, getMeasuredWidth() / 2 +
- mScanBackgroundBitmap
- .getWidth() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2);
- //View半径,取自View宽高最小者
- mRadius = mScanBitmap.getWidth() / 2 > mScanBitmap.getHeight() / 2 ? mScanBitmap.getHeight() / 2 : mScanBitmap.getWidth() / 2;
- //初始化动画
- initScaleMinAnimator();
- initScaleMaxAnimator();
- initRoateAnimator();
- initBlackAnimator();
- initInnerWhiteAnimator();
- initOutGrayAnimator();
- }
- @Override
- protected void onDraw(Canvas canvas) {
- //绘制波纹
- canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mBlackRadius, mBlackPaint);
- canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mInnerWhiteRadius, mInnerWhitePaint);
- canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, mOutGrayRadius, mOutGrayPaint);
- //绘制背景
- Bitmap mScanBgBitmap = getScanBackgroundBitmap();
- if (mScanBgBitmap != null) {
- canvas.drawBitmap(mScanBgBitmap, getMeasuredWidth() / 2 - mScanBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBgBitmap.getHeight() / 2, new Paint(Paint
- .ANTI_ALIAS_FLAG));
- }
- //绘制按钮背景
- Bitmap mButtonBgBitmap = getButtonBackgroundBitmap();
- canvas.drawBitmap(mButtonBgBitmap, getMeasuredWidth() / 2 - mButtonBgBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mButtonBgBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
- //绘制扫描图片
- Bitmap mScanBitmap = getScanBitmap();
- canvas.drawBitmap(mScanBitmap, getMeasuredWidth() / 2 - mScanBitmap.getWidth() / 2, getMeasuredHeight() / 2 - mScanBitmap.getHeight() / 2, new Paint(Paint.ANTI_ALIAS_FLAG));
- //绘制文本提示
- mTextPaint.getTextBounds(mTipText, 0, mTipText.length(), mTextBound);
- //此处50为文本与按钮之间间隔,可以自己设定
- canvas.drawText(mTipText, getMeasuredWidth() / 2 - mTextBound.width() / 2, getMeasuredHeight() / 2 + mScanBackgroundBitmap.getHeight() / 2 + mTextBound.height() + 50, mTextPaint);
- }
- //绘制白色按钮背景,根据缩放矩阵与缩放比例,复制图片达到手指点击与手指放开时按钮的缩小与放大效果
- private Bitmap getButtonBackgroundBitmap() {
- mScaleMatrix.reset();
- mScaleMatrix.postScale(mScaleRatio, mScaleRatio);
- return Bitmap.createBitmap(mScanBitmap, 0, 0, mScanBitmap.getWidth(), mScanBitmap.getHeight(), mScaleMatrix, true);
- }
- //判断是否正在执行旋转动画,如果正在执行动画,则取消灰色白净
- private Bitmap getScanBackgroundBitmap() {
- if (mRotateAnimator.isRunning()) {
- return null;
- }
- return mScanBackgroundBitmap;
- }
- //判断是否正在执行动画,如果正在执行,根据旋转矩阵,与旋转的角度复制扫描图标,则实现图标不断旋转,如果未执行,则返回未扫描图片
- private Bitmap getScanBitmap() {
- if (mRotateAnimator.isRunning()) {
- mRotateMatrix.reset();
- mRotateMatrix.postRotate(mRotateDegree, mIconScaningBitmap.getWidth() / 2, mIconScaningBitmap.getHeight() / 2);
- return Bitmap.createBitmap(mIconScaningBitmap, 0, 0, mIconScaningBitmap.getWidth(), mIconScaningBitmap.getHeight(), mRotateMatrix, true);
- }
- return mIconScanBitmap;
- }
- //刷新View
- public void invalidateView() {
- if (Looper.getMainLooper() == Looper.myLooper()) {
- invalidate();
- } else {
- postInvalidate();
- }
- }
- }
复制代码
就整体感觉而言,个人感觉不是很好,不断刷新View对内存,CPU的性能有很高的要求,如果大家有好的思路,请大家共享一下,一起学习吧
|