android圆形波纹按钮,android自定义View——圆形波纹扫描效果

蓝牙项目,考虑到后面可能会用到这个扫描的效果,所以参照大神写好的控件,增加了自己需要使用的接口。也顺便巩固一下自定义view中各种零碎的知识点。

4f82abbf1d08

需要的效果图

先放一个效果图,点击中心图片开始动画,再次点击结束动画:

4f82abbf1d08

先来思路:

可以看到,这个动画是由圆和图片构成,中心图片画出来,然后根据中心图片的大小确定创建波纹时的半径,波纹的最大半径为当前view的宽高较小的。

动画部分,主要使用runable延时 + ValueAnimator 实现波纹效果; 然后整体旋转。

代码来说话。

attrs中定义好需要在xml中设置的变量

部分成员变量注释,方便代码阅读

//波纹生成时的半径

private float mWaveRadiusMin;

//波纹消失前的半径

private float mWaveRadiusMax;

//每条波纹持续时间

private long mWaveDuration;

//波纹生成速度

private long mWaveCreatedSpeed;

private Paint mPaint;

//画笔是否为stroke模式(即线条)

private boolean stroke = false;

//中间图标画笔

private Paint mCenterBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

//中间图标区域

private Rect mCenterBitmapArea = new Rect();

//波纹颜色

private int mWaveColor;

//波纹动画效果

private Interpolator mInterpolator = new AccelerateInterpolator();

//所有的水波纹

private List mAnimatorList = new ArrayList<>();

//是否开启水波纹

private boolean mIsRuning = false;

//是否点击了中间图标

private boolean mIsCenterClick = false;

//中间的图标

private Bitmap mCenterBitmap;

//中间的圆形图标

private Bitmap mCenterCircleBitmap;

//旋转动画

private Animation operatingAnim;

构造方法

public WaveCircleView(Context context, AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,

R.styleable.WaveCircleView, 0, defStyleAttr);

for (int i = 0; i < typedArray.length(); i++) {

int attr = typedArray.getIndex(i);

switch (attr) {

case R.styleable.WaveCircleView_color:

mWaveColor = typedArray.getColor(attr, Color.BLUE);

break;

case R.styleable.WaveCircleView_image:

mCenterBitmap = BitmapFactory.decodeResource(getResources(),

typedArray.getResourceId(attr, R.mipmap.translate));

break;

case R.styleable.WaveCircleView_duration:

mWaveDuration = typedArray.getInteger(attr, 3000);

break;

case R.styleable.WaveCircleView_waveCreateSpeed:

mWaveCreatedSpeed = typedArray.getInteger(attr, 1000);

break;

case R.styleable.WaveCircleView_stroke:

stroke = typedArray.getBoolean(attr, false);

break;

}

}

typedArray.recycle();

mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mPaint.setStrokeWidth(3);

mPaint.setColor(mWaveColor);

mPaint.setDither(true);

if (stroke)//如果xml中设置为false,就把画笔属性设置为Stroke,最后的效果是线条

mPaint.setStyle(Paint.Style.STROKE);

else//填充效果

mPaint.setStyle(Paint.Style.FILL);

if (mCenterBitmap == null) {

mCenterBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.translate);

mWaveRadiusMin = Math.min(mCenterBitmap.getWidth(), mCenterBitmap.getHeight()) / 2;

}

mWaveRadiusMin = Math.min(mCenterBitmap.getWidth(), mCenterBitmap.getHeight()) / 2;

}

onDraw()中 ,先把中心图片渲染到画布

if (mCenterCircleBitmap == null) {

mCenterCircleBitmap = createCircleImage(mCenterBitmap, mCenterBitmap.getWidth());

}

canvas.drawBitmap(mCenterCircleBitmap, null, mCenterBitmapArea, mCenterBitmapPaint);

private Bitmap createCircleImage(Bitmap source, int min) {

final Paint paint = new Paint();

paint.setAntiAlias(true);

Bitmap target = Bitmap.createBitmap(min, min, Bitmap.Config.ARGB_8888);

//产生一个同样大小的画布

Canvas canvas = new Canvas(target);

//首先绘制圆形

canvas.drawCircle(min / 2, min / 2, min / 2, paint);

//使用SRC_IN

paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));

