android l 效果,[原]Android L中水波纹点击效果的实现

博主参加了2014 CSDN博客之星评选,帮我投一票吧。

前言

前段时间android L(android 5.0)出来了,界面上做了一些改动,主要是添加了若干动画和一些新的控件,相信大家对view的点击效果-水波纹很有印象吧,点击一个view,然后一个水波纹就会从点击处扩散开来,本文就来分析这种效果的实现。首先,先说下L上的实现,这种波纹效果,L上提供了一种动画,叫做Reveal效果,其底层是通过拿到view的canvas然后不断刷新view来完成的,这种效果需要view的支持,而在低版本上没有view的支持,因此,Reveal效果没法直接在低版本运行。但是,我们了解其效果、其原理后,还是可以通过模拟的方式去实现这种效果,平心而论,写出一个具有波纹效果的自定义view不难,或者说很简单,但是,view的子类很多,如果要一一去实现button、edit等控件,这样比较繁琐,于是,我们想是否有更简单的方式呢?其实是有的,我们可以写一个自定义的layout,然后让layout中所有可点击的元素都具有波纹效果,这样做,就大大简化了整个过程。接下来本文就会分析这个layout的实现,在此之前,我们先看下效果。

60519820_1.gif

实现思想

首先我们自定义一个layout,这里我们选取LinearLayout,至于原因,文章下面会进行分析。当用户点击一个可点击的元素时,比如button,我们需要得到用户点击的元素的信息,包含:用户点击了哪个元素、用户点击的那个元素的宽、高、位置信息等。得到了button的信息后,我就可以确定水波纹的范围,然后通过layout进行重绘去绘制水波纹,这样水波纹效果就实现了,当然,这只是大概步骤,中间还是有一些细节需要处理的。

layout的选取

既然我们打算实现一个自定义layout,那我们要选取那个layout呢,LinearLayout、RelativeLayout、FrameLayout?我这里选用LinearLayout。为什么呢?也许有人会问,不应该用RelativeLayout吗?因为RelativeLayout比较强大,可以实现复杂的布局,但LinearLayout和FrameLayout就不行。没错,RelativeLayout是强大,但是考虑到水波效果是通过频繁刷新layout来实现的,由于频繁重绘,因此,我们要考虑性能问题,RelativeLayout的性能是最差的(因为做的事情多),因为,为了性能,我们选择LinearLayout,至于FrameLayout,它功能太简单了,不太适合使用。当实现复杂布局的时候,我们可以在具有波纹效果的元素外部包裹LinearLayout,这样重绘的时候不至于有过重的任务。

根据上面的分析,我们定义如下的layout:

public class RevealLayout extends LinearLayout implements Runnable

实现过程

实现过程主要是如下几个问题的解决:

1. 如何得知用户点击了哪个元素

2. 如何取得被点击元素的信息

3. 如何通过layout进行重绘绘制水波纹

4. 如果延迟up事件的分发

下面一一进行分析

如何得知用户点击了哪个元素

这个问题好弄,为了得知用户点击了哪个元素(这个元素一般来说要是可点击的,否则是无意义的),我们要提前拦截所有的点击事件,于是,我们应该重写layout中的dispatchTouchEvent方法,注意,这里不推荐用onInterceptTouchEvent,因为onInterceptTouchEvent不是一直会被回调的,具体原因请参看我之前写的view系统解析系列。然后当用户点击的时候,会有一系列的down、move、up事件,我们要在down的时候来确定事件落在哪个元素上,down的元素就是用户点击的元素,当然为了严谨,我们还要判断up的时候是否也落在同一个元素上面,因为,系统click事件的判断规则就是:down和up同时落在同一个可点击的元素上。

@Override

