服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Android - Android控件实现水滴效果

Android控件实现水滴效果

2022-09-27 15:50LHavoc Android

这篇文章主要为大家详细介绍了Android控件实现水滴效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

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

效果图如下:

Android控件实现水滴效果

一、总体思路

1、画两个圆形,其中一个就是上面的大圆,还有一个是下面的小圆,大圆和小圆不断变小,大圆的位置保持不变,小圆的位置不断向下移动,即圆心不断下移。
2、画两边的曲线,这时候用到贝塞尔曲线去画。
3、用属性动画实现动态的效果。

二、代码实现

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

Android控件实现水滴效果Android控件实现水滴效果

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

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
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这方法。具体可以看代码。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
@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、用属性动画,实现动态的效果。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/*** 执行属性动画,实现水滴的效果 */
  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情况。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@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、其它的一些方法实现。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@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、字段的定义

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;

三、总结

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

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

原文链接:https://blog.csdn.net/LHavoc/article/details/54603197

延伸 · 阅读

精彩推荐