自定义View之圆形水波扩散动效
这个效果做出来以后,真的美极了!放在你的应用中,无疑增添了光彩!
效果图
其实,第一种效果,才是产品的需求要的效果。第三种效果,是不是很熟悉?支付宝的咻一咻!哈哈,无意中,我就写出来了。
实现步骤
1.attrs.xml定义属性
<declare-styleable name="WaveView">
<!--圆颜色-->
<attr name="wave_color" format="color"/>
<!--中心圆图片半径-->
<attr name="wave_coreImageRadius" format="integer"/>
<!--波浪圆之间间距,值越小越窄-->
<attr name="wave_width" format="integer"/>
</declare-styleable>
2.WaveView的初始化
/**
* 波浪圆圈颜色
*/
private int mColor = getResources().getColor(R.color.yellow);
/**
* 第一个圆圈的半径(也就是圆形图片的半径)
*/
private int mImageRadius=50;
/**
* 波浪圆之间间距
*/
private int mWidth = 3;
/**
* 最大宽度
*/
private Integer mMaxRadius = 300;
/**
* 是否正在扩散中
*/
private boolean mIsWave = false;
// 透明度集合
private List<Integer> mAlphas = new ArrayList<>();
// 扩散圆半径集合
private List<Integer> mRadius = new ArrayList<>();
private Paint mPaint;
public WaveView(Context context) {
this(context, null);
}
public WaveView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public WaveView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView, defStyleAttr, 0);
mColor = a.getColor(R.styleable.WaveView_wave_color, mColor);
mWidth = a.getInt(R.styleable.WaveView_wave_width, mWidth);
mImageRadius = a.getInt(R.styleable.WaveView_wave_coreImageRadius, mImageRadius);
a.recycle();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
// mAlphas.add(255);
// mRadius.add(0);
}
3.重写onDraw()
@Override
public void onDraw(Canvas canvas) {
// 绘制扩散圆
mPaint.setColor(mColor);
for (int i = 0; i < mAlphas.size(); i++) {
// 设置透明度
Integer alpha = mAlphas.get(i);
mPaint.setAlpha(alpha);
// 绘制波浪圆
Integer radius = mRadius.get(i);
canvas.drawCircle(getWidth() / 2, getHeight() / 2, mImageRadius+radius, mPaint);
if (alpha > 0 && mImageRadius+radius < mMaxRadius) {
alpha = (int) (255.0F * (1.0F - (mImageRadius+radius) * 1.0f / mMaxRadius));
mAlphas.set(i, alpha);
mRadius.set(i, radius + 1);
}else if(alpha < 0 && mImageRadius+radius > mMaxRadius){
// 当最外面那个圆达到了View的宽度时,移除,保证内存的回收
mRadius.remove(i);
mAlphas.remove(i);
}
}
// 判断当波浪圆扩散到指定宽度时添加新扩散圆
// if (mRadius.get(mRadius.size() - 1) == mWidth) {
// addWave();
// }
if (mIsWave) {
invalidate();
}
}
思路:一直不停的在根据list中的半径值和alpha值在画对应的圆,list中有多少个圆,就会画出多少个,当alpha的值小于0了,视觉上,人眼看不到了,或者已经到了View的边界,就将他移除。减少内存的占用。
4.提供的一些公共的方法,方便调用
/**
* 开始扩散
*/
public void start() {
mIsWave = true;
invalidate();
}
/**
* 停止扩散
*/
public void stop() {
mIsWave = false;
}
/**
* 是否扩散中
*/
public boolean isWave() {
return mIsWave;
}
/**
* 设置波浪圆颜色
*/
public void setColor(int colorId) {
mColor = colorId;
}
/**
* 设置波浪圆之间间距
*/
public void setWidth(int width) {
mWidth = width;
}
/**
* 设置中心圆半径
*/
public void setMaxRadius(int maxRadius) {
mMaxRadius = maxRadius;
}
public void setImageRadius(int imageRadius) {
mImageRadius = imageRadius;
}
public void addWave(){
mAlphas.add(255);
mRadius.add(0);
}
5.Activity中的调用和xml布局
WaveActivity.java
private ImageView head;
private WaveView wave;
private ScaleAnimation scaleAnimation;
private MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wave);
scaleAnimation = new ScaleAnimation(1.2f, 1f, 1.2f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setDuration(500);
scaleAnimation.setFillAfter(true);
wave = (WaveView) findViewById(R.id.wave);
head = (ImageView) findViewById(R.id.head);
mPlayer = MediaPlayer.create(this, R.raw.water_wave);
head.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
wave.addWave();
head.startAnimation(scaleAnimation);
if(mPlayer.isPlaying()){
mPlayer.stop();
try {
mPlayer.prepare();
} catch (IOException e) {
e.printStackTrace();
}
}
mPlayer.start();
}
});
wave.start();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
wave.setImageRadius(head.getWidth()/2);
}
activity_wave.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
>
<com.dx.demi.view.WaveView
android:id="@+id/wave"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:wave_color="@color/yellow"
app:wave_coreImageRadius="30"
app:wave_width="40"/>
<ImageView
android:id="@+id/head"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/user"
android:layout_centerInParent="true"/>
</RelativeLayout>
6.需要注意的细节
/**
* 获取View的宽高在构造方法中拿不到的,getWidth(),getHeight()都会为零
* @param hasWindowFocus
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
mMaxRadius = getWidth() > getHeight() ? getHeight() / 2 : getWidth() / 2;
invalidate();
}
/**
* 防止window是去焦点时,也就是应用在后台时,停止View的绘制
*/
@Override
public void invalidate() {
if (hasWindowFocus()) {
super.invalidate();
}
}
三种效果的切换
1.我目前贴的代码实现的是效果3(咻一咻):点击图片,图片会放大,同时会播放背景音乐(网上随便找了个还听得过去的),并增加一个新的波浪圆。
2.效果1:打开View的界面,每个一段固定的距离,就会产生一个新的波浪圆。波浪圆是空心的。这个只要设置画笔的风格为STROKE,并设置StrokeWidth。Activity中注释掉动画,注释掉点击事件,注释掉音乐播放。但是要记得一开始就得添加新圆,不然啥都没有。毕竟不像效果3那样,点击图片才有。
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(5);
mAlphas.add(255);
mRadius.add(0);
3.效果2:打开View的界面,每个一段固定的距离,就会产生一个新的波浪圆。波浪圆是实心的。Activity中注释掉动画,注释掉点击事件,注释掉音乐播放。设置画笔的风格为FILL就OK了,默认风格就是FILL。
总结:
在完成这个自定义View的时候,花了很长时间。我也不是一开始就是这样的一个思路。没有想到过用集合来存放半径,没有第一时间想到getWidth()的值在构造方法中会获取不到.所以还是得多想,多尝试。我们才会进步,提高!在自定义View时,一定要明白一个真理:”onDraw()方法重新调用时,会抹去上一次绘制过的图像“。有些地方,我写的可能不是很好,还需要优化。欢迎点评!
源代码链接
自定义View之圆形水波扩散动效的更多相关文章
- 自定义View之一圆形图片
自定义View的方法 对现有控件进行扩展 通过组合来实现新的控件 重写View来实现全新的控件 本篇文章主要讲对现有控件的扩展 1.圆形图片控件 自定义View,对ImageView的扩展 重写onD ...
- 自定义View,圆形头像
1. 效果图 2. xml中 <com.etoury.etoury.ui.view.CircleImg android:id="@+id/user_info_head_img" ...
- android自定义View绘制圆形头像与椭圆头像
要实现这两种效果,需要自定义View,并且有两种实现方式. 第一种: public class BitmapShaders extends View { private BitmapSh ...
- Android绘图机制(一)——自定义View的基础属性和方法
Android绘图机制(一)--自定义View的基础属性和方法 自定义View看起来,确实看起来高深莫测,很多Android开发都不是特别在行这一块,这里面的逻辑以及一些绘画都是有一点难的,说一下我目 ...
- 通过圆形载入View了解自定义View
这是自定义View的第一篇文章,通过制作简单的自定义View来了解自定义View的流程. 自定义View是Android学习和开发中必不可少的一部分.通过自定义View我们可以制作丰富绚丽的控件,自定 ...
- Android 自定义 View 圆形进度条总结
Android 自定义圆形进度条总结 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 微信公众号:牙锅子 源码:CircleProgress 文中如有纰漏,欢迎大家留言指出. 最近 ...
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
Android 高手进阶(21) 版权声明:本文为博主原创文章,未经博主允许不得转载. 转载请注明地址:http://blog.csdn.net/xiaanming/article/detail ...
- Android 自定义View修炼-Android实现圆形、圆角和椭圆自定义图片View(使用BitmapShader图形渲染方法)
一.概述 Android实现圆角矩形,圆形或者椭圆等图形,一般主要是个自定义View加上使用Xfermode实现的.实现圆角图片的方法其实不少,常见的就是利用Xfermode,Shader.本文直接继 ...
- Android 自定义view --圆形百分比(进度条)
转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50334595 注:本文由于是在学习过程中写的,存在大量问题(overdraw onDr ...
随机推荐
- Java 面向切面编程(Aspect Oriented Programming,AOP)
本文内容 实例 引入 原始方法 装饰者模式 JDK 动态代理和 cglib 代理 直接使用 AOP 框架--AspectWerkz 最近跳槽了,新公司使用了 AOP 相关的技术,于是查点资料,复习一下 ...
- Android 分析 Android 应用结构
本文说明 Android 项目组成,虽然简单,但决不能忽视. 当你从简单 Hello World 程序,到会实现一些常见功能,比如,下拉(上拉)刷新最新(加载更多),消息处理(UI 通知更新),Vie ...
- slf4j、jcl、jul、log4j1、log4j2、logback大总结
1 系列目录 jdk-logging.log4j.logback日志介绍及原理 commons-logging与jdk-logging.log4j1.log4j2.logback的集成原理 slf4j ...
- Word批量调整插入图片大小
做标书,word中需要插入大量图片,实为一些证书.文件的扫描文件.但插入后,大小不是想要的,太小了,打印出来看不清.需要调整,需要批量调整. 这是一个不错的方法: 选中第一张图片,按页面调整大小到适合 ...
- jQuery 用each后添加click
mydd = $('.plist'); mydd.each(function(i){ $(this).click(function(){ mydl.eq(i).hide("slow" ...
- LintCode: Happy Number
C++ class Solution { public: /** * @param n an integer * @return true if this is a happy number or f ...
- ubuntu建立快捷方式
拷贝一下文件到新的文件里 [Desktop Entry]Categories=Development;Comment[zh_CN]=Comment=Exec=/usr/local/idea-IU-17 ...
- 1073: 动物简介(animal)
#include<iostream> #include<string> #include<stdio.h> #include<algorithm> #i ...
- linux2.6.30.4内核移植(1)
内核源码:linux2.6.30.4 交叉编译工具:3.4.5 移植linux内核至:TQ2440 1.进入内核顶层目录,修改顶层Makefile,大概在193和194行,将ARCH和CROSS_CO ...
- adb remount 失败:remount failed: Operation not permitted
adb remount 失败:remount failed: Operation not permitted 关于ADB的使用,这里再说明下:经常使用命令 adb shell - 登录设备sh ...