public boolean dispatchTouchEvent(MotionEvent event) {

int x = (int) event.getRawX();

int y = (int) event.getRawY();

int action = event.getAction();

if (action == MotionEvent.ACTION_DOWN) {

View touchTarget = getTouchTarget(this, x, y);

if (touchTarget.isClickable() && touchTarget.isEnabled()) {

mTouchTarget = touchTarget;

initParametersForChild(event, touchTarget);

postInvalidateDelayed(INVALIDATE_DURATION);

}

} else if (action == MotionEvent.ACTION_UP) {

mIsPressed = false;

postInvalidateDelayed(INVALIDATE_DURATION);

mDispatchUpTouchEventRunnable.event = event;

postDelayed(mDispatchUpTouchEventRunnable, 400);

return true;

} else if (action == MotionEvent.ACTION_CANCEL) {

mIsPressed = false;

postInvalidateDelayed(INVALIDATE_DURATION);

}

return super.dispatchTouchEvent(event);

}

通过上述代码,我们可以知道,当down的时候,我们取出点击事件的屏幕坐标,然后去遍历view树找到用户所点击的那个view,代码如下,就是判断事件的坐标是否落在view的范围内,这个不再多说了,比较好理解。需要注意的是,事件的坐标我们不能用getX和getY,而要用getRawX和getRawY,二者的区别是:前者是相对于被点击view的坐标,后者是相对于屏幕的坐标,而我们的目标view具体位于layout的哪一层我们无法知道,所以,必须用屏幕的绝对坐标来进行计算。而有了事件的坐标,再根据view在屏幕中的绝对坐标,只要判断事件的xy是否落在view的上下左右四个角之内,就可以知道事件是否落在view上,从而取出用户所点击的那个view。

private View getTouchTarget(View view, int x, int y) {

View target = null;

ArrayList TouchableViews = view.getTouchables();

for (View child : TouchableViews) {

if (isTouchPointInView(child, x, y)) {

target = child;

break;

}

}

return target;

}

private boolean isTouchPointInView(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.isClickable() && y >= top && y <= bottom

&& x >= left && x <= right) {

return true;

}

return false;

}

如何取得被点击元素的信息

这个比较简单,被点击元素的信息有:宽、高、left、top、right、bottom,获取它们的代码如下:

int[] location = new int[2];

mTouchTarget.getLocationOnScreen(location);

int left = location[0] - mLocationInScreen[0];

int top = location[1] - mLocationInScreen[1];

int right = left + mTouchTarget.getMeasuredWidth();

int bottom = top + mTouchTarget.getMeasuredHeight();

说明:mTouchTarget指的是用户点击的那个view

如何通过layout进行重绘绘制水波纹

这个会水波纹比较简单,只要用drawCircle绘制一个半透明的圆环即可,这里主要说下绘制时机。一般来说,我们会选择在onDraw中去进行绘制,这是没错的,但是对于L中的效果不太适合,查看view的绘制过程,我们会明白,view的绘制大致遵循如下流程:先绘制背景,再绘制自己(onDraw),接着绘制子元素(dispatchDraw),最后绘制一些装饰等比如滚动条(onDrawScrollBars),因此,如果我们在onDraw中绘制波纹,那么由于子元素的绘制在onDraw之后,就会导致子元素盖住我们所绘制的圆环,这样,圆环就有可能看不全了,因为,把我绘制的时机很重要。根据view的绘制流程,我们选择dispatchDraw比较合适,当所有的子元素都绘制完成后,再进行波纹的绘制。读到这里,大家会更加明白,为什么我们要选择LinearLayout以及为什么不建议view的嵌套层级太深,因为如果view本身比较重或者嵌套层级太深,就会导致dispatchDraw执行的耗时增加,这样水波的绘制就会收到些许影响。因此,性能的平滑在代码中也很重要,也是需要考虑的。同时,为了不让绘制的圆环超出被点击元素的范围,我们需要对canvas进行clip。为了有波纹效果,我们需要频繁地进行layout重绘,并且在重绘的过程中改变圆环的半径,这样一个动态的水波纹就出来了。仍然,我来性能的考虑,我们选择用postInvalidateDelayed(long delayMilliseconds, int left, int top, int right, int bottom)来进行view的部分重绘,因为,其他区域是不需要重绘的,仅仅是被点击的元素所在的区域需要重绘。为什么要采用Delayed这个方法,原因是我们不能一直进行刷新,必须有一点点时间间隔,这样做的好处是:避免view的重绘抢占过多时间片从而造成潜在的间接栈溢出,因为invalidate会直接导致draw的调用。

