自定义圆形中心扩散效果

最近因为项目需求,需要写一个自定义的扩散加震动效果,废了一下午的时间,终于给他搞出来了,结果第二天需求就改掉了..

不说那么多题外话了,直接撸代码~

首先是XML文件:

<com.example.XXXXX.sxt.view.customview.DiffuseView
     android:id="@+id/customCall"
     android:layout_width="@dimen/x120"
     android:layout_height="@dimen/x120"
     app:diffuse_coreRadius="100"
     app:diffuse_maxWidth="80"
     app:diffuse_width="10"
     android:layout_centerInParent="true"/>

属性&方法

属性名 java方法 作用
diffuse_color setColor(int colorId) 设置扩散圆颜色
diffuse_coreColor setCoreColor(int colorId) 设置中心圆颜色
diffuse_coreImage setCoreImage(int imageId) 设置中心圆图片
diffuse_coreRadius setCoreRadius(int radius) 设置中心圆半径
diffuse_maxWidth setMaxWidth(int maxWidth) 设置最大扩散宽度
diffuse_width setDiffuseWidth(int width) 设置扩散圆宽度,值越小越宽

Java代码(直接拷贝成一个类就OK~):

/**
 * Created by HXY on 2018/9/25.
 * Be used for : 首页电话按钮背景view,自定义扩散背景
 */

public class DiffuseView extends View {
    /** 扩散圆圈颜色 */
    private int mColor = getResources().getColor(R.color.colorPink);
    /** 圆圈中心颜色 */
    private int mCoreColor = getResources().getColor(R.color.colorPink);
    /** 圆圈中心图片 */
    private Bitmap mBitmap;
    /** 中心圆半径 */
    private float mCoreRadius = 150;
    /** 扩散圆宽度 */
    private int mDiffuseWidth = 3;
    /** 最大宽度 */
    private Integer mMaxWidth = 255;
    /** 是否正在扩散中 */
    private boolean mIsDiffuse = false;
    // 透明度集合
    private List<Integer> mAlphas = new ArrayList<>();
    // 扩散圆半径集合
    private List<Integer> mWidths = new ArrayList<>();
    private Paint mPaint;

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

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

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

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DiffuseView, defStyleAttr, 0);
        mColor = a.getColor(R.styleable.DiffuseView_diffuse_color, mColor);
        mCoreColor = a.getColor(R.styleable.DiffuseView_diffuse_coreColor, mCoreColor);
        mCoreRadius = a.getFloat(R.styleable.DiffuseView_diffuse_coreRadius, mCoreRadius);
        mDiffuseWidth = a.getInt(R.styleable.DiffuseView_diffuse_width, mDiffuseWidth);
        mMaxWidth = a.getInt(R.styleable.DiffuseView_diffuse_maxWidth, mMaxWidth);
        int imageId = a.getResourceId(R.styleable.DiffuseView_diffuse_coreImage, -1);
        if(imageId != -1) mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
        a.recycle();
    }

    private void init() {
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mAlphas.add(255);
        mWidths.add(0);
    }

    @Override
    public void invalidate() {
        if(hasWindowFocus()){
            super.invalidate();
        }
    }

    @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 width = mWidths.get(i);
            canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCoreRadius + width, mPaint);

            if(alpha > 0 && width < mMaxWidth){
                mAlphas.set(i, alpha - 3);
                mWidths.set(i, width + 1);
            }
        }
        // 判断当扩散圆扩散到指定宽度时添加新扩散圆
        if (mWidths.get(mWidths.size() - 1) == mMaxWidth / 1) {
            mAlphas.add(255);
            mWidths.add(0);
        }
        // 超过3个扩散圆,删除最外层
        if(mWidths.size() >= 2){
            mWidths.remove(0);
            mAlphas.remove(0);
        }

        // 绘制中心圆及图片
        mPaint.setAlpha(255);
        mPaint.setColor(mCoreColor);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, mCoreRadius, mPaint);

        if(mBitmap != null){
            Bitmap newBitmap = BitmapUtils.changeSize(mBitmap, 150, 150);
            canvas.drawBitmap(newBitmap, getWidth() / 2 - newBitmap.getWidth() / 2
                    , getHeight() / 2 - newBitmap.getHeight() / 2, mPaint);
        }

        if(mIsDiffuse){
            invalidate();
        }
    }

    /**
     * 开始扩散
     */
    public void start() {
        mIsDiffuse = true;
        invalidate();
    }

    /**
     * 停止扩散
     */
    public void stop() {
        mIsDiffuse = false;
    }

    /**
     * 是否扩散中
     */
    public boolean isDiffuse(){
        return mIsDiffuse;
    }

    /**
     * 设置扩散圆颜色
     */
    public void setColor(int colorId){
        mColor = colorId;
    }

    /**
     * 设置中心圆颜色
     */
    public void setCoreColor(int colorId){
        mCoreColor = colorId;
    }

    /**
     * 设置中心圆图片
     */
    public void setCoreImage(int imageId){
        mBitmap = BitmapFactory.decodeResource(getResources(), imageId);
    }

    /**
     * 设置中心圆半径
     */
    public void setCoreRadius(int radius){
        mCoreRadius = radius;
    }

    /**
     * 设置扩散圆宽度(值越小宽度越大)
     */
    public void setDiffuseWidth(int width){
        mDiffuseWidth = width;
    }

    /**
     * 设置最大宽度
     */
    public void setMaxWidth(int maxWidth){
        mMaxWidth = maxWidth;
    }


}

