什么是水波点击的效果? 下面是几种不同的实现方法的效果图以及实现方法

 
Video_2016-08-31_003846

如何实现?

方法一 使用官方提供的RippleDrawable类

优点:使用方便,非常漂亮。

缺点:Android5.0以下版本无法使用

步骤:

  1. 添加一个普通的 ripple_bg_drawable.xml 背景文件

    <?xml version="1.0" encoding="utf-8"?>
    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="#8cc476" />
    <corners android:radius="0dp" />
    </shape>
  2. 添加带波纹效果的背景文件 ripple_bg.xml

    <?xml version="1.0" encoding="utf-8"?>
    <ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="#FF21272B">
    <item android:drawable="@drawable/ripple_bg_drawable" />
    </ripple>

    这里使用了上面的xml文件作为背景,然后给组件设置背景时,选 ripple_bg.xml 就可以了。如

    <Button
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="@drawable/ripple_bg"
    android:padding="10dp"
    android:text="使用RippleDrawable类实现" />

    效果:

     
    Video_2016-08-30_231558

    很简单的录制了下gif ,质量不好。实际效果很好看。

    注意事项:如果你的api最低版本低于21,则ripple这里其实是有错误提醒的,低于这个版本会报错。

方法二 使用代码实现

优点:低版本兼容、使用简单

缺点:效果不如官方的好