具体代码如下:

protected void dispatchDraw(Canvas canvas) {

super.dispatchDraw(canvas);

if (!mShouldDoAnimation || mTargetWidth <= 0 || mTouchTarget == null) {

return;

}

if (mRevealRadius > mMinBetweenWidthAndHeight / 2) {

mRevealRadius += mRevealRadiusGap * 4;

} else {

mRevealRadius += mRevealRadiusGap;

}

int[] location = new int[2];

mTouchTarget.getLocationOnScreen(location);

int left = location[0] - mLocationInScreen[0];

int top = location[1] - mLocationInScreen[1];

int right = left + mTouchTarget.getMeasuredWidth();

int bottom = top + mTouchTarget.getMeasuredHeight();

canvas.save();

canvas.clipRect(left, top, right, bottom);

canvas.drawCircle(mCenterX, mCenterY, mRevealRadius, mPaint);

canvas.restore();

if (mRevealRadius <= mMaxRevealRadius) {

postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);

} else if (!mIsPressed) {

mShouldDoAnimation = false;

postInvalidateDelayed(INVALIDATE_DURATION, left, top, right, bottom);

}

}

到此为止,这个layout我们已经实现了,但是细心的你,一定会发现,还有什么不妥的地方。比如,你可以给button加一个点击事件,当button被点击的时候起一个activity,很快你就会发现问题所在了:水波还没播完呢,activity就起来了,导致水波效果大打折扣,而仔细观察android L的效果,我们发现,L中总是要等到水波效果播放完毕才会进行下一步的行为。所以,最后一个待解决的问题也就出来了,请看下面的分析

如何延迟up事件的分发

针对上面所说的问题,如果我们能够延迟up时间的分发,比如延迟400ms,这样水波就有足够的时间去播放完毕,然后再分发up事件,这样就可以解决问题。最开始,我的确是这样做的,先看如下的代码:

else if (action == MotionEvent.ACTION_UP) {

mIsPressed = false;

postInvalidateDelayed(INVALIDATE_DURATION);

mDispatchUpTouchEventRunnable.event = event;

postDelayed(mDispatchUpTouchEventRunnable, 400);

return true;

}

可以发现,当up的时候,我并没有直接走系统的分发流程,只是强行消耗点up事件然后再延迟分发,请看代码:

private class DispatchUpTouchEventRunnable implements Runnable {

public MotionEvent event;

@Override

public void run() {

if (mTouchTarget == null || !mTouchTarget.isEnabled()) {

return;

}

if (isTouchPointInView(mTouchTarget, (int)event.getRawX(), (int)event.getRawY())) {

mTouchTarget.dispatchTouchEvent(event);

}

}

};

到此为止,上述几个问题都已经分析完毕了,我们就可以轻易地实现水波纹的点击效果了。

源码下载

本文中的demo源码暂时未开放到互联网上,请加群 215680213 ,在群共享中下载源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/442522.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html表情选择器,原生JS写的emoji表情选择器

//生成表情window.onload function() {var face document.getElementById(‘face‘);for(var i 0; i < 38; i) {var a document.createElement("a");a.href "javascript:;";if(i < 10) {a.innerHTML ‘‘;}else{a.innerHTML ‘‘;}face.appendCh…

3dmax里面cr材质转换vr材质_3DMAX零基础入门视频全套教程

3Dmax基础教程全套视频&#xff1a;点链接就行1.3dmax界面介绍2、3dmax主工具栏3、创建面板与修改面板4、时间轴与视口按钮5、3D视口讲解6、3dmax样条线界面7、CAD如何导入3dmax8、3DMAX绘制初步空间9、3DMAX导入图片与车削10、倒角和倒角剖面11、修改器堆栈|12、弯曲命令13、对…

