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

需要的效果图

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

先来思路:

可以看到,这个动画是由圆和图片构成,中心图片画出来,然后根据中心图片的大小确定创建波纹时的半径,波纹的最大半径为当前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占据父控件的全部大小

效果如下:

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

。。。。以后应该能用上

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

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

  1. android实现喇叭播放效果图,自定义View 实现音频播放效果

    这已经是第二次实现这种效果了.记录下来. 首先看下效果图.这里就不搞gif 了 image.png 1.首先确定需要音频条得数量 2.根据大小计算出 间距和音频条得宽度 3.绘制初始样式 4.利用ha ...

  2. Android 自定义View 圆形圆角图片

    [Android 自定义View 圆形圆角图片] 基于Xfermode 实现 1.概述 在很久以前也写过一个利用Xfermode 实现圆形.圆角图片的(Android 完美实现图片圆角和圆形(对实现进 ...

  3. android波纹效果弹窗,Android自定义View实现波纹效果

    Android自定义View实现波纹效果 时间:2017-05-27     来源:移动互联网学院 1.引言:随着Android智能手机的普及,Android应用得到了大力支持,而Android应用的 ...

  4. android 高仿ios开关,Android自定义view仿IOS开关效果

    本文主要讲解如何在 Android 下实现高仿 iOS 的开关按钮,并非是在 Android 自带的 ToggleButton 上修改,而是使用 API 提供的 onDraw.onMeasure.Ca ...

  5. Android软件开发之盘点自定义View界面大合集(二)

    Android软件开发之盘点自定义View界面大合集(二) - 雨松MOMO的程序世界 - 51CTO技术博客 雨松MOMO带大家盘点Android 中的自定义View界面的绘制 今天我用自己写的一个 ...

  6. 自定义view圆形头像

    自定义view圆形头像 我已经将源码打包成依赖 朋友们可以直接导依赖直接使用 1.正常导入: implementation 'com.github.LiHangKun:wuyuewuone:1' im ...

  7. android 仿360浮动,Android仿360悬浮小球自定义view实现示例

    Android仿360悬浮小球自定义view实现示例 效果图如下: 实现当前这种类似的效果 和360小球 悬浮桌面差不错类似.这种效果是如何实现的呢.废话不多说 ,直接上代码. 1.新建工程,添加悬浮 ...

  8. Android中实现Bitmap在自定义View中的放大与拖动

    一基本实现思路: 基于View类实现自定义View –MyImageView类.在使用View的Activity类中完成OnTouchListener接口,实现对MotionEvent事件的监听与处理 ...

  9. android 图片圆角 遮罩_Android 自定义View练手Demo(一)实现圆角遮罩效果

    Android 自定义View系列文章 Android自定义View实现圆角遮罩效果 一图胜千言,有一个遮罩就会凸显出重点区域 1-1.jpg 本文通过两种方式来实现这种效果,来达到自定义View练手 ...

  10. Carson带你学Android:源码解析自定义View Draw过程

    前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...

最新文章

  1. 隐藏滚动条,保留鼠标滚动效果
  2. 如何在eclipse中自定义一个jar包名称
  3. BFS简单搜索--POJ 2243
  4. 系列TCP/IP协议-动态IP选路协议(008)
  5. 炼数成金数据分析课程---16、机器学习中的分类算法(交叉内容,后面要重点看)...
  6. 实验6 实践课程中的程序
  7. QObject::startTimer: Timers cannot be started from another thread
  8. 复合选择器-子选择器(HTML、CSS)
  9. C语言的标准内存分配函数
  10. think php5关联模型,thinkphp5 关联模型
  11. 室外定位篇:一文解读高精度RTK定位
  12. PostScript语言里的珠玑
  13. linux运行崩溃怎么定位,Linux 程序崩溃定位
  14. 摩斯代码在线html,HTML5 摩斯(Morse)电码生成器
  15. peoplesoft 日志
  16. 【机器人基础】阻抗/导纳控制深度解析
  17. mysql 代码书写,【单选题】mysql 的代码书写规则是() A. 不区分大小写 B. 必须全部大写 C. 必须全部小写 D. 以上答案都不对...
  18. Teamcenter8 更改料号所有者操作
  19. 工业控制系统安全评估流程威胁事件构建
  20. 第五章 面向对象(上)

热门文章

  1. devtools引发的一场关于类加载问题的探究
  2. 七段数码管数字时钟实时显示显示(年月日时分秒)
  3. 服务器充电桩是什么显示,服务区有充电桩吗,汽车充电桩介绍
  4. 出门不求人,自驾游必备的八款实用App推荐
  5. pytorch 单卡加载多卡模型module
  6. 网页瞬间转换成桌面应用级程序(IOS/Win/Linux)
  7. ssm 到spring boot 校园商铺 day3 配置ssm
  8. ps教程,教你做最简单最美丽的相片
  9. 面试中问到 有没有用过VUEX做过什么事情
  10. java 热词推荐搜索实现,一个热词推荐的简单实现