HarmonyOS 開發(fā)自定義組件目前還不是很豐富,在開發(fā)過程中常常會有一些特殊效果的組件,這就需要我們額外花一些時間實現(xiàn)。
這里給大家提供了一個 BottomSheet 上拉抽屜的組件,同時通過這個組件示例講解一下 HarmonyOS 中的幾個自定義控件用到的知識,分享一下自己自定義組件的思路。
效果演示如下圖:
實現(xiàn)思路
①布局設計
選擇的是相對布局,蒙層區(qū)來改變內容區(qū)隨著抽屜的位置調節(jié)透明度。如圖 1:
②手勢判斷
先得出 Component 在屏幕的上下左右的坐標,然后手指的坐標是否在 Component 內。
/** *(x,y)是否在view的區(qū)域內 * *@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 事件的監(jiān)聽。
手指按下的判斷是否在抽屜上,然后記錄當前觸摸 y 坐標。
移動是算出偏移量 offY。
setTouchEventListener(newTouchEventListener(){ @Override publicbooleanonTouchEvent(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; } });
根據(jù)偏移量改變抽屜的位置:
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); }
④事件沖突解決
首先發(fā)現(xiàn)不能按安卓的思想去處理:
HarmonyOS 中是沒有事件分發(fā)這概念的,只有事件消費,ListContainer 先拿到事件,然后是抽屜布局。
根據(jù)抽屜在完全展開的位置,在 ListContainer 收到觸摸事件時,把 ListContainer 事件靜止掉,不讓其消費。
待抽屜完全展開時,解開 ListContainer 的事件。
listContainer.setTouchEventListener(newTouchEventListener(){ @Override publicbooleanonTouchEvent(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。背景亮暗的改變根據(jù)抽屜位置按比例設置蒙層的透明度。
floatalpha=(0.5f-Math.abs((float)bottom/(float)H))*0.5f; bgComponent.setAlpha(alpha);
⑥回彈效果
運用到了數(shù)值動畫,在手勢抬起時,判斷上下臨界點決定動畫的上下。
privatevoidonTouchUp(){ HiLog.info(logLabel,"onTouchUp"); createAnimator(); }
privatevoidcreateAnimator(){ marginBottom=directionalLayout.getMarginBottom(); HiLog.info(logLabel,"createAnimatormarginBottom:"+marginBottom); //創(chuàng)建數(shù)值動畫對象 AnimatorValueanimatorValue=newAnimatorValue(); //動畫時長 animatorValue.setDuration(300); //播放前的延遲時間 animatorValue.setDelay(0); //循環(huán)次數(shù) animatorValue.setLoopedCount(0); //動畫的播放類型 animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE); //設置動畫過程 animatorValue.setValueUpdateListener(newAnimatorValue.ValueUpdateListener(){ @Override publicvoidonUpdate(AnimatorValueanimatorValue,floatvalue){ HiLog.info(logLabel,"createAnimatorvalue:"+value); if(marginBottom>-H/4){//top HiLog.info(logLabel,"createAnimatortop:"+value); setDrawerBottomOrToP((int)(marginBottom-value*marginBottom)); }else{//bottom HiLog.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 的關系。
如何繪制一般采用以下三個方向:已有控件組合;采用畫布繪制等;繼承控件擴展功能。
若涉及到觸摸事件,需要考慮如何處理事件分發(fā)與消費。
動畫選擇,可根據(jù)需求選擇合適動畫(本文采用屬性動畫)。
計算問題,復雜的需要豐富的數(shù)學知識。
性能問題(過度計算,重復繪制,對象重復創(chuàng)建)。
代碼地址:
https://gitee.com/guangdong-wangduoyu/touch-event-demo
原文標題:HarmonyOS“上拉抽屜”效果實現(xiàn)!
文章出處:【微信公眾號:HarmonyOS技術社區(qū)】歡迎添加關注!文章轉載請注明出處。
-
安卓
+關注
關注
5文章
2126瀏覽量
57144 -
HarmonyOS
+關注
關注
79文章
1973瀏覽量
30139
原文標題:HarmonyOS“上拉抽屜”效果實現(xiàn)!
文章出處:【微信號:gh_834c4b3d87fe,微信公眾號:OpenHarmony技術社區(qū)】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論