水波图形RippleDrawable

RippleDrawable是Android在5.0之后新增的图形类,它的作用是在点击时展示水波动画,从而提示用户在这里按压了屏幕。这个提示效果类似于状态图形StateListDrawable,区别在于,StateListDrawable使用一张静止图片表示按下状态,而RippleDrawable使用荡起涟漪的水波动画表示按压动作。

水波图形的用法很简单,先在xml文件中定义水波图形的规格,然后把视图的android:background属性设置为该图形,然后点击视图就会产生动画效果了。具体的水波样式主要有三种,说明如下:

1、没有边界限制的水波,这意味着允许水波动画充满整个视图,xml定义如下:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"  android:color="#ffaaaa">
</ripple>

下面是没有边界限制的水波效果截图:

2、有边界限制的水波,只能在规定范围内显示水波动画,范围边界由mask遮罩对象指定,xml定义如下:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"  android:color="#ffaaaa"><item  android:id="@android:id/mask"  android:drawable="@drawable/btn_nine_selector" />
</ripple>

下面是有边界限制的水波效果截图(无其它背景):

3、有边界限制的水波,且水波动画必须在指定的背景图形上显示,xml定义如下:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"  android:color="#ffaaaa"><item android:drawable="@drawable/btn_nine_selector" />
</ripple>

下面是有边界限制的水波效果截图(有其它背景):

方式二与方式三看起来很像,展示效果却不一样。方式二的遮罩图形,只起到指定边界的作用,本身并没有显示出来;而方式三的背景图形,不但指定了水波的边界,而且背景自身也会显示在屏幕上。

水波动画RippleView

RippleDrawable只支持Android5.0以后的系统,如果想在4.*系统上也能展示水波动画效果,就得自己编写水波动画的控件了。

水波动画的实现思路不难,主要是以触摸点为圆心,间隔很短时间不停地向外画圆圈,从而产生水波荡漾的动画效果。但在具体编码的时候,尚有几个功能需要特别注意:
1、水波图案不能被子控件遮挡,所以不能在onDraw方法中绘制水波,只能在dispatchDraw方法中绘制;
2、与RippleDrawable一样,自定义的水波也要有边界限制,因此要调用Canvas的clipRect方法进行范围限定;
3、为了区别是否按压,在按下状态时,应保持水波图案,只有松开手指后才会消失,故而需对手势的按下事件和放开事件区分判断;
4、随着水波扩散与消失,水波图案的颜色应当逐渐变淡,这样才符合现实生活中的情况;
5、对于按钮等控件,点击操作应延迟若干时长(如0.5秒)再处理具体事务,以便留出充裕时间播放水波动画;

下面是自定义水波动画的截图:

