概述
最近看到支付宝咻咻的页面就想模仿一下,话不多说,先看效果图。(录制的有点渣)
先说说这个效果:
1.点击中间图标开始搜索附近的人。
2.开始搜索后水波纹一圈圈的加速向外扩张。
3.搜索到人之后以一个圆圈显示在水波纹上。
技术点:
1.要熟悉canvas,paint,,其中canvas的画圆,画图片(主要用于中间圆形图片的绘制,小圆圈也要用圆形图片来绘制的,由于偷懒就用蓝圆点了)。
2.水波纹一圈圈的向外扩张,圆圈扩张的半径计算是利用ValueAnimator来获取的,其中加速扩张还是减速扩张等等这些效果通过Interpolator插值器来实现的,当然也可以自己自定义Interpolator来实现想要的效果。
3.小圆圈是通过自定义viewgroup的onlayout来定位的,这里实现的定位是通过随机的角度加上随机的半径来算出小圆圈所在的坐标,当然你也可以通过距离的远近或者其他来算出小圆圈的坐标。
水波纹效果WaveView
需要水波不停的一波波的向外扩张,所以需要不停的创建水波纹,这里利用了Runnable加上postDelayed来不停的创建水波纹。
private Runnable mWaveRunable = new Runnable() {
@Override
public void run() {
if (mIsRuning) {
newWaveAnimator();
invalidate();
postDelayed(mWaveRunable, mSpeed);
}
}
};
private ValueAnimator newWaveAnimator() {
final ValueAnimator mWaveAnimator = new ValueAnimator();
mWaveAnimator.setFloatValues(mMiniRadius, mMaxRadius);
mWaveAnimator.setDuration(mWaveDuration);
mWaveAnimator.setRepeatCount(0);
mWaveAnimator.setInterpolator(mInterpolator);
mWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
// (Float) animation.getAnimatedValue();
}
});
mAnimatorList.add(mWaveAnimator);
mWaveAnimator.start();
return mWaveAnimator;
}
绘制的时候遍历所有的水波纹,然后根据AnimatedValue来获取透明度和半径,当半径达到最大半径的时候删除水波纹,最后就是绘制中间的图标,然后每隔10ms的重新绘制水波纹(貌似10ms是属性动画的帧间隔时间)
@Override
protected void onDraw(Canvas canvas){
Iterator iterator = mAnimatorList.iterator();
while (iterator.hasNext()){
ValueAnimator valueAnimator = iterator.next();
Log.e("AnimatedValue",(float)valueAnimator.getAnimatedValue() + "mMaxRadius:" + mMaxRadius);
if (!valueAnimator.getAnimatedValue().equals(mMaxRadius)){
//设置透明度
mWavePaint.setAlpha(getAlpha((Float) valueAnimator.getAnimatedValue()));
//画水波纹
canvas.drawCircle(getMeasuredWidth() / 2,getMeasuredHeight() / 2, (Float) valueAnimator.getAnimatedValue(),mWavePaint);
}else{
valueAnimator.cancel();
iterator.remove();
}
}
//绘制中间图标
drawCenterBitmap(canvas);
if (mAnimatorList.size() > 0){
postInvalidateDelayed(10);
}
}
点击中间图标开启水波纹,是通过onTouchEvent来判断是否点击了中间的图标,如果点击了中间图标,水波纹又没开启的话,开启水波纹。
//中间图标区域
private Rect mCenterBitmapArea = new Rect();
//是否开启水波纹
private boolean mIsRuning = false;
//是否点击了中间图标
private boolean mIsCenterClick = false;
@Override
public boolean onTouchEvent(MotionEvent event){
switch (event.getActionMasked()){
case MotionEvent.ACTION_DOWN:
//当按钮只有在图片即按钮区域内则认定为点击,其他不作点击
mIsCenterClick = false;
if (mCenterBitmapArea.contains((int)event.getX(),(int)event.getY())){
mIsCenterClick = true;
}
break;
case MotionEvent.ACTION_CANCEL:
break;
case MotionEvent.ACTION_MOVE:
break;
case MotionEvent.ACTION_UP:
if (mIsCenterClick && !mIsRuning){
//当点击了按钮,启动水波纹
start();
}
break;
}
return true;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mMaxRadius = Math.min(w, h) / 2;
//计算中间图标区域
mCenterBitmapArea.set((w - mCenterBitmap.getWidth()) / 2,(h - mCenterBitmap.getHeight()) / 2
,(w + mCenterBitmap.getWidth()) / 2,(h + mCenterBitmap.getHeight()) / 2);
}
小圆圈WaveCircleView
小圆圈主要是记录x,y的坐标和画一个蓝色的圆圈。
public class WaveCircleView extends View {
//圆圈画笔
private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
//圆圈颜色
private int mColor = Color.BLUE;
//圆圈半径
private float radius = DisplayUtils.dp2px(getContext(),10);
//在wavelayout的角度
private int layoutX;
//在wavelayout的半径
private int layoutY;
public WaveCircleView(Context context) {
this(context,null);
}
public WaveCircleView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public WaveCircleView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint.setColor(mColor);
mPaint.setDither(true);
mPaint.setStyle(Paint.Style.FILL);
}
public int getLayoutX() {
return layoutX;
}
public void setLayoutX(int layoutX) {
this.layoutX = layoutX;
}
public int getLayoutY() {
return layoutY;
}
public void setLayoutY(int layoutY) {
this.layoutY = layoutY;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureSize(widthMeasureSpec), measureSize(heightMeasureSpec));
}
//测量圆圈宽高
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 = DisplayUtils.dp2px(getContext(),radius * 2);
if (specMode == MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
}
}
return result;
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawCircle(radius, radius, radius, mPaint);
}
}
WaveLayout
通过onlayout方法来定位水波纹和小圆圈的位置,首先吧水波纹全屏显示,然后根据随机角度和随机半径一个一个的定位小圆圈的位置。
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
float miniCircleRadius = 0;
//设置水波纹位置
waveView.layout(0, 0, getMeasuredWidth(), getMeasuredHeight());
miniCircleRadius = waveView.getMiniRadius();
int childCount = getChildCount();
//设置circleview
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
//如果不是Circleview跳过
if (!(child instanceof WaveCircleView)) {
continue;
}
WaveCircleView waveCircleView = (WaveCircleView) child;
if (waveCircleView.getLayoutX() == 0 && waveCircleView.getLayoutY() == 0) {
//随机获取角度
double angle = Math.random() * 360;
//随机获取半径,最小半径不能小于WaveView的最小波纹半径
double radians = miniCircleRadius + waveCircleView.getMeasuredWidth() / 2 +
Math.random() * (mWidth / 2 - miniCircleRadius - waveCircleView.getMeasuredWidth() / 2);
//获取WaveCircleView的x坐标
double x = getMeasuredWidth() / 2 + Math.sin(Math.toRadians(angle)) * radians;
//获取WaveCircleView的y坐标
double y = getMeasuredHeight() / 2 + Math.cos(Math.toRadians(angle)) * radians;
waveCircleView.setLayoutX((int) x);
waveCircleView.setLayoutY((int) y);
}
waveCircleView.layout(waveCircleView.getLayoutX(),waveCircleView.getLayoutY(),waveCircleView.getLayoutX() + waveCircleView.getMeasuredWidth(),waveCircleView.getLayoutY() + waveCircleView.getMeasuredHeight());
}
}
github下载地址:https://github.com/PeachBlossom/jiujiuwave