自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

HarmonyOS 自定義組件之上拉抽屜

開發(fā) OpenHarmony
這里給大家提供了一個(gè)BottomSheet上拉抽屜的組件,同時(shí)通過這個(gè)組件示例講解一下HarmonyOS中的幾個(gè)自定義控件用到的知識(shí),分享一下自己自定義組件的思路。

[[441431]]

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

簡(jiǎn)介

HarmonyOS 開發(fā)自定義組件目前還不是很豐富,在開發(fā)過程中常常會(huì)有一些特殊效果的組件,這就需要我們額外花一些時(shí)間實(shí)現(xiàn),這里給大家提供了一個(gè)BottomSheet上拉抽屜的組件,同時(shí)通過這個(gè)組件示例講解一下HarmonyOS中的幾個(gè)自定義控件用到的知識(shí),分享一下自己自定義組件的思路。

效果演示

#星光計(jì)劃2.0# HarmonyOS 自定義組件之上拉抽屜-鴻蒙HarmonyOS技術(shù)社區(qū)

實(shí)現(xiàn)思路

1.布局設(shè)計(jì)

選擇的是相對(duì)布局,蒙層區(qū)來改變內(nèi)容區(qū)隨著抽屜的位置調(diào)節(jié)透明度。

圖1:

#星光計(jì)劃2.0# HarmonyOS 自定義組件之上拉抽屜-鴻蒙HarmonyOS技術(shù)社區(qū)
[[441433]]

 

2.手勢(shì)判斷

先得出Component在屏幕的上下左右的坐標(biāo),然后手指的坐標(biāo)是否在Component內(nèi)。

  1. /** 
  2.  * (x,y)是否在view的區(qū)域內(nèi) 
  3.  * 
  4.  * @param component 
  5.  * @param x 
  6.  * @param y 
  7.  * @return 
  8.  */ 
  9. private boolean isTouchPointInComponent(Component component, float x, float y) { 
  10.     int[] locationOnScreen = component.getLocationOnScreen(); 
  11.     int left = locationOnScreen[0]; 
  12.     int top = locationOnScreen[1]; 
  13.     int right = left + component.getEstimatedWidth(); 
  14.     int bottom = top + component.getEstimatedHeight(); 
  15.     boolean inY = y >= top && y <= bottom; 
  16.     boolean inX = x >= left && x <= right
  17.     return inY && inX; 

3.抽屜偏移

  • 這里采用的是整個(gè)component對(duì)Touch事件的監(jiān)聽;
  • 手指按下的判斷是否在抽屜上,然后記錄當(dāng)前觸摸y坐標(biāo);
  • 移動(dòng)是算出偏移量offY;
  1. setTouchEventListener(new TouchEventListener() { 
  2.     @Override 
  3.     public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  4.         HiLog.info(logLabel, "onTouchEvent action:" + touchEvent.getAction()); 
  5.         switch (touchEvent.getAction()) { 
  6.             case TouchEvent.PRIMARY_POINT_DOWN: 
  7.                 marginBottom = directionalLayout.getMarginBottom(); 
  8.                 MmiPoint position = touchEvent.getPointerScreenPosition(0); 
  9.                 if (isTouchPointInComponent(directionalLayout, position.getX(), position.getY())) { 
  10.                     dragStartPointY = touchEvent.getPointerPosition(0).getY(); 
  11.                     return true
  12.                 } 
  13.                 break; 
  14.             case TouchEvent.PRIMARY_POINT_UP: 
  15.                 onTouchUp(); 
  16.                 break; 
  17.             case TouchEvent.POINT_MOVE: 
  18.                 float y = touchEvent.getPointerPosition(0).getY(); 
  19.                 float offY = dragStartPointY - y; 
  20.                 setDrawerMarginBottom((int) offY); 
  21.                 break; 
  22.         } 
  23.         return false
  24.     } 
  25. }); 

