应用开发

HarmonyOS 自定义组件之上拉抽屉

时间:2010-12-5 17:23:32  作者:IT科技类资讯   来源:IT科技类资讯  查看:  评论:0
内容摘要:想了解更多内容,请访问:和华为官方合作共建的鸿蒙技术社区https://harmonyos.51cto.com简介HarmonyOS 开发自定义组件目前还不是很丰富,在开发过程中常常会有一些特殊效果的

想了解更多内容,自组件之上请访问:

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

https://harmonyos.51cto.com

简介

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

效果演示

实现思路

1.布局设计

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

图1:

2.手势判断

先得出Component在屏幕的自组件之上上下左右的坐标,然后手指的定义坐标是否在Component内。服务器托管

/**  * (x,拉抽y)是否在view的区域内  *  * @param component  * @param x  * @param y  * @return  */ private boolean isTouchPointInComponent(Component component, float x, float y) {      int[] locationOnScreen = component.getLocationOnScreen();     int left = locationOnScreen[0];     int top = locationOnScreen[1];     int right = left + component.getEstimatedWidth();     int bottom = top + component.getEstimatedHeight();     boolean inY = y >= top && y <= bottom;     boolean inX = x >= left && x <= right;     return inY && inX; } 

3.抽屉偏移

这里采用的是整个component对Touch事件的监听; 手指按下的判断是否在抽屉上,然后记录当前触摸y坐标; 移动是自组件之上算出偏移量offY; setTouchEventListener(new TouchEventListener() {      @Override     public boolean onTouchEvent(Component component, TouchEvent touchEvent) {          HiLog.info(logLabel, "onTouchEvent action:" + touchEvent.getAction());         switch (touchEvent.getAction()) {              case TouchEvent.PRIMARY_POINT_DOWN:                 marginBottom = directionalLayout.getMarginBottom();                 MmiPoint position = touchEvent.getPointerScreenPosition(0);                 if (isTouchPointInComponent(directionalLayout, position.getX(), position.getY())) {                      dragStartPointY = touchEvent.getPointerPosition(0).getY();                     return true;                 }                 break;             case TouchEvent.PRIMARY_POINT_UP:                 onTouchUp();                 break;             case TouchEvent.POINT_MOVE:                 float y = touchEvent.getPointerPosition(0).getY();                 float offY = dragStartPointY - y;                 setDrawerMarginBottom((int) offY);                 break;         }         return false;     } }); 

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

private void setDrawerMarginBottom(int offY) {      int bottom = marginBottom + offY;     if (bottom > 0) {          bottom = 0;         listContainer.setEnabled(true);     }     if (bottom < -H / 2) {          bottom = -H / 2;     }     HiLog.info(logLabel, "setDrawerMarginBottom bottom:" + bottom);     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f;     HiLog.info(logLabel, "setDrawerMarginBottom alpha:" + alpha);     bgComponent.setAlpha(alpha);     directionalLayout.setMarginBottom(bottom); } 

4.事件冲突解决

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

HarmonyOS中是没有事件分发这概念的,只有事件消费,定义ListContainer先拿到事件,拉抽然后是抽屉布局; 根据抽屉在完全展开的位置,在ListContainer收到触摸事件时,把ListContainer事件静止掉,不让其消费; 待抽屉完全展开时,解开ListContainer的事件; listContainer.setTouchEventListener(new TouchEventListener() {      @Override     public boolean onTouchEvent(Component component, TouchEvent touchEvent) {          marginBottom = directionalLayout.getMarginBottom();         boolean drag_down = listContainer.canScroll(DRAG_DOWN);         boolean drag_UP = listContainer.canScroll(DRAG_UP);         if (marginBottom == 0 && drag_down) {              component.setEnabled(true);             return true;         }         component.setEnabled(false);         return false;     } }); 

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

private void setDrawerMarginBottom(int offY) {      int bottom = marginBottom + offY;     if (bottom > 0) {          bottom = 0;         listContainer.setEnabled(true);     }     ....... } 

5.背景亮暗变化

首先我们XML布局参照上述布局设计—图1; 背景亮暗的改变根据抽屉位置按比例设置蒙层的透明度; float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; bgComponent.setAlpha(alpha); 

6.回弹效果

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

private void onTouchUp() {      HiLog.info(logLabel, "onTouchUp");     createAnimator(); }  private void createAnimator() {      marginBottom = directionalLayout.getMarginBottom();     HiLog.info(logLabel, "createAnimator marginBottom:" + marginBottom);     //创建数值动画对象     AnimatorValue animatorValue = new AnimatorValue();     //动画时长     animatorValue.setDuration(300);     //播放前的延迟时间     animatorValue.setDelay(0);     //循环次数     animatorValue.setLoopedCount(0);     //动画的播放类型     animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);     //设置动画过程     animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {          @Override         public void onUpdate(AnimatorValue animatorValue, float value) {              HiLog.info(logLabel, "createAnimator value:" + value);             if (marginBottom > -H / 4) {  // top                 HiLog.info(logLabel, "createAnimator top:" + value);                 setDrawerBottomOrToP((int) (marginBottom - value * marginBottom));             } else {  // bottom                 HiLog.info(logLabel, "createAnimator bottom:" + value);                 int top = H / 2 + marginBottom;                 setDrawerBottomOrToP((int) (marginBottom - value *top));             }         }     });     //开始启动动画     animatorValue.start(); }  private void setDrawerBottomOrToP(int bottom) {      if (bottom > 0) {          bottom = 0;         listContainer.setEnabled(true);     }     if (bottom < -H / 2) {          bottom = -H / 2;     }     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f;     bgComponent.setAlpha(alpha);     directionalLayout.setMarginBottom(bottom); } 

总结

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

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

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

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

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

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

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

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

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

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

https://harmonyos.51cto.com

copyright © 2025 powered by 益强资讯全景  滇ICP备2023006006号-31sitemap