之前在新浪微博看到一个雷达效果,可以查看图
先说说这个效果
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的性能有很高的要求,如果大家有好的思路,请大家共享一下,一起学习吧
|