android+水滴粘性动画,Android控件实现水滴效果

看到ios版上QQ刷新效果像水滴,然后自己也想着去实现这样的效果,这篇文章暂时没有介绍下拉刷新的效果,只是单独用一个控件来实现这样的水滴效果。

效果图如下:

02242f8eaa20786556d5754df133229c.gif

一、总体思路

1、画两个圆形,其中一个就是上面的大圆,还有一个是下面的小圆,大圆和小圆不断变小,大圆的位置保持不变,小圆的位置不断向下移动,即圆心不断下移。

2、画两边的曲线,这时候用到贝塞尔曲线去画。

3、用属性动画实现动态的效果。

二、代码实现

1、找出画曲线的几个关键点。

8388c73cc6f5d222821d225942121f29.png

2e8c7a9fcea4c8c9a69cd9e76ff7115a.png

其实我是在第一张图的基础上,再在上面分别画两个圆,就可以得到第二张图了。关键是画出第一张图。

(1)在这里,p1,p2,p3,p4,这4个点分别对应两个圆的两边的点,即p1到p2就是圆的直径。p3和p4同理,那么就很容易确定这四个点的坐标了。

(2)然后c1和c2是分别控制p1到p3、p2到p4的曲线,是贝塞尔曲线的控制点。它们的横坐标对应的是p3,p4的横坐标(相等),纵坐标取两个圆心距离的一半。这样画出这个静态的图片就不难了。

(3)画上下两个圆进去,就会变成第二张图的效果。

2、在构造方法中调用init()初始化一些基本的变量

private void init(Context context, AttributeSet attrs) {

drawFilter = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG

| Paint.FILTER_BITMAP_FLAG);

paint = new Paint();

paint.setColor(fillColor);

// 转换为像素单位

bigRadius = dip2px(context, bigRadius);

smallRadius = dip2px(context, smallRadius);

distance = dip2px(context, distance);

}

3、在onDraw()方法中画水滴效果

要注意的是path需要重新new, 贝塞尔曲线的绘制,用到是path.quadTo这方法。具体可以看代码。

@Override

protected void onDraw(Canvas canvas) {

// 必须重新new,不然路径会重复,我之前就是这样

path = new Path();

// 把画布移到中心

canvas.translate(width / 2, height / 2);

// 从canvas层面去除绘制时锯齿

canvas.setDrawFilter(drawFilter);

// 当前的两个圆心的距离

currentDis = distance * fraction;

// 计算当前大圆的半径

float bigRadius = this.bigRadius - currentDis / bigPercent;

float smallRadius = 0;

if (currentDis > 5) {// 距离大于5才改变小圆的半径

smallRadius = this.smallRadius - currentDis / smallPercent;

}

// 大圆两边的两个点坐标

leftX = -bigRadius;// 大圆当前的半径

leftY = rightY = 0;

rightX = bigRadius;// 大圆当前的半径

// 小圆两边的两个点坐标

leftX2 = -smallRadius;// 小圆当前的半径

leftY2 = rightY2 = currentDis;

rightX2 = -leftX2; // 小圆当前的半径

// 两个控制点

controlX1 = -smallRadius;// x坐标取小圆当前的半径大小

controlY1 = currentDis / 2;// y坐标取两个圆距离的一半

controlX2 = smallRadius;// x坐标取小圆当前的半径大小

controlY2 = currentDis / 2;// y坐标取两个圆距离的一半

path.moveTo(leftX, leftY);

path.lineTo(rightX, rightY);

// 用二阶贝塞尔曲线画右边的曲线,参数的第一个点是右边的一个控制点

path.quadTo(controlX2, controlY2, rightX2, rightY2);

path.lineTo(leftX2, leftY2);

// 用二阶贝塞尔曲线画左边边的曲线,参数的第一个点是左边的一个控制点

path.quadTo(controlX1, controlY1, leftX, leftY);

// 画大圆

canvas.drawCircle(0, 0, bigRadius, paint);

// 画小圆

canvas.drawCircle(0, currentDis, smallRadius, paint);

// 画path

canvas.drawPath(path, paint);

}

4、用属性动画,实现动态的效果。

/*** 执行属性动画,实现水滴的效果 */

public void perforAnim() {

ValueAnimator valAnimator = ObjectAnimator.ofFloat(0, 1);

valAnimator.addUpdateListener(new AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

fraction = (float) animation.getAnimatedValue();

postInvalidate();

}

});

valAnimator.setDuration(duration);

valAnimator.start();

}

5、重写onMeasure()方法,处理wrap_content情况。

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

/*

* 处理为wrap_content情况,那么它的specMode是AT_MOST模式,在这种模式下它的宽/高

* 等于spectSize,这种情况下view的spectSize是parentSize,而parentSize是

* 父容器目前可以使用大小,就是父容器当前剩余的空间大小, 就相当于使用match_parent一样 的效果,因此我们可以设置一个默认的值

*/

