Android自定義側(cè)滑菜單控件簡單實(shí)現(xiàn)

隨著Android的不斷成熟假瞬,許多絢麗的效果也在不斷的被大家開發(fā)出來哥捕,其中側(cè)滑的效果用到的項(xiàng)目很多竭望,用的好的更是給吸引了很多用戶怖辆。國內(nèi)像QQ和酷狗App的側(cè)滑就很給力是复,所以查了一些資料删顶,并結(jié)合ViewDragHelper輔助類竖螃,做了一種比較簡單的側(cè)滑實(shí)現(xiàn)方式。

一逗余、實(shí)現(xiàn)效果圖

  • 實(shí)現(xiàn)的效果基本跟酷狗App差不多特咆,因?yàn)榫褪欠略炜峁返膥~

二、實(shí)現(xiàn)原理

  • SlideLayout控件使用的是ViewDragHelper輔助類來實(shí)現(xiàn)的录粱。ViewDragHelper是一個(gè)實(shí)現(xiàn)View的拖拽的神器腻格,它把View的拖拽操作變得特別的簡單,不熟悉ViewDragHelper的同學(xué)請先上傳送門啥繁。

  • 要實(shí)現(xiàn)拖拽菜职,首先需要將SlideLayout和ViewDragHelper關(guān)聯(lián)起來,然后將SlideLayout的事件交給ViewDragHelper來處理旗闽,然后在ViewDragHelper提供的回調(diào)里就可以對View進(jìn)行各種操作酬核。不過拖拽的原理都是差不多的蜜另,通過水平或者豎直的移動(dòng)ViewGroup,然后不斷的layout和invalidate進(jìn)行重繪顯示。

  • 在滑動(dòng)的過程中嫡意,除了要不斷的計(jì)算滑動(dòng)的位置和重繪界面举瑰,還需要對子容器進(jìn)行不同的動(dòng)畫操作,這里使用的是ViewHelper類對View做平移縮放和漸變等動(dòng)畫蔬螟。

  • 另外還使用枚舉來記錄SlideLayout側(cè)滑的狀態(tài)此迅,包括關(guān)閉,打開和正在滑動(dòng)旧巾。并且提供PanelSlideListener監(jiān)聽滑動(dòng)的狀態(tài)耸序。這樣就可以根據(jù)不同的狀態(tài)做不同的操作。比如手動(dòng)打開側(cè)滑鲁猩,關(guān)閉側(cè)滑等等佑吝。

三、邏輯分析

這個(gè)項(xiàng)目實(shí)現(xiàn)的邏輯其實(shí)并不難绳匀,只需要計(jì)算出ViewGroup滑動(dòng)的位置芋忿,然后重繪就行,其次還需要計(jì)算控件縮放和拉伸的比例等等疾棵。當(dāng)然對各種View的操作方法還是要比較熟悉戈钢,不然搞不明白有些邏輯要做這里做。

