android controlx1,Android控件实现水滴效果

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

效果图如下:

1551058273x1551O05I88273.gif

一、总体思路

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

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

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

二、代码实现

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

1551058273f1551058C2Y7y3.png

1551058273155105lf83d273.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;

三、总结

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值