zoukankan      html  css  js  c++  java
  • 自定义View之圆形水波扩散动效

     

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

    效果图

    效果1 效果2 效果3

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

    实现步骤

    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

  • 相关阅读:
    1234D.Distinct Characters Queries(树状数组)
    1217C.The Number of Good Substrings(思维)
    1217B.Zmei Gorynich(思维)
    1213D2.Equalizing by Division(hard version)(图论)
    CentOS7.5搭建Hadoop2.7.6完全分布式集群
    CentOS7.5搭建spark2.3.1集群
    CentOS7.5搭建Hive2.3.3
    【Java入门】JDK安装和环境变量配置(Win7版)
    python第三方库大全
    Python常用模块大全
  • 原文地址:https://www.cnblogs.com/xgjblog/p/8343078.html
Copyright © 2011-2022 走看看