Android自定义View 实现水波纹动画引导

一、实现效果图

这里写图片描述

关于贝塞尔曲线
这里写图片描述
二、实现代码

1.自定义view

package com.czhappy.showintroduce.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RelativeLayout;

/**
 * Description: 水波纹动画引导view
 * User: chenzheng
 * Date: 2017/1/14 0014
 * Time: 18:01
 */
public class RippleIntroView  extends RelativeLayout implements Runnable {

    private int mMaxRadius = 70;
    private int mInterval = 20;
    private int count = 0;

    private Bitmap mCacheBitmap;
    private Paint mRipplePaint;
    private Paint mCirclePaint;
    private Path mArcPath;

    public RippleIntroView(Context context) {
        this(context, null);
    }

    public RippleIntroView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public RippleIntroView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mRipplePaint = new Paint();
        mRipplePaint.setAntiAlias(true);
        mRipplePaint.setStyle(Paint.Style.STROKE);
        mRipplePaint.setColor(Color.WHITE);
        mRipplePaint.setStrokeWidth(2.f);

        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setStyle(Paint.Style.FILL);
        mCirclePaint.setColor(Color.WHITE);

        mArcPath = new Path();
    }

    /**
     * view大小变化时系统调用
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mCacheBitmap != null) {
            mCacheBitmap.recycle();
            mCacheBitmap = null;
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        //获取加号图片view
        View mPlusChild = getChildAt(0);
        //获取提示图片view
        View mRefsChild = getChildAt(1);
        if (mPlusChild == null || mRefsChild == null) return;

        //获取加号图片大小
        final int pw = mPlusChild.getWidth();
        final int ph = mPlusChild.getHeight();

        //获取提示图片大小
        final int fw = mRefsChild.getWidth();
        final int fh = mRefsChild.getHeight();

        if (pw == 0 || ph == 0) return;

        //加号图片中心点坐标
        final float px = mPlusChild.getX() + pw / 2;
        final float py = mPlusChild.getY() + ph / 2;
        //提示图片左上角坐标
        final float fx = mRefsChild.getX();
        final float fy = mRefsChild.getY();

        final int rw = pw / 2;
        final int rh = ph / 2;

        if (mCacheBitmap == null) {
            mCacheBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            Canvas cv = new Canvas(mCacheBitmap);
            super.onDraw(cv);

            //清空所有已经画过的path至原始状态
            mArcPath.reset();

            //起始轮廓点移至x,y坐标点,即加号图片正下方再往下20位置
            mArcPath.moveTo(px, py + rh + mInterval);
            //设置二次贝塞尔,实现平滑曲线,前两个参数为操作点坐标,后两个参数为结束点坐标
            mArcPath.quadTo(px, fy - mInterval, fx + fw * 0.618f, fy - mInterval);
            //0~255,数值越小越透明
            mRipplePaint.setAlpha(255);
            cv.drawPath(mArcPath, mRipplePaint);
            //绘制半径为6的实心圆点
            cv.drawCircle(px, py + rh + mInterval, 6, mCirclePaint);
        }

        //绘制背景图片
        canvas.drawBitmap(mCacheBitmap, 0, 0, mCirclePaint);

        //保存画布当前的状态
        int save = canvas.save();
        for (int step = count; step <= mMaxRadius; step += mInterval) {
            //step越大越靠外就越透明
            mRipplePaint.setAlpha(255 * (mMaxRadius - step) / mMaxRadius);
            canvas.drawCircle(px, py, (float) (rw + step), mRipplePaint);
        }
        //恢复Canvas的状态
        canvas.restoreToCount(save);
        //延迟80毫秒后开始运行
        postDelayed(this, 80);
    }

    @Override
    public void run() {
        //把run对象的引用从队列里拿出来,这样,他就不会执行了,但 run 没有销毁
        removeCallbacks(this);
        count += 2;
        count %= mInterval;
        invalidate();//重绘
    }

    /**
     * 销毁view时调用,收尾工作
     */
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mCacheBitmap != null) {
            mCacheBitmap.recycle();
            mCacheBitmap = null;
        }
    }
}

2.MainActivity.java

package com.czhappy.showintroduce.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;

import com.czhappy.showintroduce.R;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        View view = findViewById(R.id.layout_ripple);
        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ((ViewGroup) v.getParent()).removeView(v);
            }
        });
    }
}

3.activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!" />

    <com.czhappy.showintroduce.view.RippleIntroView
        android:id="@+id/layout_ripple"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="true"
        android:fitsSystemWindows="true"
        android:background="#AA000000">

        <ImageView
            android:id="@+id/iv_plus"
            android:layout_marginTop="36dp"
            android:src="@mipmap/ic_add"
            android:layout_alignParentRight="true"
            android:layout_marginRight="6dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <ImageView
            android:src="@mipmap/tips_subscribe"
            android:id="@+id/tv_title"
            android:layout_below="@id/iv_plus"
            android:layout_marginTop="50dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="40dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </com.czhappy.showintroduce.view.RippleIntroView>

</FrameLayout>

三、源码下载

http://download.csdn.net/detail/chenzheng8975/9739379

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值