int widthSpectMode = MeasureSpec.getMode(widthMeasureSpec);

int widthSpectSize = MeasureSpec.getSize(widthMeasureSpec);

int heightSpectMode = MeasureSpec.getMode(heightMeasureSpec);

int heightSpectSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthSpectMode == MeasureSpec.AT_MOST

&& heightSpectMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(width, height);

} else if (widthSpectMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(width, heightSpectSize);

} else if (heightSpectMode == MeasureSpec.AT_MOST) {

setMeasuredDimension(widthSpectSize, height);

}

}

6、其它的一些方法实现。

@Override

protected void onLayout(boolean changed, int left, int top, int right,

int bottom) {

super.onLayout(changed, left, top, right, bottom);

if (changed) {

width = right - left;

height = bottom - top;

}

}

/**

* 根据手机的分辨率从 dp 的单位 转成为 px(像素)

*/

public int dip2px(Context context, float dpValue) {

final float scale = context.getResources().getDisplayMetrics().density;

return (int) (dpValue * scale + 0.5f);

}

7、字段的定义

private final int fillColor = 0xff999999;// 填充颜色

private Paint paint;

private int width = 100, height = 300;// 默认宽高

/* 两个圆心的最大距离 /

private int distance = 60;

/* 当前两个圆心的距离 /

private float currentDis = 0;

private float bigRadius = 20;// 大圆半径

private float smallRadius = 10;// 小圆半径

private float controlX1, controlX2, controlY1, controlY2;// 两个控制点的坐标

private float leftX, leftY, rightX, rightY;// 大圆两边的两个点的坐标

private float leftX2, leftY2, rightX2, rightY2; // 小圆两边的两个坐标

DrawFilter drawFilter;

Path path;

/* 由属性动画控制,范围为0-1 */

float fraction = 0;// 比例值