步骤:

  1. 在values下添加 attrs.xml 文件

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <declare-styleable name="MaterialLayout">
    <attr name="alpha" format="integer" />
    <attr name="alpha_step" format="integer" />
    <attr name="framerate" format="integer" />
    <attr name="duration" format="integer" />
    <attr name="mycolor" format="color" />
    <attr name="scale" format="float" />
    </declare-styleable>
    </resources>
  2. 添加一个自定义的布局类 MaterialLayout.class

    public class MaterialLayout extends RelativeLayout {
    
        private static final int DEFAULT_RADIUS = 10;
    private static final int DEFAULT_FRAME_RATE = 10;
    private static final int DEFAULT_DURATION = 200;
    private static final int DEFAULT_ALPHA = 255;
    private static final float DEFAULT_SCALE = 0.8f;
    private static final int DEFAULT_ALPHA_STEP = 5; /**
    * 动画帧率
    */
    private int mFrameRate = DEFAULT_FRAME_RATE;
    /**
    * 渐变动画持续时间
    */
    private int mDuration = DEFAULT_DURATION;
    /**
    *
    */
    private Paint mPaint = new Paint();
    /**
    * 被点击的视图的中心点
    */
    private Point mCenterPoint = null;
    /**
    * 视图的Rect
    */
    private RectF mTargetRectf;
    /**
    * 起始的圆形背景半径
    */
    private int mRadius = DEFAULT_RADIUS;
    /**
    * 最大的半径
    */
    private int mMaxRadius = DEFAULT_RADIUS; /**
    * 渐变的背景色
    */
    private int mCirclelColor = Color.LTGRAY;
    /**
    * 每次重绘时半径的增幅
    */
    private int mRadiusStep = 1;
    /**
    * 保存用户设置的alpha值
    */
    private int mBackupAlpha; /**
    * 圆形半径针对于被点击视图的缩放比例,默认为0.8
    */
    private float mCircleScale = DEFAULT_SCALE;
    /**
    * 颜色的alpha值, (0, 255)
    */
    private int mColorAlpha = DEFAULT_ALPHA;
    /**
    * 每次动画Alpha的渐变递减值
    */
    private int mAlphaStep = DEFAULT_ALPHA_STEP; private View mTargetView; /**
    * @param context
    */
    public MaterialLayout(Context context) {
    this(context, null);
    } public MaterialLayout(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs);
    } public MaterialLayout(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init(context, attrs);
    } private void init(Context context, AttributeSet attrs) {
    if (isInEditMode()) {
    return;
    } if (attrs != null) {
    initTypedArray(context, attrs);
    } initPaint(); this.setWillNotDraw(false);
    this.setDrawingCacheEnabled(true);
    } private void initTypedArray(Context context, AttributeSet attrs) {
    final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MaterialLayout);
    mCirclelColor = typedArray.getColor(R.styleable.MaterialLayout_mycolor, Color.LTGRAY);
    mDuration = typedArray.getInteger(R.styleable.MaterialLayout_duration, DEFAULT_DURATION);
    mFrameRate = typedArray.getInteger(R.styleable.MaterialLayout_framerate, DEFAULT_FRAME_RATE);
    mColorAlpha = typedArray.getInteger(R.styleable.MaterialLayout_alpha, DEFAULT_ALPHA);
    mCircleScale = typedArray.getFloat(R.styleable.MaterialLayout_scale, DEFAULT_SCALE); typedArray.recycle(); } private void initPaint() {
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setColor(mCirclelColor);
    mPaint.setAlpha(mColorAlpha); // 备份alpha属性用于动画完成时重置
    mBackupAlpha = mColorAlpha;
    } /**
    * 点击的某个坐标点是否在View的内部
    *
    * @param touchView
    * @param x 被点击的x坐标
    * @param y 被点击的y坐标
    * @return 如果点击的坐标在该view内则返回true,否则返回false
    */
    private boolean isInFrame(View touchView, float x, float y) {
    initViewRect(touchView);
    return mTargetRectf.contains(x, y);
    } /**
    * 获取点中的区域,屏幕绝对坐标值,这个高度值也包含了状态栏和标题栏高度
    *
    * @param touchView
    */
    private void initViewRect(View touchView) {
    int[] location = new int[2];
    touchView.getLocationOnScreen(location);
    // 视图的区域
    mTargetRectf = new RectF(location[0], location[1], location[0] + touchView.getWidth(),
    location[1] + touchView.getHeight()); } /**
    * 减去状态栏和标题栏的高度
    */
    private void removeExtraHeight() {
    int[] location = new int[2];
    this.getLocationOnScreen(location);
    // 减去两个该布局的top,这个top值就是状态栏的高度
    mTargetRectf.top -= location[1];
    mTargetRectf.bottom -= location[1];
    // 计算中心点坐标
    int centerHorizontal = (int) (mTargetRectf.left + mTargetRectf.right) / 2;
    int centerVertical = (int) ((mTargetRectf.top + mTargetRectf.bottom) / 2);
    // 获取中心点
    mCenterPoint = new Point(centerHorizontal, centerVertical); } private View findTargetView(ViewGroup viewGroup, float x, float y) {
    int childCount = viewGroup.getChildCount();
    // 迭代查找被点击的目标视图
    for (int i = 0; i < childCount; i++) {
    View childView = viewGroup.getChildAt(i);
    if (childView instanceof ViewGroup) {
    return findTargetView((ViewGroup) childView, x, y);
    } else if (isInFrame(childView, x, y)) { // 否则判断该点是否在该View的frame内
    return childView;
    }
    } return null;
    } private boolean isAnimEnd() {
    return mRadius >= mMaxRadius;
    } private void calculateMaxRadius(View view) {
    // 取视图的最长边
    int maxLength = Math.max(view.getWidth(), view.getHeight());
    // 计算Ripple圆形的半径
    mMaxRadius = (int) ((maxLength / 2) * mCircleScale); int redrawCount = mDuration / mFrameRate;
    // 计算每次动画半径的增值
    mRadiusStep = (mMaxRadius - DEFAULT_RADIUS) / redrawCount;
    // 计算每次alpha递减的值
    mAlphaStep = (mColorAlpha - 100) / redrawCount;
    } /**
    * 处理ACTION_DOWN触摸事件, 注意这里获取的是Raw x, y,
    * 即屏幕的绝对坐标,但是这个当屏幕中有状态栏和标题栏时就需要去掉这些高度,因此得到mTargetRectf后其高度需要减去该布局的top起点
    * ,也就是标题栏和状态栏的总高度.
    *
    * @param event
    */
    private void deliveryTouchDownEvent(MotionEvent event) {
    if (event.getAction() == MotionEvent.ACTION_DOWN) {
    mTargetView = findTargetView(this, event.getRawX(), event.getRawY());
    if (mTargetView != null) {
    removeExtraHeight();
    // 计算相关数据
    calculateMaxRadius(mTargetView);
    // 重绘视图
    invalidate();
    }
    }
    } @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
    deliveryTouchDownEvent(event);
    return super.onInterceptTouchEvent(event);
    } @Override
    protected void dispatchDraw(Canvas canvas) {
    super.dispatchDraw(canvas);
    // 绘制Circle
    drawRippleIfNecessary(canvas);
    } private void drawRippleIfNecessary(Canvas canvas) {
    if (isFoundTouchedSubView()) {
    // 计算新的半径和alpha值
    mRadius += mRadiusStep;
    mColorAlpha -= mAlphaStep; // 裁剪一块区域,这块区域就是被点击的View的区域.通过clipRect来获取这块区域,使得绘制操作只能在这个区域范围内的进行,
    // 即使绘制的内容大于这块区域,那么大于这块区域的绘制内容将不可见. 这样保证了背景层只能绘制在被点击的视图的区域
    canvas.clipRect(mTargetRectf);
    mPaint.setAlpha(mColorAlpha);
    // 绘制背景圆形,也就是
    canvas.drawCircle(mCenterPoint.x, mCenterPoint.y, mRadius, mPaint);
    } if (isAnimEnd()) {
    reset();
    } else {
    invalidateDelayed();
    }
    } /**
    * 发送重绘消息
    */
    private void invalidateDelayed() {
    this.postDelayed(new Runnable() { @Override
    public void run() {
    invalidate();
    }
    }, mFrameRate);
    } /**
    * 判断是否找到被点击的子视图
    *
    * @return
    */
    private boolean isFoundTouchedSubView() {
    return mCenterPoint != null && mTargetView != null;
    } private void reset() {
    mCenterPoint = null;
    mTargetRectf = null;
    mRadius = DEFAULT_RADIUS;
    mColorAlpha = mBackupAlpha;
    mTargetView = null;
    invalidate();
    }
    }
  3. 在布局文件中引用

    <com.liangddyy.rippledemo.MaterialLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <Button
    android:layout_marginTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:padding="10dp"
    android:text="自定义布局实现1" />
    <Button
    android:layout_marginTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:padding="10dp"
    android:text="自定义布局实现2" />
    </LinearLayout>
    </com.liangddyy.rippledemo.MaterialLayout>

    效果:

     
    Video_2016-08-30_235950

    其实可以看到,只要在 MaterialLayout 布局中的控件都有这个效果,所以使用其他是很方便的。

    这部分代码见原作者博客 http://blog.csdn.net/bboyfeiyu/article/details/42587799