html5 自定义 datepicker,如何使用 React 构建自定义日期选择器(3)

本文作者&#xff1a;IMWeb howenhuo未经同意&#xff0c;禁止转载Datepicker 组件构建 Datepicker 组件要开始构建 Datepicker 组件&#xff0c;请将以下代码片段添加到 src/components/Datepicker/index.js 文件。import React from "react";import PropTypes from…

群签名和环签名的区别_超级签名和TF签名使用个人开发者账号的区别是什么?...

了解过当前ios签名的朋友都知道&#xff0c;目前ios签名共分为企业签名、超级签名和TF签名&#xff0c;其中企业签名作为签名行业的“老大哥”&#xff0c;深受各路开发者和App运营商的喜爱。而我们今天的主角却是其他两种&#xff1a;超级签名和TF签名。这两种签名方式与企业签…

matlab水蒸气焓值计算_从第一性原理计算出发来理解含能配合物宏观行为的趋势...

欢迎关注微信公众平台"计算材料学"&#xff0c;微信ID&#xff1a;jisuancailiao近日&#xff0c;北京理工大学物理学院郭伟课题组&#xff08;博士研究生孙矗丽&#xff09;与北京理工大学爆炸科学与技术国家重点实验室、机电学院张同来课题组在Physical Chemistry …

抖音文字时钟壁纸html,抖音文字时钟app

抖音文字时钟app是一款蛮火蛮有趣的复古罗盘时钟应用&#xff0c;喜欢刷抖音的用户是不是在抖音上看到了这些的时钟&#xff0c;你是不是对这个罗盘产生了兴趣&#xff0c;今天小编为你提供了设置这个时钟的app&#xff0c;当前只有安卓版&#xff0c;苹果版暂未上线&#xff0…

计算机网络十进制转二进制的应用题,【网络-理论】二进制与十进制的转换

由于计算机中运行的数据都是以二进制数的形式存在的&#xff0c;学习二进制数的计算成为计算机专业必备的一门知识。概述正如字面上的意思&#xff1a;二进制数&#xff0c;满二进一&#xff0c;所以说二进制只由 数字0和数字1组成。十进制&#xff0c;满十进一&#xff0c;所以…

最大化_怎样保证油压缓冲器工作效率最大化?

对于油压缓冲器&#xff0c;广大使用者在采购前普遍考虑的是油压缓冲器的价格、质量、品牌。挑选出合适的油压缓冲器仅仅是第一步&#xff0c;提高其工作效率&#xff0c;增加产能&#xff0c;才能为企业创造更多价值。要保证油压缓冲器工作效率最大化&#xff0c;需要做到以下…

aosp 本地版本管理_谈 DevOps 平台实施:我在本地跑明明成功的,为什么在你平台跑就报错?...

我在本地跑明明成功的&#xff0c;为什么在你平台跑就报错&#xff1f;用户在 Jenkins 上跑构建时&#xff0c;失败了&#xff0c;把日志截图给我看&#xff0c;如下图&#xff1a;在过去几个月&#xff0c;每个星期都会有一两个 Jenkins 用户就会给我发送类似的错误日志。这样…

江苏大学2020计算机考研,江苏大学2020年硕士研究生复试时间及主要安排

根据《江苏大学2020年硕士研究生招生复试及录取办法》(江大研〔2020〕3号)&#xff0c;为做好我校2020年硕士研究生复试及录取工作&#xff0c;现将有关工作安排通知如下&#xff1a;一、复试人选符合我校复试分数线要求的一志愿考生、符合各学院(中心、研究院、所&#xff0c;…

尼康d7200拍照_为什么尼康和佳能的全画幅旗舰单反却只有2000多万像素?

华为P20Pro摄像头都做到了4000万像素&#xff0c;为什么尼康和佳能的全画幅顶级单反却只有2000多万像素&#xff1f;下面我们先来看看目前市场上摄影器材的像素情况。单机身售价高达3万多的尼康旗舰全画幅单反D5总像素是2133万&#xff0c;有效像素是2082万&#xff1b;单机身售…

