• 1
  • 2
  • 3
  • 4

首页 / 行业

HarmonyOS 中的几个自定义控件介绍

2022-01-04 13:49:00

HarmonyOS 中的几个自定义控件介绍

HarmonyOS 开发自定义组件目前还不是很丰富,在开发过程中常常会有一些特殊效果的组件,这就需要我们额外花一些时间实现。

这里给大家提供了一个 BottomSheet 上拉抽屉的组件,同时通过这个组件示例讲解一下 HarmonyOS 中的几个自定义控件用到的知识,分享一下自己自定义组件的思路。

效果演示如下图:

实现思路

①布局设计

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

②手势判断

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

/***(x,y)是否在view的区域内**@paramcomponent*@paramx*@paramy*@return*/privatebooleanisTouchPointInComponent(Componentcomponent,floatx,floaty){int[]locationOnScreen=component.getLocationOnScreen();intleft=locationOnScreen[0];inttop=locationOnScreen[1];intright=left+component.getEstimatedWidth();intbottom=top+component.getEstimatedHeight();booleaninY=y>=top&&y<= bottom;    boolean inX = x >=left&&x<= right;    return inY && inX;}

③抽屉偏移

步骤如下:

这里采用的是整个 component 对 Touch 事件的监听。

手指按下的判断是否在抽屉上,然后记录当前触摸 y 坐标。

移动是算出偏移量 offY。

setTouchEventListener(newTouchEventListener(){@OverridepublicbooleanonTouchEvent(Componentcomponent,TouchEventtouchEvent){HiLog.info(logLabel,"onTouchEventaction:"+touchEvent.getAction());switch(touchEvent.getAction()){caseTouchEvent.PRIMARY_POINT_DOWN:marginBottom=directionalLayout.getMarginBottom();MmiPointposition=touchEvent.getPointerScreenPosition(0);if(isTouchPointInComponent(directionalLayout,position.getX(),position.getY())){dragStartPointY=touchEvent.getPointerPosition(0).getY();returntrue;}break;caseTouchEvent.PRIMARY_POINT_UP:onTouchUp();break;caseTouchEvent.POINT_MOVE:floaty=touchEvent.getPointerPosition(0).getY();floatoffY=dragStartPointY-y;setDrawerMarginBottom((int)offY);break;}returnfalse;}});

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

privatevoidsetDrawerMarginBottom(intoffY){intbottom=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);}

④事件冲突解决

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

HarmonyOS 中是没有事件分发这概念的,只有事件消费,ListContainer 先拿到事件,然后是抽屉布局。

根据抽屉在完全展开的位置,在 ListContainer 收到触摸事件时,把 ListContainer 事件静止掉,不让其消费。

待抽屉完全展开时,解开 ListContainer 的事件。

listContainer.setTouchEventListener(newTouchEventListener(){@OverridepublicbooleanonTouchEvent(Componentcomponent,TouchEventtouchEvent){marginBottom=directionalLayout.getMarginBottom();booleandrag_down=listContainer.canScroll(DRAG_DOWN);booleandrag_UP=listContainer.canScroll(DRAG_UP);if(marginBottom==0&&drag_down){component.setEnabled(true);returntrue;}component.setEnabled(false);returnfalse;}});

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

privatevoidsetDrawerMarginBottom(intoffY){intbottom=marginBottom+offY;if(bottom>0){bottom=0;listContainer.setEnabled(true);}.......}

⑤背景亮暗变化

首先我们 XML 布局参照上述布局设计—如图 1。背景亮暗的改变根据抽屉位置按比例设置蒙层的透明度。

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

⑥回弹效果

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

privatevoidonTouchUp(){HiLog.info(logLabel,"onTouchUp");createAnimator();}
privatevoidcreateAnimator(){marginBottom=directionalLayout.getMarginBottom();HiLog.info(logLabel,"createAnimatormarginBottom:"+marginBottom);//创建数值动画对象AnimatorValueanimatorValue=newAnimatorValue();//动画时长animatorValue.setDuration(300);//播放前的延迟时间animatorValue.setDelay(0);//循环次数animatorValue.setLoopedCount(0);//动画的播放类型animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);//设置动画过程animatorValue.setValueUpdateListener(newAnimatorValue.ValueUpdateListener(){@OverridepublicvoidonUpdate(AnimatorValueanimatorValue,floatvalue){HiLog.info(logLabel,"createAnimatorvalue:"+value);if(marginBottom>-H/4){//topHiLog.info(logLabel,"createAnimatortop:"+value);setDrawerBottomOrToP((int)(marginBottom-value*marginBottom));}else{//bottomHiLog.info(logLabel,"createAnimatorbottom:"+value);inttop=H/2+marginBottom;setDrawerBottomOrToP((int)(marginBottom-value*top));}}});//开始启动动画animatorValue.start();}
privatevoidsetDrawerBottomOrToP(intbottom){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://gitee.com/guangdong-wangduoyu/touch-event-demo

原文标题:HarmonyOS“上拉抽屉”效果实现!

文章出处:【微信公众号:HarmonyOS技术社区】欢迎添加关注!文章转载请注明出处。

审核编辑:彭菁

自定义控件自定义组件组件几个

  • 1
  • 2
  • 3
  • 4

最新内容

手机

相关内容

  • 1
  • 2
  • 3

猜你喜欢