Android自定義側滑菜單控件簡單實現

隨著Android的不斷成熟,許多絢麗的效果也在不斷的被大家開發出來,其中側滑的效果用到的項目很多,用的好的更是給吸引了很多用戶。國內像QQ和酷狗App的側滑就很給力,所以查了一些資料,并結合ViewDragHelper輔助類,做了一種比較簡單的側滑實現方式。

一、實現效果圖

  • 實現的效果基本跟酷狗App差不多,因為就是仿造酷狗的~~

二、實現原理

  • SlideLayout控件使用的是ViewDragHelper輔助類來實現的。ViewDragHelper是一個實現View的拖拽的神器,它把View的拖拽操作變得特別的簡單,不熟悉ViewDragHelper的同學請先上傳送門

  • 要實現拖拽,首先需要將SlideLayout和ViewDragHelper關聯起來,然后將SlideLayout的事件交給ViewDragHelper來處理,然后在ViewDragHelper提供的回調里就可以對View進行各種操作。不過拖拽的原理都是差不多的,通過水平或者豎直的移動ViewGroup,然后不斷的layout和invalidate進行重繪顯示。

  • 在滑動的過程中,除了要不斷的計算滑動的位置和重繪界面,還需要對子容器進行不同的動畫操作,這里使用的是ViewHelper類對View做平移縮放和漸變等動畫。

  • 另外還使用枚舉來記錄SlideLayout側滑的狀態,包括關閉,打開和正在滑動。并且提供PanelSlideListener監聽滑動的狀態。這樣就可以根據不同的狀態做不同的操作。比如手動打開側滑,關閉側滑等等。

三、邏輯分析

這個項目實現的邏輯其實并不難,只需要計算出ViewGroup滑動的位置,然后重繪就行,其次還需要計算控件縮放和拉伸的比例等等。當然對各種View的操作方法還是要比較熟悉,不然搞不明白有些邏輯要做這里做。

1. SlideLayout應該作為一個控件容器來包容兩個子容器,一個菜單容器,一個主容器,首先我們需要獲取SlideLayout容器的寬高和兩個子容器對象

  • 在View的onSizeChanged()方法里獲取SlideLayout的寬高,此時控件已經測量完成

    /**
     * 當控件的寬高發生變化時會回調這個方法,可以用來測量控件的寬高
     *
     * @param w
     * @param h
     * @param oldw
     * @param oldh
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mSlideHeight = getMeasuredHeight();
        mSlideWidth = getMeasuredWidth();
        /**
         * 初始化拖動的范圍
         * 默認為屏幕寬度的60%
         */
        mSlideRange = (int) (mSlideWidth * mRangePercent);
    }
    
  • 在View的onFinishInflate()方法里可以獲取容器對象,此時布局已經填充

    /**
     * 當View填充結束時會調用這個方法,可以獲取子View對象
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() < 2) {
            throw new IllegalStateException("SlideLayout控件的子View必須大于2個");
        }
        if (!((getChildAt(0) instanceof ViewGroup) && (getChildAt(1) instanceof ViewGroup))) {
            throw new IllegalArgumentException("SlideLayout控件的子View必須是ViewGroup");
        }
        mMenuContainer = (ViewGroup) getChildAt(0);
        mMainContainer = (ViewGroup) getChildAt(1);
    }
    

2. 獲取到了需要的屬性和對象之后,就可以將SlideLayout和ViewDragHelper進行綁定

  • 首先在控件的構造里創建ViewDragHelper對象,創建完之后會有一個回調,而我們對View的各種操作就是在回調的各種方法里進行的

    /** View的滑動的輔助類,在回調里監聽View的各種操作
     * @param forParent 要進行觸摸滑動的父控件
     * @param sensitivity 控件滑動的速度,敏感度,1.0f正常
     * @param cb  對View的事件發生改變的回調
     */
    mDragHelper = ViewDragHelper.create(this, 1.0f, mViewCallback);
    
  • 創建對象之后,如果此時就對View進行操作是沒有效果的,因為還需要把SlideLayout的處理事件傳遞給ViewDragHelper

    /**
      * 轉交攔截事件給輔助類
      *
      * @param ev
      * @return
      */
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
         final int action = MotionEventCompat.getActionMasked(ev);
         if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
             mDragHelper.cancel();
             return false;
         }
         return mDragHelper.shouldInterceptTouchEvent(ev);
     }
    
     /**
      * 轉交觸摸事件給輔助類
      *
      * @param event
      * @return
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         try {
             mDragHelper.processTouchEvent(event);
         } catch (Exception e) {
             e.printStackTrace();
         }
         return true;
     }
    
  • 最重要的地方就是ViewDragHelper的回調了,里面有很多方法,每一個都很重要,這里列舉一個對容器的滑動處理方法onViewPositionChanged()。其實邏輯也是比較簡單,就是判斷當前滑動的是哪一個容器,計算容器的左邊界值,然后對容器進行重繪

    /**
     * 當子View的位置發送改變時回調
     * @param changedView 改變的子View
     * @param left 距離左邊界距離
     * @param top 距離頂部距離
     * @param dx 水平滑動距離差
     * @param dy 豎直滑動距離差
     */
    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        /**
         * 將菜單面板的移動量給主面板
         */
        if (changedView == mMenuContainer) {
            mMenuContainer.layout(0, 0, mSlideWidth, mSlideHeight);
            int newLeft = mMainContainer.getLeft() + dx;
            newLeft = fixLeft(newLeft);
            mMainContainer.layout(newLeft, 0, newLeft + mSlideWidth, mSlideHeight);
        }
    
        // 處理移動事件
        performSlideEvent();
    }
    

五、使用教程

  • 布局文件中

    <com.pinger.slide.SlideLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/slideLayout"
        android:layout_width="match_parent"
        android:background="@mipmap/icon_bg"
        android:layout_height="match_parent">
    
        // 菜單容器
        <include layout="@layout/layout_menu"/>
    
        // 主容器
        <include layout="@layout/layout_main"/>
    </com.pinger.slide.SlideLayout>
    
  • 代碼中獲取對象,設置監聽,設置打開或者關閉側滑

六、總結

有了ViewDragHelper這個輔助類,對ViewGroup進行操作相對來說已經比較簡單了,只需要處理計算和繪制的工作,其他的都已經做好了。當然ViewDragHelper的作用遠不于此,想要了解更多的同學可以去研究一下這個類的源碼。這里也只是簡單的實現了側滑功能,要想做的更完美的同學請自行修改。


Demo地址

歡迎大家訪問我的簡書博客GitHub

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容