隨著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é)請自行修改。