下面是自定义水波动画的关键代码片段:

 @Overrideprotected void dispatchDraw(Canvas canvas) {super.dispatchDraw(canvas);if (mPaint.getColor()==Color.TRANSPARENT || mTargetWidth<=0 || mTouchTarget==null) {return;}if (mRadius > mMinSize / 2) {mRadius += mRadiusGap * 4;} else {mRadius += mRadiusGap;}getLocationOnScreen(mLocation);int[] location = new int[2];mTouchTarget.getLocationOnScreen(location);int left = location[0] - mLocation[0];int top = location[1] - mLocation[1];int right = left + mTouchTarget.getMeasuredWidth();int bottom = top + mTouchTarget.getMeasuredHeight();canvas.save();canvas.clipRect(left, top, right, bottom); // 裁剪水波的范围canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint); // 画水波canvas.restore();if (mRadius <= mMaxRadius) {postInvalidateDelayed(mDelay, left, top, right, bottom);} else if (!bPressed) {if (mPaint.getColor() == mPaintColor) {mPaint.setColor(mPaintHalfColor); // 最后一次画水波,颜色减淡} else {mPaint.setColor(Color.TRANSPARENT); // 结束水波动画}postInvalidateDelayed(mDelay, left, top, right, bottom);}}@Overridepublic boolean dispatchTouchEvent(MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {// 获取水波动画的载体mTouchTarget = getTouchTarget(this, event.getRawX(), event.getRawY());if (mTouchTarget != null) {initChild(event, mTouchTarget);mPaint.setColor(mPaintColor);postInvalidateDelayed(mDelay);}} else if (event.getAction() == MotionEvent.ACTION_UP) {bPressed = false;postInvalidateDelayed(mDelay);}return super.dispatchTouchEvent(event);}private View getTouchTarget(View view, float x, float y) {View target = null;ArrayList<View> touchableViews = view.getTouchables();for (View child : touchableViews) {if (isTouchInView(child, (int) x, (int) y)) {target = child;break;}}return target;}private boolean isTouchInView(View view, int x, int y) {int[] location = new int[2];view.getLocationOnScreen(location);int left = location[0];int top = location[1];int right = left + view.getMeasuredWidth();int bottom = top + view.getMeasuredHeight();if (view.isEnabled() && x >= left && x <= right && y >= top && y <= bottom) {return true;} else {return false;}}

点击下载本文用到的水波图形与水波动画的工程代码

点此查看Android开发笔记的完整目录

Android开发笔记(一百三十一)水波图形与水波动画相关推荐

  1. Android开发笔记(三十一)SQLite游标及其数据结构

    ContentValues ContentValues类似于映射,也是用于存储键值对.区别之处在于ContentValues的键只能是字符串,查看ContentValues的源码,会发现其内部保存键值 ...

  2. Android开发笔记(六十一)文件下载管理DownloadManager

    下载管理DownloadManager 文件下载其实是网络数据访问的一种特殊形式,使用普通的http请求也能完成,就是实现起来会繁琐一些.因为下载功能比较常用,而且业务功能相对统一,所以从Androi ...

  3. Android开发笔记(五十一)通过Messenger实现进程间通信

    进程间通信IPC IPC是"Inter-Process Communication"的缩写,即进程间通信.Android为APP提供了多进程工作模式,这是因为多线程存在若干局限: ...

  4. Android开发笔记(八十一)屏幕规格适配

    Configuration 适配各种屏幕规格,首先要取到系统对于屏幕的配置信息,这些配置可从工具类Configuration获得.Configuration对象在Activity中通过调用getRes ...

  5. Android开发笔记(二十一)横幅轮播页Banner

    ViewPager ViewPager的概念 在前面的博文< Android开发笔记(十九)底部标签栏TabBar>中,我们提到可以在一个主页面里通过选项卡方式,切换到不同的子页面.那么在 ...

  6. Android开发笔记(三十八)列表类视图

    AdapterView AdapterView顾名思义是适配器视图,Spinner.ListView和GridView都间接继承自AdapterView,这三个视图都存在多个元素并排展示的情况,所以需 ...

  7. Android开发笔记(三十六)展示类控件

    View/ViewGroup View是单个视图,所有的控件类都是从它派生出来:而ViewGroup是个视图组织,所有的布局视图类都是从它派生出来.由于View和ViewGroup是基类,因此很少会直 ...

  8. Android开发笔记(三十七)按钮类控件

    Button与ImageButton Button是文本按钮(继承自TextView),而ImageButton是图像按钮(继承自ImageView).两者之间的区别在于: 1.Button即可显示文 ...

  9. Android开发笔记(三十五)页面布局视图

    布局视图的类别 布局视图有五类,分别是线性布局LinearLayout.相对布局RelativeLayout.框架布局FrameLayout.绝对布局AbsoluteLayout.表格布局TableL ...

  10. Android开发笔记(七十一)区分开发模式和上线模式

    为什么要区分两种模式 许多开发者(包括博主在内)都是闷骚的程序员,为了开发调试方便,常常在代码里加上日志,还经常在页面上各种弹窗提示.这固然有利于发现bug.提高软件质量,但过多的调试信息往往容易泄露 ...

最新文章

  1. centos7 安装Git
  2. [程序员创造力训练 1] 猜单词 - 关于健康
  3. 步步高java短信恢复_步步高智能手机自带的程序不见了怎么找回
  4. JavaScript从内容中筛选出手机号码集合
  5. C++抽象基类和纯虚成员函数
  6. Datepicker
  7. pythonjson序列化_Python Json序列化与反序列化的示例
  8. 2021-06-18激活函数的意义
  9. Git上手:四种常见的Git协同工作方式
  10. MySQL经常使用命令--create命令使用
  11. 拓端tecdat|基于keras平台CNN神经网络模型的服装识别分析
  12. xware for linux,Ubuntu 14.04安装迅雷Xware过程笔记
  13. java如何实现英文翻译中文,22年最新
  14. 非常逆天的六款Photoshop插件!
  15. Macbook怎么开启三指移动 ForceTouch TrackPad开启三指移动方法
  16. Msm8960(APQ8064)平台的MSM-AOSP-kitkat编译适配(8):wifi与蓝牙
  17. linux怎样安装麒麟双系统,win10系统装麒麟系统双系统的具体办法
  18. 代码之谜(五)- 浮点数(谁偷了你的精度?)
  19. U盘文件夹病毒,.exe病毒删除方法
  20. 线段覆盖 java,南邮 OJ 1407 线段覆盖

热门文章

  1. TensorFlow2.0:tensorboard使用
  2. TensorFlow2.0:张量限幅
  3. ubuntu安装teamviewer教程 linux安装teamviewer教程
  4. Linux编程(5)_静态库与动态库
  5. 使用webpack引入sass全局变量
  6. 阿里巴巴为什么选择Apache Flink?
  7. [PHP] 算法-邻接矩阵图的广度和深度优先遍历的PHP实现
  8. Splash广告界面
  9. 计算机游戏有哪几种,这些游戏你玩过几个? 你认为最经典的电脑游戏有哪些?
  10. 装系统可能会出现的问题--无法装在该磁盘,因为格式的原因