//绘制图片

canvas.drawBitmap(source, 0, 0, paint);

return target;

}

波纹效果

使用runable实现循环更新UI

private Runnable mWaveRunable = new Runnable() {

@Override

public void run() {

if (mIsRuning) {

createWaveAnimator();

invalidate();

//延时循环

postDelayed(mWaveRunable, mWaveCreatedSpeed);

}

}

};

private ValueAnimator createWaveAnimator() {

final ValueAnimator mWaveAnimator = new ValueAnimator();

mWaveAnimator.setFloatValues(mWaveRadiusMin, mWaveRadiusMax);

mWaveAnimator.setDuration(mWaveDuration);

mWaveAnimator.setRepeatCount(0);

mWaveAnimator.setInterpolator(mInterpolator);

mWaveAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

}

});

//将生成的ValueAnimator存储在list里

mAnimatorList.add(mWaveAnimator);

//开始动画

mWaveAnimator.start();

return mWaveAnimator;

}

onDraw()中绘制波纹,根据动画集合中存储的AnimatedValue 改变画笔透明度 和 canvas 时圆的radius,完成扩散渐隐效果

Iterator iterator = mAnimatorList.iterator();

while (iterator.hasNext()) {

ValueAnimator valueAnimator = iterator.next();

// Log.e("AnimatedValue",(float)valueAnimator.getAnimatedValue() + "mWaveRadiusMax:" + mWaveRadiusMax);

if (!valueAnimator.getAnimatedValue().equals(mWaveRadiusMax)) {

//设置透明度

mPaint.setAlpha(getAlpha((Float) valueAnimator.getAnimatedValue()));

//画水波纹

canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2, (Float) valueAnimator.getAnimatedValue(), mPaint);

} else {

valueAnimator.cancel();

iterator.remove();

}

}

getAlpha()方法:

private int getAlpha(float mRadius) {

int alpha = 1;

if (mWaveRadiusMax > 0) {

alpha = (int) ((1 - (mRadius - mWaveRadiusMin) / (mWaveRadiusMax - mWaveRadiusMin)) * 255);

}

return alpha;

}

最后,onTouchEvent()实现点击中心控制动画开关

@Override

public boolean onTouchEvent(MotionEvent event) {

switch (event.getActionMasked()) {

case MotionEvent.ACTION_DOWN:

//判断是否点击在中心图片范围内

mIsCenterClick = false;

//mCenterBitmapArea 方法在onSizeChanged()中初始化

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();

} else {

stop();

}

break;

}

return true;

}

@Override

protected void onSizeChanged(int w, int h, int oldw, int oldh) {

mWaveRadiusMax = Math.min(w, h) / 2;

//计算中间图标区域

mCenterBitmapArea.set((w - mCenterBitmap.getWidth()) / 2, (h - mCenterBitmap.getHeight()) / 2

, (w + mCenterBitmap.getWidth()) / 2, (h + mCenterBitmap.getHeight()) / 2);

}

public void start() {

if (!mIsRuning) {

mIsRuning = true;

mWaveRunable.run();

//旋转效果

operatingAnim = AnimationUtils.loadAnimation(getContext(), R.anim.roa);

LinearInterpolator lin = new LinearInterpolator();

operatingAnim.setInterpolator(lin);

operatingAnim.setDuration(mWaveDuration);

startAnimation(operatingAnim);

}

}

anim中定义的旋转动画以及使用

android:fromDegrees="0"

android:toDegrees="360"

android:repeatCount="-1"

android:pivotX="50%"

android:pivotY="50%" />

最后 , xml中测试

android:layout_width="wrap_content"

android:layout_height="wrap_content"

app:color="@color/colorAccent"

app:stroke="true"

app:duration="3000"

app:waveCreateSpeed="500"

app:image="@mipmap/bluetooth"

/>

由于没有写onMeasure(),所以wrap_content占据父控件的全部大小

效果如下:

4f82abbf1d08

当然,部分需要用到的属性,也通过getter 和setter暴露出去,方便java代码中灵活控制。

。。。。以后应该能用上

代码中可能还有笔者未发现的bug和暂时不想解决的bug。。。欢迎交流~~~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值