《Android自定义View之——带有阴影扩散的Button》

版权声明:本文为博主原创文章,欢迎转载,转载注明出处即可~~ https://blog.csdn.net/WuchangI/article/details/80785243

一、效果预览

闲话少叙,先看看效果:


1

从上图可以看到,当我们长按时,“按钮”上的阴影扩散得比较慢;当我们纯粹只是点击一下时,“按钮”上的阴影扩散速度加快。



二、实现原理

其实,上图中显示的“按钮”是基于Android的ImageButton实现的。具体过程如下:

  • 通过Android的自定义View机制,继承Android原生的ImageButton并重写其中的按钮监听方法、界面重绘方法等;
  • 准备所需的按钮背景图片;
  • 在xml布局文件中使用该自定义的ImageButton,并引用准备好的背景图片;
  • 在MainActivity中获取该按钮的实例,为其绑定相应的点击事件。


因此从上面不难看出,我们可以根据自己的需求设置“按钮”底部的背景图,并在该“按钮”上呈现出我们期望的阴影扩散效果。



三、具体实现代码

考虑到代码注释有详细的注释,故在此不叙述实现过程。

1、自定义的ImageButton

Code:

RippleButton.java

package com.example.wuchangi.customview;


import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.SystemClock;
import android.support.v7.widget.AppCompatImageButton;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.ViewConfiguration;


/**
 * Created by WuchangI on 2018/6/10.
 */

//一款自定义的阴影扩散效果按钮
public class RippleButton extends AppCompatImageButton
{
    //按钮宽度、 高度
    private int buttonViewWidth, buttonViewHeight;

    //刷新周期
    private static final int INVALIDATE_PERIOD = 15;

    //扩散增量
    private int diffuseIncrement;

    //按钮的左上角原点坐标
    private int buttonViewX, buttonViewY;

    //扩散效果的最大半径
    private int maxDiffuseRadius;

    //扩散效果的半径
    private int diffuseRadius;

    //绘制按钮底部背景的画笔
    private Paint backgroundPaint;

    //绘制扩散效果(波纹)颜色的画笔
    private Paint diffusePaint;

    //记录按钮是否被按下
    private boolean isPushButton;

    //用户触摸位置
    private int touchEventX, touchEventY;

    //用户按下按钮的时间
    private long downTime = 0;

    //获取按钮的长按时间,超过此时间就认为是长按事件
    int longPressTimeout;


    public RippleButton(Context context, AttributeSet attrs)
    {
        super(context, attrs);

        //初始化界面数据
        initData();

        //初始化画笔
        initPaints();
    }


    //监听用户按钮事件
    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:

                if (downTime == 0)
                {
                    //记录按钮按下时的时间点
                    downTime = SystemClock.elapsedRealtime();
                }

                isPushButton = true;

                touchEventX = (int) event.getX();
                touchEventY = (int) event.getY();

                //计算扩散效果的最大半径
                countMaxDiffuseRadius();

                //进行扩散(刷新界面)
                postInvalidateDelayed(INVALIDATE_PERIOD);

                break;

            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                //如果用户只是轻触按下,没有长按
                if (SystemClock.elapsedRealtime() - downTime < longPressTimeout)
                {
                    //增大扩散增量,加快扩散速度
                    diffuseIncrement = 35;

                    //刷新界面
                    postInvalidate();
                }
                else
                {
                    resetData();
                }

                break;
        }

        return super.onTouchEvent(event);
    }


    @Override
    protected void dispatchDraw(Canvas canvas)
    {
        super.dispatchDraw(canvas);

        //如果按钮没有被按下,则返回
        if (!isPushButton)
        {
            return;
        }

        //绘制按下按钮后的背景(这里设置为透明背景,以显示底部的背景图片)
        canvas.drawRect(buttonViewX, buttonViewY, buttonViewX + buttonViewWidth, buttonViewY + buttonViewHeight, backgroundPaint);

        //保存当前画布的状态
        canvas.save();

        //从画布中裁剪出待绘制的区域(也就是按钮所覆盖的画布区域)
        canvas.clipRect(buttonViewX, buttonViewY, buttonViewX + buttonViewWidth, buttonViewY + buttonViewHeight);

        //绘制扩散的圆形
        canvas.drawCircle(touchEventX, touchEventY, diffuseRadius, diffusePaint);

        //恢复画布之前的状态
        canvas.restore();

        if (diffuseRadius < maxDiffuseRadius)
        {
            postInvalidateDelayed(INVALIDATE_PERIOD, buttonViewX, buttonViewY, buttonViewX + buttonViewWidth, buttonViewY + buttonViewHeight);

            diffuseRadius += diffuseIncrement;
        }
        else
        {
            resetData();
        }

    }


    //获取按钮的宽度和高度
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);

        this.buttonViewWidth = w;
        this.buttonViewHeight = h;
    }


    //初始化界面数据
    private void initData()
    {
        downTime = 0;
        diffuseIncrement = 10;
        diffuseRadius = 0;
        buttonViewX = buttonViewY = 0;
        isPushButton = false;
        longPressTimeout = ViewConfiguration.getLongPressTimeout();
    }


    //初始化必要的画笔
    private void initPaints()
    {
        backgroundPaint = new Paint();
        //设置透明背景,以显示底部的背景图片
        backgroundPaint.setColor(Color.parseColor("#00000000"));

        diffusePaint = new Paint();
        //设置画笔为灰色,制造阴影波纹扩散效果
        diffusePaint.setColor(Color.parseColor("#50000000"));
    }


    //计算扩散效果的最大半径
    private void countMaxDiffuseRadius()
    {
        if (buttonViewWidth > buttonViewHeight)
        {
            if (touchEventX < buttonViewWidth / 2)
            {
                maxDiffuseRadius = buttonViewWidth - touchEventX;
            }
            else
            {
                maxDiffuseRadius = touchEventX;
            }
        }
        else
        {
            if (touchEventY < buttonViewHeight / 2)
            {
                maxDiffuseRadius = buttonViewHeight - touchEventY;
            }
            else
            {
                maxDiffuseRadius = touchEventY;
            }
        }
    }


    //重置数据并刷新界面
    private void resetData()
    {
        downTime = 0;
        diffuseIncrement = 10;
        diffuseRadius = 0;

        //刷新界面(取消波纹效果)
        isPushButton = false;
        postInvalidate();
    }

}


2、按钮背景图

可以根据自己的需要替换成带有文字的任何图片。


3、xml布局文件

Code:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              >

    <com.example.wuchangi.customview.RippleButton
            android:id="@+id/custom_button"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:src="@mipmap/button_view"/>

</LinearLayout>


4、MainActivity

Code:

MainActivity.java

package com.example.wuchangi.customview;

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


/**
 * Created by WuchangI on 2018/6/10.
 */

public class MainActivity extends AppCompatActivity
{
    private RippleButton rippleButton;

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

        rippleButton = (RippleButton)findViewById(R.id.custom_button);
        rippleButton.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View view)
            {
                Toast.makeText(MainActivity.this, "我是一款自定义的button~~", Toast.LENGTH_SHORT).show();
            }
        });

    }
}



四、其他想说的

其实,以上的绘制机制不仅可以用来绘制按钮的阴影扩散,我们也可以根据自己的需要将扩散的阴影颜色替换成其他颜色,实现水波扩散的效果。


3

猜你喜欢

转载自blog.csdn.net/WuchangI/article/details/80785243