国家级一级计算机考试题,国家级计算机一级考试试题

国家级计算机一级考试试题1.在Windows中&#xff0c;打开"资源管理器"窗口后&#xff0c;要改变文件或文件夹的显示方式&#xff0c;应选用( )。CA、"文件"菜单B、"编辑"菜单C、"查看"菜单D、"帮助"菜单2.在Windows的"…

重物码垛搬运机器人_节卡机器人:5G下的智慧物流——柔性生产物流系统

随着经济的发展&#xff0c;制造一直面临着劳动力成本迅速攀升、产能过剩、竞争激烈、客户个性化需求日益增长等问题。另一方面招工难&#xff0c;以及缺乏专业人才&#xff0c;制造业转型迫在眉睫。现在&#xff0c;5G、物联网、协作机器人、机器视觉等新兴技术迅速兴起&#…

霍纳法树形流图中处理机p个数_处理机管理(进程管理)

一、进程1.1.进程的定义程序关于某个数据集合的一次执行过程1.2.进程的特征(与程序比较&#xff09;结构特征进程控制块(PCB) 程序 数据 进程实体动态性 -- 最基本特征进程&#xff1a;进程实体的一次执行过程&#xff0c;有生命周期程序&#xff1a;程序是一组有序指令的集…

元胞计算机系统,元胞自动机的应用

【定义】元胞自动机(Cellular Automata, CA)定义在一个具有离散、有限状态的元胞组成的元胞空间上&#xff0c;并按照一定的局部规则&#xff0c;在离散的时间维度上演化的动力学系统。【构成】可以视为由一个元胞空间和定义于该空间的变换函数所组成【构形】在某个时刻&#x…

vue获取当前月最后一天_10月的最后一天,有哪些不想谈恋爱适合发朋友圈的文案?...

小时光提醒&#xff1a;今天是10月月的最后一天&#xff0c;凡是遇往&#xff0c;皆为序章。没关系的&#xff0c;不要给自己太大压力&#xff0c;生活不是选择&#xff0c;而是热爱&#xff01;“慢慢又漫漫&#xff0c;漫漫亦灿灿”我的意思是&#xff0c;所有等待的日子&…

手机psp模拟器哪个好_功能强大,手机微信群控系统和云控哪个好?

互联网信息技术在发展的同时&#xff0c;也在不断刷新我们对新科技的认知。随着微营销发展的风生水起&#xff0c;手机微信群控和云控出现了&#xff0c;主要就是通过一台电脑控制几十上百部手机&#xff0c;场面十分震撼&#xff0c;这样的黑科技&#xff0c;你了解过吗&#…

pandas中size方法_如何使用pdpipe与Pandas构建管道?

作者 | Tirthajyoti Sarkar译者 | 清儿爸编辑 | 夕颜来源 | AI科技大本营(ID: rgznai100)【导读】Pandas 是 Python 生态系统中的一个了不起的库&#xff0c;用于数据分析和机器学习。它在 Excel/CSV 文件和 SQL 表所在的数据世界与 Scikit-learn 或 TensorFlow 施展魔力的建模…

软件测试ipad电池,ipad2020电池有问题是真的吗

ipad2020电池有问题是真的吗&#xff1f;近期&#xff0c;ipad2020电池翻车事件传播的沸沸扬扬&#xff0c;很多人都想退货&#xff0c;也有人说是爱思助手没做适配的问题。如果你担心自己入手的ipad2020电池健康有问题&#xff0c;可以看看本站提供的介绍哦&#xff01;ipad20…

惯性制导精度是多少_航天装备的命中精度

1航天装备的精度航天装备的精度&#xff0c;即航天装备的命中精度&#xff0c;在打击固定目标时&#xff0c;航天装备命中精度用圆概率偏差(CEP)描述。设P为落点坐标落在以目标为原点R为半径的圆内的概率&#xff0c;当P50%时&#xff0c;R就是圆概率偏差CEP&#xff0c;即航天…