/* 大圆半径变化的比例 /

private final int bigPercent = 8;

/* 小圆半径变化的比例 /

private final int smallPercent = 20;

// 动画的执行时间

private long duration = 3000;

三、总结

一种动画效果,应该先分析它的静态的实现,然后添加动态的效果,这样就比较容易实现它的动画效果了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

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

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

相关文章

第一台鸿蒙手机是,第一台预装鸿蒙OS的手机终于登场。

原标题:第一台预装鸿蒙OS的手机终于登场。千呼万唤始出来,之前大家期待了很久的鸿蒙OS它终于真正到来了。据工业和信息化部公布的消息,搭载鸿蒙OS的新款华为手机正式入网了,这也将是华为第一台预装鸿蒙OS的新款手机。但令人完全没…

html跳转网页为什么网页无法访问,朋友的网站被网址跳转,导致官网无法正常访问...

原标题:朋友的网站被网址跳转,导致官网无法正常访问昨天中午接到客户的一个电话,告知其某个网站打开之后直接跳转到其他的网站。客户的这个网站,不是我们做的,但是关系一直保持的不错,所以就顺带给他解决一…

2021中卫一中高考成绩查询,2021年宁夏高中排名及分数线 高考本科升学率排行榜...

2019年宁夏高中排名及分数线 高考本科升学率排行榜如何判断一所高中学校的优劣好坏,人们往往从这几个方面来看:高考/竞赛成绩、生源质量、师资力量、软硬件设施、管理水平等。高考成绩因为最直接,最广为关注,但是很多时候未免有失…

微观经济学如何计算机会成本,【微观经济学】机会成本

概念:机会成本机会成本是指你做了某项选择,而不得不因此失去的其他利益。比如你选择了A,就必须放弃B,B就是A的机会成本。对企业来说,最优方案的机会成本,就是次优方案可能带来的收益。机会成本是听起来很简…

计算机网络技术俄罗斯方块,《The Tetris Effect》:这本新书讲述了俄罗斯方块的传奇故事...

原标题:《The Tetris Effect》:这本新书讲述了俄罗斯方块的传奇故事在游戏发展史上,俄罗斯方块是个传奇。没有一款游戏像它一样影响深远,受到全球不同人的喜爱。在即将出版的《The Tetris Effect》(俄罗斯方块效应)中,科技记者 Da…

计算机主机响是什么原因,电脑主机发出不同的响声及解决措施

电脑在使用一段时间后,难免都会遇到些小问题。如很多朋友使用的台式电脑,在开机的时候,主机就会发出很大的响声。其实当主机发出不同响声时,就代表着不同的故障,我们可以根据相应的响声来找出故障的所在。今天U大侠小编…

计算机上的键有什么功能吗,电脑功能键都有什么用,电脑上功能键的用法_系统圣地...

电脑大家都使用了不少年了,但是对于键盘最上方的F1到F12的功能都是什么,相信不少小伙伴都一知半解吧,毕竟平时使用的比较少,但是在某些情况下,这些功能键能够有效的提升你的做事效率,所以了解一下还是不错的…

云测试软件详解,软件测试之登录测试详解

一、功能测试–登录功能性测试用例包括:1.什么都不输入,点击提交按钮,看提示信息。(非空检查)2.输入已注册的用户名和正确的密码,验证是否登录成功;3.输入已注册的用户名和不正确的密码,验证是否登录失败&a…

计算机启用时间 查找方式,电脑实用知识技巧 篇六:不需要第三方软件,这种方法查看系统启动时间...

电脑实用知识技巧 篇六:不需要第三方软件,这种方法查看系统启动时间2019-04-04 09:19:210点赞0收藏0评论上次我们说到:开机小助手,要让我们看到开机时间,必须添加自启动项目,这将拖慢我们的开机速度。有没有…

迷你世界显示未连接服务器成功,迷你世界登录未成功是什么意思 | 手游网游页游攻略大全...

发布时间:2017-08-29迷你世界两周年庆活动到来,那么两周年庆迷你世界有哪些活动呢?很多小伙伴都不了解吧,那么下面牛游戏小编就给大家仔细的介绍一下迷你世界两周年庆活动吧,希望能给大家带来帮助. 迷你世界两周年庆活动 世界守护计划--环保筑梦师 ...标签&#xf…

广播 消息 没有服务器,服务器节点消息广播

服务器节点消息广播 内容精选换一换添加节点时提示“添加节点失败,节点已存在”。待添加节点的服务器上已安装系统性能分析或者添加过节点。如果待添加节点的服务器上已安装系统性能分析,需要登录服务器卸载系统性能分析,详细步骤请参见卸载&…

微信收款音响s3服务器断开,微信收款音响s2和s3有什么区别

微信收款音响s2和s3的区别:1、S2是一款四角梯形形状的音箱,S3是一款圆角正方体形状的音箱。2、S2电池1200mAh,S3电池1800mAh。3、S3可以连接WiFi,S2都不可以。音响(Audio electronics)广义上是指一种利用电子回路设计进行音讯与电…

手机访问服务器中的数据库文件,手机连接服务器数据库文件在哪里

手机连接服务器数据库文件在哪里 内容精选换一换通过PostgreSQL客户端连接实例的方式有非SSL连接和SSL连接两种,其中SSL连接通过了加密功能,具有更高的安全性。绑定弹性公网IP并设置安全组规则。对目标实例绑定弹性公网IP。关于如何绑定弹性公网IP&#…

浅谈C语言字节对齐

首先,我们得知道为什么要进行内存对齐,它的意义何在?在这儿可以先看这样一张图。(手绘请见谅!!!) 我们知道,在32位CPU下,一个读取周期可以读取四个字节。一个…

C语言中函数调用中的传值与传址

首先介绍一下函数中传值与传址的概念: 传值:传值,实际是把实参的值赋值给行参,相当于copy。那么对行参的修改,不会影响实参的值 。传址: 实际是传值的一种特殊方式,只是他传递的是地址&#xf…

C语言交换两个变量数值的几种方法

因为经常见到这类题目&#xff0c;就自己总结了以下几种办法 1. 创建中间变量 这是最快也是最简单的办法&#xff0c;例如&#xff1a; #include<stdio.h>int main() {int a10;int b20;int temp;printf("交换前a,b的值为:\n");printf("a%d\n",a);…

vim中自动添加文件的作者、时间信息、版本等

1、linux系统版本&#xff1a;ubuntu-10.10 2、打开&#xff1a;vim ~/.vimrc 在文件末尾添加如下内容&#xff0c;如图一 &#xff08;图一&#xff09; 3、新建文件后直接按“F4”可插入作者文件信息&#xff0c;如图二所示 &#xff08;图二&#xff09;

数组的下标越界与内存溢出

很相似的两个概念&#xff0c;一不小心就会混淆 首先&#xff0c;对两个名词做一个大概的解释&#xff1a; 下标越界 在引用数组元素时&#xff0c;使用的下标超过了该数组下标的应有范围&#xff0c;但应注意的是&#xff1a; C/C不对数组做边界检查。 可以重写数组的每一…

二分查找(折半查找)

二分查找(折半查找)&#xff1a; 从有序序列中找到给出的要查询的数字。 原理是&#xff1a;首先把一个有序序列中间位置的值与要查找的数比较&#xff0c;若相等则找到了有序序列中的此数&#xff1b;否则比较两者的大小&#xff0c;若前者大&#xff0c;则把有序序列的中间…

C语言实现用星号在屏幕上打印菱形

很多人第一感觉肯定都是&#xff1a;很简单啊&#xff0c;不就是多写几个printf 语句嘛 像这样&#xff1a; #define _CRT_SECURE_NO_WARNINGS 1#include<stdio.h>int main() {printf(" *\n");printf(" ***\n");printf(" *****\n");p…