方法三 第三方库实现

github上的一个叫 RippleEffec 项目

优点:美观、使用简单、最低兼容API 9

缺点:不如官方的好看啦

步骤:

  1. 导入库

    dependencies {
    compile 'com.github.traex.rippleeffect:library:1.3'
    }
  2. 使用类似于方法二,比较灵活和方便

    <com.andexert.library.RippleView
    android:layout_marginTop="10dp"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    rv_centered="true">
    <Button
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:background="#4ce65e"
    android:text="第三方库实现"/>
    </com.andexert.library.RippleView>

如果直接导入使用报错,那可以复制如下源码到工程使用。

  1. 定义个颜色

    <color name="rippelColor">#FFFFFF</color>

  2. 添加attrs.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
    <declare-styleable name="RippleView">
    <attr name="rv_alpha" format="integer" />
    <attr name="rv_framerate" format="integer" />
    <attr name="rv_rippleDuration" format="integer" />
    <attr name="rv_zoomDuration" format="integer" />
    <attr name="rv_color" format="color" />
    <attr name="rv_centered" format="boolean" />
    <attr name="rv_type" format="enum">
    <enum name="simpleRipple" value="0" />
    <enum name="doubleRipple" value="1" />
    <enum name="rectangle" value="2" />
    </attr>
    <attr name="rv_ripplePadding" format="dimension" />
    <attr name="rv_zoom" format="boolean" />
    <attr name="rv_zoomScale" format="float" />
    </declare-styleable>
    </resources>
  3. 添加RippleView.java文件

    代码太多,不复制了。参见末尾处的项目源码吧。

    效果:

     
    Video_2016-08-31_003808

最后附上整个示例工程代码:

https://github.com/liangddyy/RippleDemo

原文:http://539go.com/2016/08/31/Android-MaterialDesign%E4%B9%8B%E6%B0%B4%E6%B3%A2%E7%82%B9%E5%87%BB%E6%95%88%E6%9E%9C%E7%9A%84%E5%87%A0%E7%A7%8D%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%B3%95/