外部调用:

DiffuseView mDiffuseView = (DiffuseView) findViewById(R.id.diffuseView);
mDiffuseView.start(); // 开始扩散
mDiffuseView.stop();// 停止扩散

其实在网上看到类似代码的时候还是有很多坑的,比如如果直接给这个自定义控件添加图片的话,我们是不能给图片添加动效的,因为直接设置图片的时候是bitmap对象.所以我的做法是用一个 RelativeLayout 去作为外层布局,里面放我们的自定义控件,同时添加一个 ImageView 

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="@dimen/x130"
                android:background="@mipmap/home">

                <com.example.XXXXX.sxt.view.customview.DiffuseView
                    android:id="@+id/customCall"
                    android:layout_width="@dimen/x120"
                    android:layout_height="@dimen/x120"
                    app:diffuse_coreRadius="100"
                    app:diffuse_maxWidth="80"
                    app:diffuse_width="10"
                    android:layout_centerInParent="true"/>

                <ImageView
                    android:id="@+id/call"
                    android:layout_width="@dimen/x40"
                    android:layout_height="@dimen/x40"
                    android:background="@mipmap/phone"
                    android:layout_centerInParent="true"
                    android:clickable="true"/>

            </RelativeLayout>

这样就能达成我们期望的效果,直接在代码里面设置动画就可以了.但是真正这样做了之后又遇到一个问题,只要我们开启了动画之后,我们的自定义控件就不能正常绘制,只有由默认加载状态切换一下其他页面,再切换回来之后才能正常绘制出我们想要的效果.具体什么原因现在暂时没有搞明白,知道的同学可以下方评论留言一下~

解决方法就是开启两个线程,分别去控制自定义的效果和图片的动画效果,代码如下:

    //开始播放电话按钮相关动画
    private void startAni(final ImageView call) {
        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                getActivity().runOnUiThread(new Runnable() {
                    public void run() {
                        //开启电话颤动效果
                        setAnimation(call);
                    }
                });
            }
        };
        TimerTask task2 = new TimerTask() {
            @Override
            public void run() {
                // TODO Auto-generated method stub
                getActivity().runOnUiThread(new Runnable() {
                    public void run() {
                        //开启背景扩散效果
                        customCall.start();
                    }
                });
            }
        };
        //执行开启任务
        timer.schedule(task, 0, 2500);
        timer.schedule(task2, 0, 100);
    }
/**
     * 设置自定义动画
     */
    private void setAnimation(ImageView imageView){
        AnimatorSet animatorSet = new AnimatorSet();
        Animator anim1 = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 30f);
        anim1.setDuration(200);
        Animator anim2 = ObjectAnimator.ofFloat(imageView, "rotation", 30f, -15f);
        anim1.setDuration(150);
        Animator anim3 = ObjectAnimator.ofFloat(imageView, "rotation", -15f, 00f);
        anim1.setDuration(150);
        animatorSet.playSequentially(anim1,anim2,anim3);
        animatorSet.start();
    }

暂时解决,达成需求~

--------------------- 本文借鉴于 Airsaid 的CSDN 博客 ,原文地址请点击:https://blog.csdn.net/airsaid/article/details/52683193?utm_source=copy

猜你喜欢

转载自blog.csdn.net/qq_19681347/article/details/82852382