更多信息请访问:https://harmonyos.51cto.com,一个与华为共建的鸿蒙技术社区官方介绍HarmonyOS自定义组件的开发不是很丰富,在开发过程中,经常会出现一些具有特殊效果的组件,需要我们额外花一些时间去实现。这是一个在BottomSheet上拉抽屉的组件。定义控件用到的知识,分享自己对自定义组件的想法。效果演示实现思路1.布局设计选择相对布局,遮罩区域随抽屉位置改变内容区域和调整透明度。图1:2.手势判断先获取Component在屏幕上、下、左、右的坐标,再获取手指坐标是否在Component中。/***(x,y)在视图区域**@paramcomponent*@paramx*@paramy*@return*/privatebooleanisTouchPointInComponent(Componentcomponent,floatx,floaty){int[]locationOnScreen=component.getLocationOnScreen();inleft=locationOnScreen[0];inttop=locationOnScreen[1];intright=left+component.getEstimatedWidth();intbottom=top+component.getEstimatedHeight();booleaninY=y>=top&&y<=bottom;booleaninX=x>=left&&x<=right;returninY&&inX;}3.抽屉偏移这里整个组件用来监听Touch事件;手指被按下,判断是否在抽屉上,然后记录当前触摸的y坐标;移动是计算偏移量offY;setTouchEventListener(newTouchEventListener(){@OverridepublicbooleanonTouchEvent(Componentcomponent,TouchEventtouchEvent){HiLog.info(logLabel,"onTouchEventaction:"+touchEvent.getAction());switch(touchEvent.getAction()){caseTouchEvent.PRIMARY_POINT_DOWN:marginBottom);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,"setDrawerMarginBottombottom:"+bottom);floatalpha=(0.5f-Math.abs((float)bottom/(float)H))*0.5f;HiLog.info(logLabel,"setDrawerMarginBottomalpha:"+alpha);bgComponent.setAlpha(alpha);directionalLayout.setMarginBottom(底部);}4。事件冲突解决首先发现不能按照Android的思路来处理:HarmonyOS没有事件分发的概念,只有事件消费,ListContainer先获取事件,再抽屉布局;根据抽屉完全展开的位置,在ListContainer收到touch事件时,停止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&&true);returntrue;}component.setEnabled(false);returnfalse;}});这里是抽屉容器定位抽屉时,判断是否开启ListContainer事件privatevoidsetDrawerMarginBottom(intoffY){intbottom=marginBottom+offY;if(bottom>0){bottom=0;listContainer.setEnabled(true);}......}5.背景明暗变化首先我们的XML布局参考上面的布局设计——图1;背景明暗变化根据抽屉位置设置图层的透明度;floatalpha=(0.5f-Math.abs((float)bottom/(float)H))*0.5f;bgComponent.setAlpha(alpha);6.反弹效果应用于数值动画,当手势抬起时,判断上下临界点来确定动画的上下。privatevoidonTouchUp(){HiLog.info(logLabel,"onTouchUp");createAnimator();}privatevoidcreateAnimator(){marginBottom=directionalLayout.getMarginBottom();HiLog.info(logLabel,"createAnimatormarginBottom:"+marginBottom);//创建一个valueAnimationobjectAnimatorValueanimatorValue=newAnimatorValue();//动画持续时间animatorValue.setDuration(300);//播放前的延迟时间animatorValue.setDelay(0);//循环次数animatorValue.setLoopedCount(0);//动画播放类型animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);//设置动画过程-H/4){//topHiLog.info(logLabel,"createAnimatortop:"+value);setDrawerBottomOrToP((int)(marginBottom-value*marginBottom));}else{//bottomHiLog.info(logLabel,"createAnimatorbottom:"+value);inttop=H/2+marginBottom;setDrawer底部或顶部((int)(marginBottom-value*top));}}});//启动动画animatorValue.start();}privatevoidsetDrawerBottomOrToP(intbottom){if(bottom>0){bottom=0;listContainer.setEnabled(true);}if(bottom<-H/2){bottom=-H/2;}floatalpha=(0.5f-Math.abs((float)bottom/(float)H))*0.5f;bgComponent.setAlpha(alpha);directionalLayout.setMarginBottom(bottom);}自定义组件步骤及思考方向总结:理清父容器与子视图的关系;如何绘制一般采用以下三个方向:现有控件组合;使用画布绘图等;继承控件扩展函数;如果涉及到触摸事件,需要考虑如何处理事件的分发和消费;动画选择,可以根据自己的需要选择合适的动画(本文使用属性动画);计算题,复杂的需要丰富的数学知识;性能问题(overcomputing,Repeateddrawing,repeatedcreationofobjects)更多信息请访问:与华为官方共建的鸿蒙技术社区https://harmonyos.51cto.com
