SlideMenu: 仿QQ側滑菜單

SlideMenu

春節的時候在家里完成一個二手交易項目的時候,需求要求做一個側滑菜單,菜單的形式就跟QQ差不多,效果上跟QQ略有一點差別。QQ在菜單滑動的時候,菜單部分的View兩邊都有一個隱藏的效果;我們這個App僅僅需要實現最簡單的側滑菜單效果即可,實現復雜程度上,較QQ小了許多。

以前都是項目中有側滑菜單,我都是拿現成的開源項目SlidingMenu來實現的。放假在家,時間比較充裕,就干脆自己實現了一波。

先上演示圖(是用360手機助手實時演示的,所以效果上有些卡頓。在真機上運行很流暢。)

再看看QQ的側滑菜單演示圖

QQ菜單在滑動的時候菜單View兩端同時隱藏或滑出

實現思路

  1. 對于菜單的橫向滑動,我們可以借助Android提供的HorizontalScrollView幫助我們實現。我們先通過繼承HorizontalScrollView幫助我們創建一個可以橫向滾動的視圖。

  2. 對于自定義控件,特別是自定義靈活的組合控件,一般要實現一個構造器

        public class SlideMenu extends HorizontalScrollView {
             public SlideMenu(Context context, AttributeSet attrs){
                super(context, attrs);
             }
        }
         第二個參數是一個屬性集合,通過它我們能獲得一些自定義的屬性
  3. 在values文件夾下建立一個attrs.xml文件,編寫我們需要的自定義屬性

    在實際項目中,我們一般需要設置菜單View的寬度,菜單以及內容View是使用哪一個布局,所以一般建立以下這三個屬性就可以了。有其他需求的也可以自己擴展。

        <?xml version="1.0" encoding="utf-8"?>
        <resources>
            <declare-styleable name="SlideMenu">
                <attr name="menu_layout" format="reference"></attr>
                <attr name="content_layout" format="reference"></attr>
                <attr name="menu_width" format="reference|dimension"></attr>
            </declare-styleable>
        </resources>
  4. 在SlideMenu類中,我們可以創建一個init()方法,來幫我們完成解析自定義屬性的值,并做一些其他初始化參數的操作。

        private void init(AttributeSet attrs) {
            //解析自定義參數的值
            TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.SlideMenu);
            int menuLayoutId = typedArray.getResourceId(0, -1);
            int contentLayoutId = typedArray.getResourceId(1, -1);
            mMenuWidth = typedArray.getDimensionPixelOffset(2, 0);
    
            //加載View
            mMenu = View.inflate(getContext(), menuLayoutId, null);
            mContent = View.inflate(getContext(), contentLayoutId, null);
    
            //獲取屏幕寬高
            DisplayMetrics displayMetrics = getContext().getResources().getDisplayMetrics();
            mScreenWidth = displayMetrics.widthPixels;
            mScreenHeight = displayMetrics.heightPixels;
    
            //設置為不能滑出邊界(滑出邊界的效果比較難看)
            setOverScrollMode(OVER_SCROLL_NEVER);
    
            //設置無滾動條
            setHorizontalScrollBarEnabled(false);
        }
  5. 自定義ViewGroup通常要實現兩個方法onMeasure 和 onLayout方法 onMeasure:計算所有ChildView的寬度和高度 然后根據ChildView的計算結果,設置自己的寬和高 onLayout :對子View進行布局,會依次調用子ViewGroup的layout方法

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            //設置測量的寬高為屏幕的寬高
            setMeasuredDimension(
                    mScreenWidth, mScreenHeight);
            if(!mHasInit){
                //由于會多次測量,所以使用mHasInit保證布局只加載一次
                mHasInit = true;
                //創建子View,并加到ScrollView中
                mWrapper = new LinearLayout(getContext());
                LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(mMenuWidth+mScreenWidth,getMeasuredHeight());
                mWrapper.setOrientation(LinearLayout.HORIZONTAL);
                LinearLayout.LayoutParams menuParams = new LinearLayout.LayoutParams(mMenuWidth, mScreenHeight);
                mWrapper.addView(mMenu, menuParams);
                LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(mScreenWidth, mScreenHeight);
                mWrapper.addView(mContent, contentParams);
                mWrapper.setLayoutParams(layoutParams);
                addView(mWrapper);
            }
        }
    
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            //change表示view有新的布局和尺寸。在子view被初始化并加載到父view的時候就會調用onLayout方法,并且change為true
            if(changed){
                //將ScrollView滾動到mMenuWidth的位置,也就是滾動到跟內容View的左側對其,讓菜單欄剛好完全隱藏在屏幕左側
                //注意滾動的時機,當view有新的布局和尺寸時調用,也就是在初始化的時候去滾動。這個時候滾動才不會在打開頁面時有滾動的效果
                scrollTo(mMenuWidth, 0);
            }
        }
  6. 這樣就基本實現了能夠滑動的布局。接下來我們要處理觸摸事件,以滿足以下要求:

    • 當Scroller滾動到0位置時,可以響應任何事件
    • 當Scroller滾動到mMenuWidth位置時,只有觸摸事件的X坐標小于50dp(大小任意設置)時,才能響應橫向滑動事件。
    • 當Scroller滾動到0的位置時,點擊事件的X坐標大于mMenuWidth時,使菜單View隱藏掉
    • 當手指抬起時Scroller滾動到小于mMenuWidth/2位置時,使Scroller平緩滾動到0,當手指抬起時Scroller滾動到大于mMenuWidth/2位置時,使Scroller平緩滾動到mMenuWidth
    • </ul>

      如果了解了View的事件分發機制,對觸摸事件的處理寫起來就沒有難度,只需要不斷的調試就能實現效果,在這里就不貼代碼了。

      </li> </ol>

      基本使用方法

          <com.opensource.slidemenu.SlideMenu
              android:id="@+id/menu"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              app:menu_layout="@layout/menu"
              app:content_layout="@layout/content"
              app:menu_width="300dp"
              >
          </com.opensource.slidemenu.SlideMenu>

      在@layout/menu和@layout/content中寫布局或者直接引入Fragment都可以,靈活度比較高。

      QQ側滑的思考

      雖然沒有動手去實現QQ的側滑,但我也對QQ的側滑菜單實現方法做了一些思考。

      由于QQ菜單在滑動的時候會出現菜單View被壓再內容View下面的情況,所以容器肯定不能使用LinearLayout,View疊加的頁面我們就需要使用FrameLayout。

      側滑的效果可以這樣實現:

      我們可以處理觸摸事件,在橫向滑動的時候,計算滑動距離,根據滑動距離計算此時 菜單View和內容View的距離屏幕最左邊的值,然后動態改變菜單View和內容View的margin值。

      項目地址: https://github.com/VeHan/SlideMenu

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!