1. SlideLayout應(yīng)該作為一個(gè)控件容器來包容兩個(gè)子容器是尔,一個(gè)菜單容器殉了,一個(gè)主容器,首先我們需要獲取SlideLayout容器的寬高和兩個(gè)子容器對象

  • 在View的onSizeChanged()方法里獲取SlideLayout的寬高拟枚,此時(shí)控件已經(jīng)測量完成

    /**
     * 當(dāng)控件的寬高發(fā)生變化時(shí)會(huì)回調(diào)這個(gè)方法薪铜,可以用來測量控件的寬高
     *
     * @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();
        /**
         * 初始化拖動(dòng)的范圍
         * 默認(rèn)為屏幕寬度的60%
         */
        mSlideRange = (int) (mSlideWidth * mRangePercent);
    }
    
  • 在View的onFinishInflate()方法里可以獲取容器對象,此時(shí)布局已經(jīng)填充

    /**
     * 當(dāng)View填充結(jié)束時(shí)會(huì)調(diào)用這個(gè)方法恩溅,可以獲取子View對象
     */
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() < 2) {
            throw new IllegalStateException("SlideLayout控件的子View必須大于2個(gè)");
        }
        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進(jìn)行綁定

  • 首先在控件的構(gòu)造里創(chuàng)建ViewDragHelper對象,創(chuàng)建完之后會(huì)有一個(gè)回調(diào)脚乡,而我們對View的各種操作就是在回調(diào)的各種方法里進(jìn)行的

    /** View的滑動(dòng)的輔助類蜒滩,在回調(diào)里監(jiān)聽View的各種操作
     * @param forParent 要進(jìn)行觸摸滑動(dòng)的父控件
     * @param sensitivity 控件滑動(dòng)的速度,敏感度奶稠,1.0f正常
     * @param cb  對View的事件發(fā)生改變的回調(diào)
     */
    mDragHelper = ViewDragHelper.create(this, 1.0f, mViewCallback);
    
  • 創(chuàng)建對象之后俯艰,如果此時(shí)就對View進(jìn)行操作是沒有效果的,因?yàn)檫€需要把SlideLayout的處理事件傳遞給ViewDragHelper

    /**
      * 轉(zhuǎn)交攔截事件給輔助類
      *
      * @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);
     }
    
     /**
      * 轉(zhuǎn)交觸摸事件給輔助類
      *
      * @param event
      * @return
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         try {
             mDragHelper.processTouchEvent(event);
         } catch (Exception e) {
             e.printStackTrace();
         }
         return true;
     }
    
  • 最重要的地方就是ViewDragHelper的回調(diào)了锌订,里面有很多方法竹握,每一個(gè)都很重要,這里列舉一個(gè)對容器的滑動(dòng)處理方法onViewPositionChanged()辆飘。其實(shí)邏輯也是比較簡單啦辐,就是判斷當(dāng)前滑動(dòng)的是哪一個(gè)容器污秆,計(jì)算容器的左邊界值,然后對容器進(jìn)行重繪

    /**
     * 當(dāng)子View的位置發(fā)送改變時(shí)回調(diào)
     * @param changedView 改變的子View
     * @param left 距離左邊界距離
     * @param top 距離頂部距離
     * @param dx 水平滑動(dòng)距離差
     * @param dy 豎直滑動(dòng)距離差
     */
    @Override
    public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
        /**
         * 將菜單面板的移動(dòng)量給主面板
         */
        if (changedView == mMenuContainer) {
            mMenuContainer.layout(0, 0, mSlideWidth, mSlideHeight);
            int newLeft = mMainContainer.getLeft() + dx;
            newLeft = fixLeft(newLeft);
            mMainContainer.layout(newLeft, 0, newLeft + mSlideWidth, mSlideHeight);
        }
    
        // 處理移動(dòng)事件
        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>
    
  • 代碼中獲取對象良拼,設(shè)置監(jiān)聽,設(shè)置打開或者關(guān)閉側(cè)滑

六充边、總結(jié)

有了ViewDragHelper這個(gè)輔助類庸推,對ViewGroup進(jìn)行操作相對來說已經(jīng)比較簡單了,只需要處理計(jì)算和繪制的工作浇冰,其他的都已經(jīng)做好了贬媒。當(dāng)然ViewDragHelper的作用遠(yuǎn)不于此,想要了解更多的同學(xué)可以去研究一下這個(gè)類的源碼肘习。這里也只是簡單的實(shí)現(xiàn)了側(cè)滑功能际乘,要想做的更完美的同學(xué)請自行修改。


Demo地址

歡迎大家訪問我的簡書漂佩,博客GitHub脖含。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市投蝉,隨后出現(xiàn)的幾起案子养葵,更是在濱河造成了極大的恐慌,老刑警劉巖瘩缆,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件关拒,死亡現(xiàn)場離奇詭異,居然都是意外死亡庸娱,警方通過查閱死者的電腦和手機(jī)着绊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來熟尉,“玉大人归露,你說我怎么就攤上這事〕加#” “怎么了靶擦?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雇毫。 經(jīng)常有香客問我,道長踩蔚,這世上最難降的妖魔是什么棚放? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮馅闽,結(jié)果婚禮上飘蚯,老公的妹妹穿的比我還像新娘馍迄。我一直安慰自己,他們只是感情好局骤,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布攀圈。 她就那樣靜靜地躺著,像睡著了一般峦甩。 火紅的嫁衣襯著肌膚如雪赘来。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天凯傲,我揣著相機(jī)與錄音犬辰,去河邊找鬼。 笑死冰单,一個(gè)胖子當(dāng)著我的面吹牛幌缝,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播诫欠,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼涵卵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荒叼?” 一聲冷哼從身側(cè)響起缘厢,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甩挫,沒想到半個(gè)月后贴硫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡伊者,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年英遭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片亦渗。...
    茶點(diǎn)故事閱讀 40,001評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挖诸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出法精,到底是詐尸還是另有隱情多律,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布搂蜓,位于F島的核電站狼荞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏帮碰。R本人自食惡果不足惜相味,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望殉挽。 院中可真熱鬧丰涉,春花似錦拓巧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至投慈,卻和暖如春承耿,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背逛裤。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工瘩绒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人带族。 一個(gè)月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓锁荔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蝙砌。 傳聞我的和親對象是個(gè)殘疾皇子阳堕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容