HarmonyOS 開(kāi)發(fā)自定義組件目前還不是很豐富,在開(kāi)發(fā)過(guò)程中常常會(huì)有一些特殊效果的組件,這就需要我們額外花一些時(shí)間實(shí)現(xiàn)。
這里給大家提供了一個(gè) BottomSheet 上拉抽屜的組件,同時(shí)通過(guò)這個(gè)組件示例講解一下 HarmonyOS 中的幾個(gè)自定義控件用到的知識(shí),分享一下自己自定義組件的思路。
效果演示如下圖:
實(shí)現(xiàn)思路
①布局設(shè)計(jì)
選擇的是相對(duì)布局,蒙層區(qū)來(lái)改變內(nèi)容區(qū)隨著抽屜的位置調(diào)節(jié)透明度。如圖 1:
②手勢(shì)判斷
先得出 Component 在屏幕的上下左右的坐標(biāo),然后手指的坐標(biāo)是否在 Component 內(nèi)。
/** *(x,y)是否在view的區(qū)域內(nèi) * *@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; }
③抽屜偏移
步驟如下:
這里采用的是整個(gè) component 對(duì) Touch 事件的監(jiān)聽(tīng)。
手指按下的判斷是否在抽屜上,然后記錄當(dāng)前觸摸 y 坐標(biāo)。
移動(dòng)是算出偏移量 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
④事件沖突解決
首先發(fā)現(xiàn)不能按安卓的思想去處理:
HarmonyOS 中是沒(méi)有事件分發(fā)這概念的,只有事件消費(fèi),ListContainer 先拿到事件,然后是抽屜布局。
根據(jù)抽屜在完全展開(kāi)的位置,在 ListContainer 收到觸摸事件時(shí),把 ListContainer 事件靜止掉,不讓其消費(fèi)。
待抽屜完全展開(kāi)時(shí),解開(kāi) 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; } });
這里是抽屜容器定位抽屜時(shí),判斷是否打開(kāi) ListContainer 事件。
privatevoidsetDrawerMarginBottom(intoffY){ intbottom=marginBottom+offY; if(bottom>0){ bottom=0; listContainer.setEnabled(true); } ....... }
⑤背景亮暗變化
首先我們 XML 布局參照上述布局設(shè)計(jì)—如圖 1。背景亮暗的改變根據(jù)抽屜位置按比例設(shè)置蒙層的透明度。
floatalpha=(0.5f-Math.abs((float)bottom/(float)H))*0.5f; bgComponent.setAlpha(alpha);
⑥回彈效果
運(yùn)用到了數(shù)值動(dòng)畫(huà),在手勢(shì)抬起時(shí),判斷上下臨界點(diǎn)決定動(dòng)畫(huà)的上下。
privatevoidonTouchUp(){ HiLog.info(logLabel,"onTouchUp"); createAnimator(); }
privatevoidcreateAnimator(){ marginBottom=directionalLayout.getMarginBottom(); HiLog.info(logLabel,"createAnimatormarginBottom:"+marginBottom); //創(chuàng)建數(shù)值動(dòng)畫(huà)對(duì)象 AnimatorValueanimatorValue=newAnimatorValue(); //動(dòng)畫(huà)時(shí)長(zhǎng) animatorValue.setDuration(300); //播放前的延遲時(shí)間 animatorValue.setDelay(0); //循環(huán)次數(shù) animatorValue.setLoopedCount(0); //動(dòng)畫(huà)的播放類(lèi)型 animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE); //設(shè)置動(dòng)畫(huà)過(guò)程 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)); } } }); //開(kāi)始啟動(dòng)動(dòng)畫(huà) animatorValue.start(); }
privatevoidsetDrawerBottomOrToP(intbottom){ if(bottom>0){ bottom=0; listContainer.setEnabled(true); } if(bottom
總結(jié)
自定義組件步驟及思考方向:
明確父容器和子 view 的關(guān)系。
如何繪制一般采用以下三個(gè)方向:已有控件組合;采用畫(huà)布繪制等;繼承控件擴(kuò)展功能。
若涉及到觸摸事件,需要考慮如何處理事件分發(fā)與消費(fèi)。
動(dòng)畫(huà)選擇,可根據(jù)需求選擇合適動(dòng)畫(huà)(本文采用屬性動(dòng)畫(huà))。
計(jì)算問(wèn)題,復(fù)雜的需要豐富的數(shù)學(xué)知識(shí)。
性能問(wèn)題(過(guò)度計(jì)算,重復(fù)繪制,對(duì)象重復(fù)創(chuàng)建)。
代碼地址:
https://gitee.com/guangdong-wangduoyu/touch-event-demo
原文標(biāo)題:HarmonyOS“上拉抽屜”效果實(shí)現(xiàn)!
文章出處:【微信公眾號(hào):HarmonyOS技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
審核編輯:彭菁
-
安卓
+關(guān)注
關(guān)注
5文章
2140瀏覽量
58147 -
HarmonyOS
+關(guān)注
關(guān)注
79文章
2005瀏覽量
31748
原文標(biāo)題:HarmonyOS“上拉抽屜”效果實(shí)現(xiàn)!
文章出處:【微信號(hào):gh_834c4b3d87fe,微信公眾號(hào):OpenHarmony技術(shù)社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
LabVIEW中如何制作漂亮的自定義控件(按鈕)
LabVIEW自定義控件
LabVIEW自定義控件的好處是什么?
講解一下HarmonyOS中的幾個(gè)自定義組件用到的知識(shí)
HarmonyOS實(shí)現(xiàn)自定義控件的過(guò)程分享
如何在LabVIEW中實(shí)現(xiàn)自定義控件

鴻蒙系統(tǒng)如何設(shè)置自定義下拉刷新控件
C#自定義開(kāi)關(guān)按鈕控件(WINFORM)

評(píng)論