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

源代码链接

https://github.com/Demidong/ClockView

原文地址:https://www.cnblogs.com/xgjblog/p/8343078.html

时间: 2024-11-09 10:25:40

自定义View之圆形水波扩散动效的相关文章

自定义View之实现日出日落太阳动效

以前也很羡慕网上大神随手写写就是一个很漂亮的自定义控件,所以我下决心也要学着去写,刚好最近复习了Android View的绘制流程知识,看来看去就是那些个知识点,没点产出总感觉很迷.现在个人呢用的是华为荣耀8手机,碰巧在看自带的天气APP时,滑到最下面看到那个动效图:日出时间和日落时间上边是一个半圆,白天任意的时刻(在日出和日落时间之间)都有对应一个太阳从日出时刻沿着半圆弧做动画特效,个人第一感觉就是:就拿这个来练练手啦!于是拿着笔和纸,画了模型图,甚至求什么sin.cos函数,有点过分了哈,还

Android进阶:九、自定义View之手写Loading动效

这是一个很简单的动画效果,使用属性动画即可实现,希望对读者学习动画能达到抛砖引玉的效果 一.自定义动画效果--Loading效果 如上是我们需要做的一个Loading动画.Loading效果是很常见的一种动画,最简单的实现让设计画个动态图即可,或者画个静态图然后使用帧动画也可以实现.但是今天我们用纯代码实现,不用任何图片资源.大致思路我们自定义一个View,继承View类,然后画两个不同半径的弧形,转动不同的角度即可实现.绘制两个不同半径的弧形首先初始化外圆和内园的Recf(); private

【Android 应用开发】 自定义 View 组件 -- 圆形进度条

转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://download.csdn.net/detail/han1202012/8069497 ; -- GitHub 地址 : https://github.com/han1202012/CircleProcess.git ; -- 工程示例 : 一. 相关知识点解析 1. 自定义 View 组件构造方法 构造方

自定义View之一圆形图片

自定义View的方法 对现有控件进行扩展 通过组合来实现新的控件 重写View来实现全新的控件 本篇文章主要讲对现有控件的扩展 1.圆形图片控件 自定义View,对ImageView的扩展 重写onDraw方法,绘制图片 图片需要进行修改,让其圆形方式显示 主要难点在于圆形图片的绘制,如果我们知道PorterDuffXfermode,其实难度就不大,里面有一种模式是显示相交模式,我们下面的代码就是根据这个模式,绘画出圆形图片的 @Override protected void onDraw(Ca

android自定义View绘制圆形头像与椭圆头像

要实现这两种效果,需要自定义View,并且有两种实现方式. 第一种: public class BitmapShaders extends View { private  BitmapShader bitmapShader = null; private Bitmap bitmap = null; private Paint paint = null; private ShapeDrawable shapeDrawable = null; private int BitmapWidth  = 0

自定义view之圆形进度条

本节介绍自定义view-圆形进度条 思路: 根据前面介绍的自定义view内容可拓展得之: 1:新建类继承自View 2:添加自定义view属性 3:重写onDraw(Canvas canvas) 4:实现功能 下面上代码 1.自定义view代码: public class CustomView extends View { //背景圆环颜色 private int circleColor; //进度条颜色&字体颜色(为了美观,所以设计字体颜色和进度条颜色值一致) private int seco

Android自定义View——圆形进度条式按钮

介绍 今天上班的时候有个哥们问我怎么去实现一个按钮式的进度条,先来看看他需要实现的效果图. 和普通的圆形进度条类似,只是中间的地方有两个状态表示,未开始,暂停状态.而且他说圆形进度的功能已经实现了.那么我们只需要对中间的两个状态做处理就行了. 先来看看实现的效果图: 上面说了我们只需要处理中间状态的变化就可以了,对于进度的处理直接使用了弘洋文章中实现: http://blog.csdn.net/lmj623565791/article/details/43371299 下面开始具体实现. 具体实

Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

转载请注明地址:http://blog.csdn.net/xiaanming/article/details/10298163 很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己来自定义一个能满足我们需求的View,自定义View我们需要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为了我们自定义的View在一个项目中能够重用,有时候我们需要自定义其属性,举个很简单的例子,我在项目中的多个界面使用我自定义的View,每个界面该自定义View

android自定义View之(六)------高仿华为荣耀3C的圆形刻度比例图(ShowPercentView)

为什么写这篇文章: 显示当前的容量所占的比例,表现当前计划的进度,一般都会采用百分比的方式,而图形显示,以其一目了然的直观性和赏心悦目的美观形成为了我们的当然的首选. 在图形表示百分比的方法中,我们有用画圆的圆形进度条的方法<<android自定义View之(二)------简单进度条显示样例篇>>,也有用画弧形的进度条的方法<<android自定义View之(三)------视频音量调控样例>>,今天看到华为荣耀3C的一个界面: 个人觉得这个表示比例的圆形