鴻蒙開源第三方組件—SlidingMenu_ohos側滑菜單組件
前言
基于安卓平臺的SlidingMenu側滑菜單組件(https://github.com/jfeinstein10/SlidingMenu),實現(xiàn)了鴻蒙化遷移和重構,代碼已經(jīng)開源到(https://gitee.com/isrc_ohos/sliding-menu_ohos),歡迎各位下載使用并提出寶貴意見!
背景
SlidingMenu_ohos提供了一個側滑菜單的導航框架,使菜單可以隱藏在手機屏幕的左側、右側或左右兩側。當用戶使用時,通過左滑或者右滑的方式調(diào)出,既節(jié)省了主屏幕的空間,也方便用戶操作,在很多主流APP中都有廣泛的應用。
效果展示
由于菜單從左右兩側調(diào)出的顯示效果相似,此處僅以菜單從左側調(diào)出為例進行效果展示。
組件未啟用時,應用顯示主頁面。單指觸摸屏幕左側并逐漸向右滑動,菜單頁面逐漸顯示,主頁面逐漸隱藏。向右滑動的距離超過某個閾值時,菜單頁面全部顯示,效果如圖1所示。
圖1 菜單展示和隱藏效果圖
Sample解析
Sample部分的內(nèi)容較為簡單,主要包含兩個部分。一是創(chuàng)建SlidingMenu_ohos組件的對象,可根據(jù)用戶的實際需求,調(diào)用Library的接口,對組件的具體屬性進行設置。二是將設置好的組件添加到Ability中。下面將詳細介紹組件的使用方法。
1、導入SlidingMenu類
- import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu;
2、設置Ability的布局
此布局用作為主頁面的布局,在組件隱藏的時候顯示。
- DirectionalLayout directionalLayout =
- (DirectionalLayout)LayoutScatter.getInstance(this).parse(ResourceTable.Layout_activity_main,null,false);setUIContent(directionalLayout);
3、實例化組件的對象
- SlidingMenu slidingMenu = null;
- try {
- //初始化SlidingMenu實例
- slidingMenu = new SlidingMenu(this);
- } catch (IOException e) {
- e.printStackTrace();
- } catch (NotExistException e) {
- e.printStackTrace();
- }
- 1.
4、設置組件屬性
此步驟可以根據(jù)具體需求,設置組件的位置、觸發(fā)范圍、布局、最大寬度等屬性。
- 4、設置組件屬性
- 此步驟可以根據(jù)具體需求,設置組件的位置、觸發(fā)范圍、布局、最大寬度等屬性。
5、關聯(lián)Ability
attachToAbility()方法是Library提供的重要方法,用于將菜單組件關聯(lián)到Ability。其參數(shù)SLIDING_WINDOW和SLIDING_CONTENT是菜單的不同模式,SLIDING_WINDOW模式下的菜單包含Title / ActionBar部分,菜單需在整個手機頁面上顯示,如圖2所示;SLIDING_CONTENT模式下的菜單不包括包含Title / ActionBar部分,菜單可以在手機頁面的局部范圍內(nèi)顯示,如圖3所示。
- try {
- //關聯(lián)Ability,獲取頁面展示根節(jié)點
- slidingMenu.attachToAbility(directionalLayout,this, SlidingMenu.SLIDING_WINDOW);
- } catch (NotExistException e) {
- e.printStackTrace();
- } catch (WrongTypeException e) {
- e.printStackTrace();
- } catch (IOException e) {
- e.printStackTrace();
- }
- 1
圖2 SLIDING_WINDOW展示效果圖
圖3 SLIDING_CONTENT展示效果圖
Library解析
Library的工程結構如下圖所示,CustomViewAbove表示主頁面,CustomViewBehind表示菜單頁面,SlidingMenu主要用于控制主頁面位于菜單頁面的上方,還可以設置菜單的寬度、觸發(fā)范圍、顯示模式等屬性。為了方便解釋,以下均以手指從左側觸摸屏幕并向右滑動為例進行講解,菜單均采用SLIDING_WINDOW的顯示模式。
圖4 Library的工程結構
1、CustomViewAbove主頁面
CustomViewAbove需要監(jiān)聽觸摸、移動、抬起和取消等Touch事件,并記錄手指滑動的距離和速度。
(1)對Touch事件的處理
Touch事件決定了菜單的顯示、移動和隱藏。例如:在菜單的觸發(fā)范圍內(nèi),手指向右滑動(POINT_MOVE)時,菜單會跟隨滑動到手指所在位置。手指抬起(PRIMARY_POINT_UP)或者取消滑動(CANCEL)時,會依據(jù)手指滑動的距離和速度決定菜單頁面的下一狀態(tài)是全部隱藏還是全部顯示。
- switch (action) {
- //按下
- case TouchEvent.PRIMARY_POINT_DOWN:
- .....
- mInitialMotionX=mLastMotionX=ev.getPointerPosition(mActivePointerId).getX();
- break;
- //滑動
- case TouchEvent.POINT_MOVE:
- ......
- //菜單滑動到此時手指所在位置(x)
- left_scrollto(x);
- break;
- //抬起
- case TouchEvent.PRIMARY_POINT_UP:
- ......
- //獲得菜單的下一狀態(tài)(全屏顯示或者全部隱藏)
- int nextPage = determineTargetPage(pageOffset, initialVelocity,totalDelta);
- //設置菜單的下一狀態(tài)
- setCurrentItemInternal(nextPage,initialVelocity);
- ......
- endDrag();
- break;
- //取消
- case TouchEvent.CANCEL:
- ......
- //根據(jù)菜單當前狀態(tài)mCurItem設置菜單下一狀態(tài)
- setCurrentItemInternal(mCurItem);
- //結束拖動
- endDrag();
- break;
- }
(2)對滑動的距離和速度的處理
手指抬起時,滑動的速度和距離分別大于最小滑動速度和最小移動距離,判定此時的操作為快速拖動,菜單立即彈出并全部顯示,如圖5所示。
- private int determineTargetPage(float pageOffset, int velocity, int deltaX) {
- //獲得當前菜單狀態(tài),0:左側菜單正在展示,1:菜單隱藏,2:右側菜單正在展示
- int targetPage = getCurrentItem();
- //針對快速拖動的判斷
- if (Math.abs(deltaX) > mFlingDistance && Math.abs(velocity) > mMinimumVelocity) {
- if (velocity > 0 && deltaX > 0) {
- targetPage -= 1;
- } else if (velocity < 0 && deltaX < 0){
- targetPage += 1;
- }
- }
- }
圖5 快速拖動效果圖
當手指抬起并且不滿足快速拖動標準時,需要根據(jù)滑動距離判斷菜單的隱藏或顯示。若菜單已展開的部分超過自身寬度的1/2,菜單立即彈出全部顯示,,效果圖如圖1所示;若不足自身寬度的1/2,則立即彈回全部隱藏,效果圖如圖6所示。
- //獲得當前菜單狀態(tài),0:左側菜單正在展示,1:菜單隱藏,2:右側菜單正在展示
- switch (mCurItem){
- case 0:
- targetPage=1-Math.round(pageOffset);
- break;
- case 1:
- //菜單隱藏時,首先要判斷此時菜單的放置狀態(tài)是左側還是右側
- if(current_state == SlidingMenu.LEFT){
- targetPage = Math.round(1-pageOffset);
- }
- if(current_state == SlidingMenu.RIGHT){
- targetPage = Math.round(1+pageOffset);
- }
- break;
- case 2:
- targetPage = Math.round(1+pageOffset);
- break;
- }
圖6 緩慢拖動效果圖
(3)菜單顯示和隱藏的實現(xiàn)
主頁面的左側邊線與手指的位置綁定,當手指向右滑動時,主頁面也會隨手指向右滑動,在這個過程中菜單頁面漸漸展示出來,實現(xiàn)菜單頁面隨手指滑動慢慢展開的視覺效果。
- void setCurrentItemInternal(int item,int velocity) {
- //獲得菜單的目標狀態(tài)
- item = mViewBehind.getMenuPage(item);
- mCurItem = item;
- final int destX = getDestScrollX(mCurItem);
- /*菜單放置狀態(tài)為左側,通過設置主頁面的位置實現(xiàn)菜單的彈出展示或彈回隱藏
- 1.destX=0,主頁面左側邊線與屏幕左側邊線對齊,菜單被全部遮擋,實現(xiàn)菜單彈回隱藏
- 2.destX=MenuWidth,主頁面左側邊線向右移動與菜單總寬度相等的距離,實現(xiàn)菜單彈出展示*/
- if (mViewBehind.getMode() == SlidingMenu.LEFT) {
- mContent.setLeft(destX);
- mViewBehind.scrollBehindTo(destX);
- }
- ......
- }
- // 菜單放置在左側時的菜單滑動操作
- public void left_scrollto(float x) {
- //當menu的展示寬度大于最大寬度時僅展示最大寬度
- if(x>getMenuWidth()){
- x=getMenuWidth();
- }
- //主頁面(主頁面左側邊線)和菜單(菜單右側邊線)分別移動到指定位置X
- mContent.setLeft((int)x);
- mViewBehind.scrollBehindTo((int)x);
- }
2、CustomViewBehind 菜單頁面
CustomViewBehind為菜單頁面,邏輯相比于主頁面簡單許多。主要負責根據(jù)主頁面中的Touch事件改變自身狀態(tài)值,同時向外暴露接口,用于設置或者獲取菜單頁面的最大寬度、自身狀態(tài)等屬性。
- // 設置菜單最大寬度
- public void setMenuWidth(int menuWidth) {
- this.menuWidth = menuWidth;
- }
- // 獲得菜單最大寬度
- public int getMenuWidth() {
- return menuWidth;
- }
- 1.
3. SlidingMenu
分別實例化CustomViewAbove和CustomViewBehind的對象,并按照主頁面在上菜單頁面在下的順序分別添加到SlidingMenu的容器中。
- //添加菜單子控件
- addComponent(mViewBehind, behindParams);
- //添加主頁面子控件
- addComponent(mViewAbove, aboveParams);
項目貢獻人
徐澤鑫 鄭森文 朱偉 陳美汝 王佳思 張馨心