Android MaterialDesign之水波点击效果的几种实现方法的更多相关文章

  1. cocos2d-x 显示触摸操作(显示水波点击效果,用于视频演示)

    昨天刚刚參加玩游戏设计大赛, 积累了一些东西. 接下去将会逐个分享出来. 首先是显示触摸操作. 由于要演示我们的作品.使用试玩过程中, 假设没办法显示我们的触摸操作(像录制视频一样, 点击了屏幕某点, ...

  2. Android之Button自定义点击效果

    我们在界面上经常会用到button按钮,但通常button点击后看不到点击的效果,如果用户连续点击了两次,就会报NAR错误,这样交互性就比较差了.如果我们自定义了button点击效果,比如我们点击了b ...

  3. 在Android开发中,定时器一般有以下3种实现方法

    在Android开发中,定时器一般有以下3种实现方法: 原文地址http://www.360doc.com/content/12/0619/13/87000_219180978.shtml 一.采用H ...

  4. android selector设置button点击效果(具体)以及常见问题

    button的点击效果学习起来其实比較easy,此点对开发人员来说也是使用的比較频繁的一个知识点,与它相关的还有编辑框的获取焦点时改变背景颜色.选择button选择时改变字体颜色等等.这些其实都是用到 ...

  5. Android 纯代码加入点击效果

    项目中非常多的Button, 同一时候配置非常多button切图,Selector是不是非常烦, 使用以下这个类,就能够直接为Button添加点击效果. 不用多个图片,不用Selector. 使用方法 ...

  6. 给子元素设置margin-top无效果的一种解决方法

    在写一个登陆界面的时候,设置登录按钮的margin-top时出了问题 先是这么写的 <div style="margin-top:30px"> <a style= ...

  7. Android 文字自动滚动(跑马灯)效果的两种实现方法[特别好使]

    有时候在xml中写的跑马灯效果不滚动:原因有以下 Android系统中TextView实现跑马灯效果,必须具备以下几个条件: 1.android:ellipsize=”marquee” 2.TextV ...

  8. WPF 平板上按钮点击不触发,鼠标点击触发的两种解决方法

    今天运行在windows平板上的程序,有个功能是弹出子窗体,点弹出窗体的关闭按钮,要点好几次才能触发.网上找了找,也有人与我类似的情形. 解决方法如下: public static void Disa ...

  9. 我的Android进阶之旅------>Android Listview跳到指定条目位置的两种实现方法

    前言 今天实现ListView跳转到第一个条目位置时,使用smoothScrollToPosition(int position)方法跳转实现了,但是交互说不需要这样的动画效果,需要直接跳转到第一项, ...

随机推荐

  1. 基于Spring Security2与 Ext 的权限管理设计与兑现

    基于Spring Security2与 Ext 的权限管理设计与实现 一.Spring Security介绍 Spring Security的前身Acegi,其配置及使用相对来说复杂一些,因为要配置的 ...

  2. 中国网建SMS短信接口调用(java发送和接收手机短信)

    1.先注册账号,一定要填写好签名格式.不填会返回-51错误.   代码信息接口详细==>http://sms.webchinese.cn/api.shtml   . 2.测试代码 package ...

  3. Java基础——Servlet(一)

    在学习Servlet之前,需要首先学习一些关联性的知识. 一.动态网页程序 动态网页:它是网页中的偏功能性的部分也是最重要的部分.它不是我们平时所看见的页面特效,展示的效果.而是,一种交互行为.比如, ...

  4. Django REST framework基础:版本、认证、权限、限制

    1  认证.权限和限制 2  认证 2.1  自定义Token认证 2.1.1  表 2.1.2  定义一个登录视图: 2.1.3  定义一个认证类 2.1.4  视图级别认证 2.1.5  全局级别 ...

  5. CSS 关于屏幕适配REM

    这里不多说了,想详细了解的可以参考 2350305682 的博客 https://www.cnblogs.com/annie211/p/8118857.html 不想多深究,想先实现的看这(移动端): ...

  6. 设计模式原则(7)--Composition&AggregationPrinciple(CARP)--合成&聚合复用原则

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.定义:  要尽量使用合成和聚合,尽量不要使用继承. 2.使用场景: 要正确的选择合成/复用和继承,必须透彻地理 ...

  7. jsp登录显示

    1.登录成功设置session request.getSession().setAttribute("user", user); 2.前台test <div class=&q ...

  8. IDEA项目搭建一——使用Maven创建多模块项目

    废话不多说,直接开始吧,如果有哪里写的不多的,还望指出,谢谢 一.创建空项目EmpayProject File -> New -> Project 二.添加父模块Parent Module ...

  9. React Native 二维码扫描组件

    学rn得朋友们,你们知道rn开源项目吗?来吧看这里:http://www.marno.cn/(rn开源项目) React Native学习之路(9) - 注册登录验证的实现 + (用Fetch实现po ...

  10. springboot 学习之路 14(整合mongodb的Api操作)

    springboot整合mongodb: mongodb的安装和权限配置  请点击连接参考 mongodb集成 : 第一步:引如pom文件 第二步:配置文件配置mongodb路径: 第三步:关于mon ...