Android开发笔记(一百三十一)水波图形与水波动画
水波图形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开发笔记(一百三十一)水波图形与水波动画相关推荐
- Android开发笔记(三十一)SQLite游标及其数据结构
ContentValues ContentValues类似于映射,也是用于存储键值对.区别之处在于ContentValues的键只能是字符串,查看ContentValues的源码,会发现其内部保存键值 ...
- Android开发笔记(六十一)文件下载管理DownloadManager
下载管理DownloadManager 文件下载其实是网络数据访问的一种特殊形式,使用普通的http请求也能完成,就是实现起来会繁琐一些.因为下载功能比较常用,而且业务功能相对统一,所以从Androi ...
- Android开发笔记(五十一)通过Messenger实现进程间通信
进程间通信IPC IPC是"Inter-Process Communication"的缩写,即进程间通信.Android为APP提供了多进程工作模式,这是因为多线程存在若干局限: ...
- Android开发笔记(八十一)屏幕规格适配
Configuration 适配各种屏幕规格,首先要取到系统对于屏幕的配置信息,这些配置可从工具类Configuration获得.Configuration对象在Activity中通过调用getRes ...
- Android开发笔记(二十一)横幅轮播页Banner
ViewPager ViewPager的概念 在前面的博文< Android开发笔记(十九)底部标签栏TabBar>中,我们提到可以在一个主页面里通过选项卡方式,切换到不同的子页面.那么在 ...
- Android开发笔记(三十八)列表类视图
AdapterView AdapterView顾名思义是适配器视图,Spinner.ListView和GridView都间接继承自AdapterView,这三个视图都存在多个元素并排展示的情况,所以需 ...
- Android开发笔记(三十六)展示类控件
View/ViewGroup View是单个视图,所有的控件类都是从它派生出来:而ViewGroup是个视图组织,所有的布局视图类都是从它派生出来.由于View和ViewGroup是基类,因此很少会直 ...
- Android开发笔记(三十七)按钮类控件
Button与ImageButton Button是文本按钮(继承自TextView),而ImageButton是图像按钮(继承自ImageView).两者之间的区别在于: 1.Button即可显示文 ...
- Android开发笔记(三十五)页面布局视图
布局视图的类别 布局视图有五类,分别是线性布局LinearLayout.相对布局RelativeLayout.框架布局FrameLayout.绝对布局AbsoluteLayout.表格布局TableL ...
- Android开发笔记(七十一)区分开发模式和上线模式
为什么要区分两种模式 许多开发者(包括博主在内)都是闷骚的程序员,为了开发调试方便,常常在代码里加上日志,还经常在页面上各种弹窗提示.这固然有利于发现bug.提高软件质量,但过多的调试信息往往容易泄露 ...
最新文章
- centos7 安装Git
- [程序员创造力训练 1] 猜单词 - 关于健康
- 步步高java短信恢复_步步高智能手机自带的程序不见了怎么找回
- JavaScript从内容中筛选出手机号码集合
- C++抽象基类和纯虚成员函数
- Datepicker
- pythonjson序列化_Python Json序列化与反序列化的示例
- 2021-06-18激活函数的意义
- Git上手:四种常见的Git协同工作方式
- MySQL经常使用命令--create命令使用
- 拓端tecdat|基于keras平台CNN神经网络模型的服装识别分析
- xware for linux,Ubuntu 14.04安装迅雷Xware过程笔记
- java如何实现英文翻译中文,22年最新
- 非常逆天的六款Photoshop插件!
- Macbook怎么开启三指移动 ForceTouch TrackPad开启三指移动方法
- Msm8960(APQ8064)平台的MSM-AOSP-kitkat编译适配(8):wifi与蓝牙
- linux怎样安装麒麟双系统,win10系统装麒麟系统双系统的具体办法
- 代码之谜(五)- 浮点数(谁偷了你的精度?)
- U盘文件夹病毒,.exe病毒删除方法
- 线段覆盖 java,南邮 OJ 1407 线段覆盖
热门文章
- TensorFlow2.0:tensorboard使用
- TensorFlow2.0:张量限幅
- ubuntu安装teamviewer教程 linux安装teamviewer教程
- Linux编程(5)_静态库与动态库
- 使用webpack引入sass全局变量
- 阿里巴巴为什么选择Apache Flink?
- [PHP] 算法-邻接矩阵图的广度和深度优先遍历的PHP实现
- Splash广告界面
- 计算机游戏有哪几种,这些游戏你玩过几个? 你认为最经典的电脑游戏有哪些?
- 装系统可能会出现的问题--无法装在该磁盘,因为格式的原因