根據(jù)偏移量改變抽屜的位置;

  1. private void setDrawerMarginBottom(int offY) { 
  2.     int bottom = marginBottom + offY; 
  3.     if (bottom > 0) { 
  4.         bottom = 0; 
  5.         listContainer.setEnabled(true); 
  6.     } 
  7.  
  8.     if (bottom < -H / 2) { 
  9.         bottom = -H / 2; 
  10.     } 
  11.     HiLog.info(logLabel, "setDrawerMarginBottom bottom:" + bottom); 
  12.  
  13.     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  14.     HiLog.info(logLabel, "setDrawerMarginBottom alpha:" + alpha); 
  15.     bgComponent.setAlpha(alpha); 
  16.     directionalLayout.setMarginBottom(bottom); 

4.事件沖突解決

首先發(fā)現(xiàn)不能按安卓的思想去處理:

  • HarmonyOS中是沒有事件分發(fā)這概念的,只有事件消費(fèi),ListContainer先拿到事件,然后是抽屜布局;
  • 根據(jù)抽屜在完全展開的位置,在ListContainer收到觸摸事件時(shí),把ListContainer事件靜止掉,不讓其消費(fèi);
  • 待抽屜完全展開時(shí),解開ListContainer的事件;
  1. listContainer.setTouchEventListener(new TouchEventListener() { 
  2.     @Override 
  3.     public boolean onTouchEvent(Component component, TouchEvent touchEvent) { 
  4.         marginBottom = directionalLayout.getMarginBottom(); 
  5.         boolean drag_down = listContainer.canScroll(DRAG_DOWN); 
  6.         boolean drag_UP = listContainer.canScroll(DRAG_UP); 
  7.         if (marginBottom == 0 && drag_down) { 
  8.             component.setEnabled(true); 
  9.             return true
  10.         } 
  11.         component.setEnabled(false); 
  12.         return false
  13.     } 
  14. }); 

這里是抽屜容器定位抽屜時(shí),判斷是否打開ListContainer事件。

  1. private void setDrawerMarginBottom(int offY) { 
  2.     int bottom = marginBottom + offY; 
  3.     if (bottom > 0) { 
  4.         bottom = 0; 
  5.         listContainer.setEnabled(true); 
  6.     } 
  7.     ....... 

5.背景亮暗變化

  • 首先我們XML布局參照上述布局設(shè)計(jì)—圖1;
  • 背景亮暗的改變根據(jù)抽屜位置按比例設(shè)置蒙層的透明度;
  1. float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  2. bgComponent.setAlpha(alpha); 

6.回彈效果

運(yùn)用到了數(shù)值動(dòng)畫,在手勢(shì)抬起時(shí),判斷上下臨界點(diǎn)決定動(dòng)畫的上下。

  1. private void onTouchUp() { 
  2.     HiLog.info(logLabel, "onTouchUp"); 
  3.     createAnimator(); 
  1. private void createAnimator() { 
  2.     marginBottom = directionalLayout.getMarginBottom(); 
  3.     HiLog.info(logLabel, "createAnimator marginBottom:" + marginBottom); 
  4.     //創(chuàng)建數(shù)值動(dòng)畫對(duì)象 
  5.     AnimatorValue animatorValue = new AnimatorValue(); 
  6.     //動(dòng)畫時(shí)長 
  7.     animatorValue.setDuration(300); 
  8.     //播放前的延遲時(shí)間 
  9.     animatorValue.setDelay(0); 
  10.     //循環(huán)次數(shù) 
  11.     animatorValue.setLoopedCount(0); 
  12.     //動(dòng)畫的播放類型 
  13.     animatorValue.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE); 
  14.     //設(shè)置動(dòng)畫過程 
  15.     animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() { 
  16.         @Override 
  17.         public void onUpdate(AnimatorValue animatorValue, float value) { 
  18.             HiLog.info(logLabel, "createAnimator value:" + value); 
  19.             if (marginBottom > -H / 4) { // top 
  20.                 HiLog.info(logLabel, "createAnimator top:" + value); 
  21.                 setDrawerBottomOrToP((int) (marginBottom - value * marginBottom)); 
  22.             } else { // bottom 
  23.                 HiLog.info(logLabel, "createAnimator bottom:" + value); 
  24.                 int top = H / 2 + marginBottom; 
  25.                 setDrawerBottomOrToP((int) (marginBottom - value *top)); 
  26.             } 
  27.         } 
  28.     }); 
  29.     //開始啟動(dòng)動(dòng)畫 
  30.     animatorValue.start(); 
  1. private void setDrawerBottomOrToP(int bottom) { 
  2.     if (bottom > 0) { 
  3.         bottom = 0; 
  4.         listContainer.setEnabled(true); 
  5.     } 
  6.  
  7.     if (bottom < -H / 2) { 
  8.         bottom = -H / 2; 
  9.     } 
  10.   
  11.     float alpha = (0.5f - Math.abs((float) bottom / (float) H)) * 0.5f; 
  12.  
  13.     bgComponent.setAlpha(alpha); 
  14.     directionalLayout.setMarginBottom(bottom); 

總結(jié)

自定義組件步驟及思考方向:

明確父容器和子view的關(guān)系;

如何繪制一般采用以下三個(gè)方向:

  1. 已有控件組合;
  2. 采用畫布繪制等;
  3. 繼承控件擴(kuò)展功能;

若涉及到觸摸事件,需要考慮如何處理事件分發(fā)與消費(fèi);

動(dòng)畫選擇,可根據(jù)需求選擇合適動(dòng)畫(本文采用屬性動(dòng)畫);

計(jì)算問題,復(fù)雜的需要豐富的數(shù)學(xué)知識(shí);

性能問題(過度計(jì)算,重復(fù)繪制,對(duì)象重復(fù)創(chuàng)建)。

想了解更多內(nèi)容,請(qǐng)?jiān)L問:

51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)

https://harmonyos.51cto.com

 

責(zé)任編輯:jianghua 來源: 鴻蒙社區(qū)
相關(guān)推薦

2022-04-24 15:17:56

鴻蒙操作系統(tǒng)

2023-02-20 15:20:43

啟動(dòng)頁組件鴻蒙

2022-10-25 15:12:24

自定義組件鴻蒙

2022-10-26 15:54:46

canvas組件鴻蒙

2022-02-21 15:16:30

HarmonyOS鴻蒙操作系統(tǒng)

2022-06-20 15:43:45

switch開關(guān)鴻蒙

2021-11-01 10:21:36

鴻蒙HarmonyOS應(yīng)用

2009-06-24 15:13:36

自定義JSF組件

2022-06-30 14:02:07

鴻蒙開發(fā)消息彈窗組件

2021-09-15 10:19:15

鴻蒙HarmonyOS應(yīng)用

2022-07-15 16:45:35

slider滑塊組件鴻蒙

2022-07-06 20:24:08

ArkUI計(jì)時(shí)組件

2022-02-16 15:25:31

JS代碼Canvas鴻蒙

2022-07-12 16:56:48

自定義組件鴻蒙

2022-06-23 07:23:34

自定義組件計(jì)時(shí)器

2021-01-11 11:36:23

鴻蒙HarmonyOSApp開發(fā)

2021-12-24 15:46:23

鴻蒙HarmonyOS應(yīng)用

2022-02-16 16:09:12

鴻蒙游戲操作系統(tǒng)

2021-11-22 10:00:33

鴻蒙HarmonyOS應(yīng)用

2022-05-20 14:34:20

list組件鴻蒙操作系統(tǒng)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)