自定义View之圆形水波扩散动效

 

这个效果作出来之后,真的美极了!放在你的应用中,无疑增添了光彩!java

效果图

效果1 效果2 效果3

其实,第一种效果,才是产品的需求要的效果。第三种效果,是否是很熟悉?支付宝的咻一咻!哈哈,无心中,我就写出来了。android

实现步骤

1.attrs.xml定义属性git

<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的初始化github

/** * 波浪圆圈颜色 */ 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()canvas

@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的边界,就将他移除。减小内存的占用。markdown

4.提供的一些公共的方法,方便调用app

/** * 开始扩散 */ 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布局ide

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.xmlpost

<?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()方法从新调用时,会抹去上一次绘制过的图像“。有些地方,我写的可能不是很好,还须要优化。欢迎点评!

源代码连接

https://github.com/Demidong/ClockView

相关文章
相关标签/搜索