文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

HarmonyOS 自定义组件之上拉抽屉

2024-12-02 11:56

关注

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

简介

HarmonyOS 开发自定义组件目前还不是很丰富,在开发过程中常常会有一些特殊效果的组件,这就需要我们额外花一些时间实现,这里给大家提供了一个BottomSheet上拉抽屉的组件,同时通过这个组件示例讲解一下HarmonyOS中的几个自定义控件用到的知识,分享一下自己自定义组件的思路。

效果演示


实现思路

1.布局设计

选择的是相对布局,蒙层区来改变内容区随着抽屉的位置调节透明度。

图1:

[[441433]]

 

2.手势判断

先得出Component在屏幕的上下左右的坐标,然后手指的坐标是否在Component内。

  1.  
  2. private boolean isTouchPointInComponent(Component component, float x, float y) { 
  3.     int[] locationOnScreen = component.getLocationOnScreen(); 
  4.     int left = locationOnScreen[0]; 
  5.     int top = locationOnScreen[1]; 
  6.     int right = left + component.getEstimatedWidth(); 
  7.     int bottom = top + component.getEstimatedHeight(); 
  8.     boolean inY = y >= top && y <= bottom; 
  9.     boolean inX = x >= left && x <= right
  10.     return inY && inX; 

3.抽屉偏移

  1. setTouchEventListener(new TouchEventListener() { 
  2.     @Override 
  3.     public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  4.         HiLog.info(logLabel, "onTouchEvent action:" + touchEvent.getAction()); 
  5.         switch (touchEvent.getAction()) { 
  6.             case TouchEvent.PRIMARY_POINT_DOWN: 
  7.                 marginBottom = directionalLayout.getMarginBottom(); 
  8.                 MmiPoint position = touchEvent.getPointerScreenPosition(0); 
  9.                 if (isTouchPointInComponent(directionalLayout, position.getX(), position.getY())) { 
  10.                     dragStartPointY = touchEvent.getPointerPosition(0).getY(); 
  11.                     return true
  12.                 } 
  13.                 break; 
  14.             case TouchEvent.PRIMARY_POINT_UP: 
  15.                 onTouchUp(); 
  16.                 break; 
  17.             case TouchEvent.POINT_MOVE: 
  18.                 float y = touchEvent.getPointerPosition(0).getY(); 
  19.                 float offY = dragStartPointY - y; 
  20.                 setDrawerMarginBottom((int) offY); 
  21.                 break; 
  22.         } 
  23.         return false
  24.     } 
  25. }); 

根据偏移量改变抽屉的位置;

  1. private void setDrawerMarginBottom(int offY) { 
  2.     int bottom = marginBottom + offY; 
  3.     if (bottom > 0) { 
  4.         bottom = 0; 
  5.         listContainer.setEnabled(true); 
  6.     } 
  7.  
  8.     if (bottom < -H / 2) { 
  9.         bottom = -H / 2; 
  10.     } 
  11.     HiLog.info(logLabel, "setDrawerMarginBottom bottom:" + bottom); 
  12.  
  13.     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  14.     HiLog.info(logLabel, "setDrawerMarginBottom alpha:" + alpha); 
  15.     bgComponent.setAlpha(alpha); 
  16.     directionalLayout.setMarginBottom(bottom); 

4.事件冲突解决

首先发现不能按安卓的思想去处理:

  1. listContainer.setTouchEventListener(new TouchEventListener() { 
  2.     @Override 
  3.     public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  4.         marginBottom = directionalLayout.getMarginBottom(); 
  5.         boolean drag_down = listContainer.canScroll(DRAG_DOWN); 
  6.         boolean drag_UP = listContainer.canScroll(DRAG_UP); 
  7.         if (marginBottom == 0 && drag_down) { 
  8.             component.setEnabled(true); 
  9.             return true
  10.         } 
  11.         component.setEnabled(false); 
  12.         return false
  13.     } 
  14. }); 

这里是抽屉容器定位抽屉时,判断是否打开ListContainer事件。

  1. private void setDrawerMarginBottom(int offY) { 
  2.     int bottom = marginBottom + offY; 
  3.     if (bottom > 0) { 
  4.         bottom = 0; 
  5.         listContainer.setEnabled(true); 
  6.     } 
  7.     ....... 

5.背景亮暗变化

  1. float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  2. bgComponent.setAlpha(alpha); 

6.回弹效果

运用到了数值动画,在手势抬起时,判断上下临界点决定动画的上下。

  1. private void onTouchUp() { 
  2.     HiLog.info(logLabel, "onTouchUp"); 
  3.     createAnimator(); 
  1. private void createAnimator() { 
  2.     marginBottom = directionalLayout.getMarginBottom(); 
  3.     HiLog.info(logLabel, "createAnimator marginBottom:" + marginBottom); 
  4.     //创建数值动画对象 
  5.     AnimatorValue animatorValue = new AnimatorValue(); 
  6.     //动画时长 
  7.     animatorValue.setDuration(300); 
  8.     //播放前的延迟时间 
  9.     animatorValue.setDelay(0); 
  10.     //循环次数 
  11.     animatorValue.setLoopedCount(0); 
  12.     //动画的播放类型 
  13.     animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE); 
  14.     //设置动画过程 
  15.     animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() { 
  16.         @Override 
  17.         public void onUpdate(AnimatorValue animatorValue, float value) { 
  18.             HiLog.info(logLabel, "createAnimator value:" + value); 
  19.             if (marginBottom > -H / 4) { // top 
  20.                 HiLog.info(logLabel, "createAnimator top:" + value); 
  21.                 setDrawerBottomOrToP((int) (marginBottom - value * marginBottom)); 
  22.             } else { // bottom 
  23.                 HiLog.info(logLabel, "createAnimator bottom:" + value); 
  24.                 int top = H / 2 + marginBottom; 
  25.                 setDrawerBottomOrToP((int) (marginBottom - value *top)); 
  26.             } 
  27.         } 
  28.     }); 
  29.     //开始启动动画 
  30.     animatorValue.start(); 
  1. private void setDrawerBottomOrToP(int bottom) { 
  2.     if (bottom > 0) { 
  3.         bottom = 0; 
  4.         listContainer.setEnabled(true); 
  5.     } 
  6.  
  7.     if (bottom < -H / 2) { 
  8.         bottom = -H / 2; 
  9.     } 
  10.   
  11.     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  12.  
  13.     bgComponent.setAlpha(alpha); 
  14.     directionalLayout.setMarginBottom(bottom); 

总结

自定义组件步骤及思考方向:

明确父容器和子view的关系;

如何绘制一般采用以下三个方向:

  1. 已有控件组合;
  2. 采用画布绘制等;
  3. 继承控件扩展功能;

若涉及到触摸事件,需要考虑如何处理事件分发与消费;

动画选择,可根据需求选择合适动画(本文采用属性动画);

计算问题,复杂的需要丰富的数学知识;

性能问题(过度计算,重复绘制,对象重复创建)。

想了解更多内容,请访问:

51CTO和华为官方合作共建的鸿蒙技术社区

https://harmonyos.51cto.com

 

来源:鸿蒙社区内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