HarmonyOS 自定义UI之水波纹效果 原创 精华

深开鸿开发板
发布于 2021-9-3 09:53
浏览
7收藏

概述

​ 当点击一个view时,然后一个水波纹就会从点击处扩散开来,模拟水波背景动效实现。

实现思路

​ 任何东西都可以在生活中找到案例,我们要做水波纹效果,就想象一下,每个人应该都有把石头扔进进水里的经历,首先水波是从中心点的小圆慢慢放大为大圆,然后慢慢消失,我们模拟的时候只需要画圆,通过Canvas. drawCircle(float x, float y, float radius, Paint paint); 方法来实现,通过一点点放大半径在根据变化速率设置透明度的方式实现。

效果如下:

HarmonyOS  自定义UI之水波纹效果-鸿蒙开发者社区

实现过程:

通过计算view的最大宽度 / 2为布局的最大半径,最小半径就是0然后给它一个插值动画让它动起来,每次点击就让它绘制圆,圆的半径为 (this.radiusMax * 插值动画比例 ),插值动画比例的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,代码如下

//初始化画笔
private void inint() {
     this.animationRunning = false;
     this.paint = new Paint();
     this.paint.setAntiAlias(true);
     this.paint.setStyle(FILLANDSTROKE_STYLE);
     this.paint.setColor(new Color(Color.getIntColor("#0000FF")));//设置水波纹的颜色
 }

 @Override
 public void onDraw(Component component, Canvas canvas) {
     float touchX = (float) this.getWidth() / 2;
     float touchY = (float) this.getHeight() / 2;
      this.paint.setAlpha(1 - 1* this.ripplePose);//透明都也是从0到1  因为ripplePose 是从0-1变换
     this.radiusMax = component.getWidth()/2;
		float radiusMax2 = component.getWidth()/4;
     //根据比例设置半径大小
      canvas.drawCircle(touchX, touchY,  this.radiusMax * this.ripplePose +radiusMax2, this.paint);
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.

this.ripplePose的变化曲率为 0 - 1 根据比例计算出半径从小到大的大小,来绘制圆,这里我为什么加 radiusMax2是因为给他一个初始半径,让他不要从圆心开始,这样看起来就比较舒服了, 在点击的时候触发动画,代码如下

@Override
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
 if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_DOWN) { // 一个手指按下
     this.downX = this.getTouchX(touchEvent, 0);
     this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0));
 } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == POINT_MOVE) { // 一个手指移动


 } else if (touchEvent.getPointerCount() == 1 && touchEvent.getAction() == PRIMARY_POINT_UP) { //一个手指抬起
     this.invalidate();
 }
 return false;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

效果如下:

HarmonyOS  自定义UI之水波纹效果-鸿蒙开发者社区

但是当我把水波纹应用到 PageSlider 中带有ListContainer 的时候,滑动的时候也会触发水波纹,所以需要解决事件冲突。

事件冲突解决

在添加了长按阴影效果后,在滑动PageSlider 页面的时候listContainer的子item也会触发点击事件,导致各种事件冲突,

解决方法就是 在Touch事件中计算定义出各种事件,各个击破

1.点击事件:抬起时间-按下时间 没有超过200ms

2.长按事件:按下超过200ms

3.长按滑动事件:移动监听里面计算 滑动距离超过20,时间超过200 没有抬起

4.短按滑动事件: 时间少于200ms 距离超过 200px 没有抬起

5.然后处理各种事件,

另外发现,手机点击的时候移动事件会触发,而模拟器不会触发,这个也需要注意!

这样就完成了整个绘制过程,代码如下

/**
  * 触摸监听
  *
  * @param component view
  * @param touchEvent touchEvent
  * @return 是否消费
  */
public boolean onTouchEvent(Component component, TouchEvent touchEvent) {
 if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM1) {
     this.downX = this.getTouchX(touchEvent, 0);
     this.downY = this.getTouchY(touchEvent, 0);
     this.downTime = System.currentTimeMillis();
     this.isDownPose = true;
     this.isSlide = false;
     isYinYing = false;
 } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM3) {
     float upx = this.getTouchX(touchEvent, 0);
     long theTime = System.currentTimeMillis();
     int xjuli = (int) Math.abs(upx - downX);
     int timejuli = (int) Math.abs(theTime - this.downTime);
     if (isDownPose && timejuli > NUM200 && xjuli < NUM2 && !isSlide) {
         this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0));
         myevenHandler.sendEvent(1, NUM250);
         this.isSlide = true;
         return true;
     }
     return true;
 } else if (touchEvent.getPointerCount() == NUM1 && touchEvent.getAction() == NUM2) {
     float upx = this.getTouchX(touchEvent, 0);
     long upTimes = System.currentTimeMillis();
     isDownPose = false;
     isYinYing = false;
     int xjuli = (int) Math.abs(upx - downX);
     if (!this.isDownPose && upTimes - this.downTime < NUM200 && xjuli < NUM5) {
         this.isDownPose = false;
         this.createAnimation(this.getTouchX(touchEvent, 0), this.getTouchY(touchEvent, 0));
     } else if (!this.isDownPose && System.currentTimeMillis() - this.downTime > NUM250) {
         this.invalidate();
     } else {
         this.isDownPose = false;
     }
     this.isDownPose = false;
 }
 return true;
}
 
/**
  * 长按监听
  *
  * @param component view
  */
@Override
 public void onLongClicked(Component component) {
     this.createAnimation(this.downX, this.downY);
     myevenHandler.sendEvent(1, NUM250);
     this.isSlide = true;
 }
 
 
   /**
  * 绘制
  *
  * @param var1 Component
  * @param var2 Canvas
  */
  public void onDraw(Component var1, Canvas var2) {
     if (isYinYing) {
         this.paint.setAlpha(NUM03F);
         var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - 0 / NUM2F, this.paint);
     } else {
         if (this.getWidth() != 0 && this.getHeight() != 0) {
             this.radiusMax = (float) Math.sqrt(this.getWidth()
                     * this.getWidth() + this.getHeight() * this.getHeight());
             if (this.rippleType != NUM2) {
                 this.radiusMax /= NUM2F;
             }
             this.radiusMax -= (float) this.ripplePadding;
             if (this.isCentered || this.rippleType == 1) {
                 this.touchX = (float) this.getWidth() / NUM2F;
                 this.touchY = (float) this.getHeight() / NUM2F;
             }
             this.paint.setAlpha(this.rippleAlpha - this.rippleAlpha * this.ripplePose);
             float var3 = 0.0F;
             if (this.rippleType == NUM1 && this.ripplePose > NUM04) {
                 this.paint.setStyle(Paint.Style.STROKE_STYLE);
                 var3 = this.radiusMax * this.ripplePose - this.radiusMax * (this.ripplePose - NUM04) / NUM06;
                 this.paint.setStrokeWidth(this.radiusMax * this.ripplePose
                         - this.radiusMax * (this.ripplePose - NUM04) / NUM06);
             } else {
                 this.paint.setStyle(Paint.Style.FILL_STYLE);
             }
             var2.drawCircle(this.touchX, this.touchY, this.radiusMax * this.ripplePose - var3 / NUM2F, this.paint);
         }
     }
 }
  • 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.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.

效果如下

HarmonyOS  自定义UI之水波纹效果-鸿蒙开发者社区

完整项目代码

作者:王江涛

更多原创内容请关注:开鸿 HarmonyOS 学院

入门到精通、技巧到案例,系统化分享HarmonyOS开发技术,共建鸿蒙生态,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2021-9-10 15:27:54修改
8
收藏 7
回复
举报
8
1
7
1条回复
按时间正序
/
按时间倒序
mb609898e2cfb86
mb609898e2cfb86

老师这出文章的效率也太高了!

回复
2021-9-3 10:23:04


回复
    相关推荐