-
大小: 0.98M文件類型: .rar金幣: 1下載: 0 次發(fā)布日期: 2021-02-01
- 標(biāo)簽: 菜單??Android??BidirSliding??滑動??
資源簡介
BidirSlidingLayout_Android雙向滑動菜單完全解析,教你如何一分鐘實現(xiàn)雙向滑動特效
package com.example.bidirslidinglayout; import android.content.Context; import android.os.AsyncTask; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.View.OnTouchListener; import android.view.WindowManager; import android.widget.RelativeLayout; /** * 雙向滑動菜單框架 * * @author guolin */ public class BidirSlidingLayout extends RelativeLayout implements OnTouchListener { /** * 滾動顯示和隱藏左側(cè)布局時,手指滑動需要達到的速度。 */ public static final int SNAP_VELOCITY = 200; /** * 滑動狀態(tài)的一種,表示未進行任何滑動。 */ public static final int DO_NOTHING = 0; /** * 滑動狀態(tài)的一種,表示正在滑出左側(cè)菜單。 */ public static final int SHOW_LEFT_MENU = 1; /** * 滑動狀態(tài)的一種,表示正在滑出右側(cè)菜單。 */ public static final int SHOW_RIGHT_MENU = 2; /** * 滑動狀態(tài)的一種,表示正在隱藏左側(cè)菜單。 */ public static final int HIDE_LEFT_MENU = 3; /** * 滑動狀態(tài)的一種,表示正在隱藏右側(cè)菜單。 */ public static final int HIDE_RIGHT_MENU = 4; /** * 記錄當(dāng)前的滑動狀態(tài) */ private int slideState; /** * 屏幕寬度值。 */ private int screenWidth; /** * 在被判定為滾動之前用戶手指可以移動的最大值。 */ private int touchSlop; /** * 記錄手指按下時的橫坐標(biāo)。 */ private float xDown; /** * 記錄手指按下時的縱坐標(biāo)。 */ private float yDown; /** * 記錄手指移動時的橫坐標(biāo)。 */ private float xMove; /** * 記錄手指移動時的縱坐標(biāo)。 */ private float yMove; /** * 記錄手機抬起時的橫坐標(biāo)。 */ private float xUp; /** * 左側(cè)菜單當(dāng)前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isLeftMenuVisible; /** * 右側(cè)菜單當(dāng)前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isRightMenuVisible; /** * 是否正在滑動。 */ private boolean isSliding; /** * 左側(cè)菜單布局對象。 */ private View leftMenuLayout; /** * 右側(cè)菜單布局對象。 */ private View rightMenuLayout; /** * 內(nèi)容布局對象。 */ private View contentLayout; /** * 用于監(jiān)聽滑動事件的View。 */ private View mBindView; /** * 左側(cè)菜單布局的參數(shù)。 */ private MarginLayoutParams leftMenuLayoutParams; /** * 右側(cè)菜單布局的參數(shù)。 */ private MarginLayoutParams rightMenuLayoutParams; /** * 內(nèi)容布局的參數(shù)。 */ private RelativeLayout.LayoutParams contentLayoutParams; /** * 用于計算手指滑動的速度。 */ private VelocityTracker mVelocityTracker; /** * 重寫B(tài)idirSlidingLayout的構(gòu)造函數(shù),其中獲取了屏幕的寬度和touchSlop的值。 * * @param context * @param attrs */ public BidirSlidingLayout(Context context, AttributeSet attrs) { super(context, attrs); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); screenWidth = wm.getDefaultDisplay().getWidth(); touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } /** * 綁定監(jiān)聽滑動事件的View。 * * @param bindView * 需要綁定的View對象。 */ public void setScrollEvent(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(this); } /** * 將界面滾動到左側(cè)菜單界面,滾動速度設(shè)定為-30. */ public void scrollToLeftMenu() { new LeftMenuScrollTask().execute(-30); } /** * 將界面滾動到右側(cè)菜單界面,滾動速度設(shè)定為-30. */ public void scrollToRightMenu() { new RightMenuScrollTask().execute(-30); } /** * 將界面從左側(cè)菜單滾動到內(nèi)容界面,滾動速度設(shè)定為30. */ public void scrollToContentFromLeftMenu() { new LeftMenuScrollTask().execute(30); } /** * 將界面從右側(cè)菜單滾動到內(nèi)容界面,滾動速度設(shè)定為30. */ public void scrollToContentFromRightMenu() { new RightMenuScrollTask().execute(30); } /** * 左側(cè)菜單是否完全顯示出來,滑動過程中此值無效。 * * @return 左側(cè)菜單完全顯示返回true,否則返回false。 */ public boolean isLeftLayoutVisible() { return isLeftMenuVisible; } /** * 右側(cè)菜單是否完全顯示出來,滑動過程中此值無效。 * * @return 右側(cè)菜單完全顯示返回true,否則返回false。 */ public boolean isRightLayoutVisible() { return isRightMenuVisible; } /** * 在onLayout中重新設(shè)定左側(cè)菜單、右側(cè)菜單、以及內(nèi)容布局的參數(shù)。 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 獲取左側(cè)菜單布局對象 leftMenuLayout = getChildAt(0); leftMenuLayoutParams = (MarginLayoutParams) leftMenuLayout.getLayoutParams(); // 獲取右側(cè)菜單布局對象 rightMenuLayout = getChildAt(1); rightMenuLayoutParams = (MarginLayoutParams) rightMenuLayout.getLayoutParams(); // 獲取內(nèi)容布局對象 contentLayout = getChildAt(2); contentLayoutParams = (RelativeLayout.LayoutParams) contentLayout.getLayoutParams(); contentLayoutParams.width = screenWidth; contentLayout.setLayoutParams(contentLayoutParams); } } @Override public boolean onTouch(View v, MotionEvent event) { createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下時,記錄按下時的坐標(biāo) xDown = event.getRawX(); yDown = event.getRawY(); // 將滑動狀態(tài)初始化為DO_NOTHING slideState = DO_NOTHING; break; case MotionEvent.ACTION_MOVE: xMove = event.getRawX(); yMove = event.getRawY(); // 手指移動時,對比按下時的坐標(biāo),計算出移動的距離。 int moveDistanceX = (int) (xMove - xDown); int moveDistanceY = (int) (yMove - yDown); // 檢查當(dāng)前的滑動狀態(tài) checkSlideState(moveDistanceX, moveDistanceY); // 根據(jù)當(dāng)前滑動狀態(tài)決定如何偏移內(nèi)容布局 switch (slideState) { case SHOW_LEFT_MENU: contentLayoutParams.rightMargin = -moveDistanceX; checkLeftMenuBorder(); contentLayout.setLayoutParams(contentLayoutParams); break; case HIDE_LEFT_MENU: contentLayoutParams.rightMargin = -leftMenuLayoutParams.width - moveDistanceX; checkLeftMenuBorder(); contentLayout.setLayoutParams(contentLayoutParams); case SHOW_RIGHT_MENU: contentLayoutParams.leftMargin = moveDistanceX; checkRightMenuBorder(); contentLayout.setLayoutParams(contentLayoutParams); break; case HIDE_RIGHT_MENU: contentLayoutParams.leftMargin = -rightMenuLayoutParams.width moveDistanceX; checkRightMenuBorder(); contentLayout.setLayoutParams(contentLayoutParams); default: break; } break; case MotionEvent.ACTION_UP: xUp = event.getRawX(); int upDistanceX = (int) (xUp - xDown); if (isSliding) { // 手指抬起時,進行判斷當(dāng)前手勢的意圖 switch (slideState) { case SHOW_LEFT_MENU: if (shouldScrollToLeftMenu()) { scrollToLeftMenu(); } else { scrollToContentFromLeftMenu(); } break; case HIDE_LEFT_MENU: if (shouldScrollToContentFromLeftMenu()) { scrollToContentFromLeftMenu(); } else { scrollToLeftMenu(); } break; case SHOW_RIGHT_MENU: if (shouldScrollToRightMenu()) { scrollToRightMenu(); } else { scrollToContentFromRightMenu(); } break; case HIDE_RIGHT_MENU: if (shouldScrollToContentFromRightMenu()) { scrollToContentFromRightMenu(); } else { scrollToRightMenu(); } break; default: break; } } else if (upDistanceX < touchSlop && isLeftMenuVisible) { // 當(dāng)左側(cè)菜單顯示時,如果用戶點擊一下內(nèi)容部分,則直接滾動到內(nèi)容界面 scrollToContentFromLeftMenu(); } else if (upDistanceX < touchSlop && isRightMenuVisible) { // 當(dāng)右側(cè)菜單顯示時,如果用戶點擊一下內(nèi)容部分,則直接滾動到內(nèi)容界面 scrollToContentFromRightMenu(); } recycleVelocityTracker(); break; } if (v.isEnabled()) { if (isSliding) { // 正在滑動時讓控件得不到焦點 unFocusBindView(); return true; } if (isLeftMenuVisible || isRightMenuVisible) { // 當(dāng)左側(cè)或右側(cè)布局顯示時,將綁定控件的事件屏蔽掉 return true; } return false; } return true; } /** * 根據(jù)手指移動的距離,判斷當(dāng)前用戶的滑動意圖,然后給slideState賦值成相應(yīng)的滑動狀態(tài)值。 * * @param moveDistanceX * 橫向移動的距離 * @param moveDistanceY * 縱向移動的距離 */ private void checkSlideState(int moveDistanceX, int moveDistanceY) { if (isLeftMenuVisible) { if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX < 0) { isSliding = true; slideState = HIDE_LEFT_MENU; } } else if (isRightMenuVisible) { if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX > 0) { isSliding = true; slideState = HIDE_RIGHT_MENU; } } else { if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX > 0 && Math.abs(moveDistanceY) < touchSlop) { isSliding = true; slideState = SHOW_LEFT_MENU; contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0); contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT); contentLayout.setLayoutParams(contentLayoutParams); // 如果用戶想要滑動左側(cè)菜單,將左側(cè)菜單顯示,右側(cè)菜單隱藏 leftMenuLayout.setVisibility(View.VISIBLE); rightMenuLayout.setVisibility(View.GONE); } else if (!isSliding && Math.abs(moveDistanceX) >= touchSlop && moveDistanceX < 0 && Math.abs(moveDistanceY) < touchSlop) { isSliding = true; slideState = SHOW_RIGHT_MENU; contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); contentLayoutParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT); contentLayout.setLayoutParams(contentLayoutParams); // 如果用戶想要滑動右側(cè)菜單,將右側(cè)菜單顯示,左側(cè)菜單隱藏 rightMenuLayout.setVisibility(View.VISIBLE); leftMenuLayout.setVisibility(View.GONE); } } } /** * 在滑動過程中檢查左側(cè)菜單的邊界值,防止綁定布局滑出屏幕。 */ private void checkLeftMenuBorder() { if (contentLayoutParams.rightMargin > 0) { contentLayoutParams.rightMargin = 0; } else if (contentLayoutParams.rightMargin < -leftMenuLayoutParams.width) { contentLayoutParams.rightMargin = -leftMenuLayoutParams.width; } } /** * 在滑動過程中檢查右側(cè)菜單的邊界值,防止綁定布局滑出屏幕。 */ private void checkRightMenuBorder() { if (contentLayoutParams.leftMargin > 0) { contentLayoutParams.leftMargin = 0; } else if (contentLayoutParams.leftMargin < -rightMenuLayoutParams.width) { contentLayoutParams.leftMargin = -rightMenuLayoutParams.width; } } /** * 判斷是否應(yīng)該滾動將左側(cè)菜單展示出來。如果手指移動距離大于左側(cè)菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認(rèn)為應(yīng)該滾動將左側(cè)菜單展示出來。 * * @return 如果應(yīng)該將左側(cè)菜單展示出來返回true,否則返回false。 */ private boolean shouldScrollToLeftMenu() { return xUp - xDown > leftMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應(yīng)該滾動將右側(cè)菜單展示出來。如果手指移動距離大于右側(cè)菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認(rèn)為應(yīng)該滾動將右側(cè)菜單展示出來。 * * @return 如果應(yīng)該將右側(cè)菜單展示出來返回true,否則返回false。 */ private boolean shouldScrollToRightMenu() { return xDown - xUp > rightMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應(yīng)該從左側(cè)菜單滾動到內(nèi)容布局,如果手指移動距離大于左側(cè)菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認(rèn)為應(yīng)該從左側(cè)菜單滾動到內(nèi)容布局。 * * @return 如果應(yīng)該從左側(cè)菜單滾動到內(nèi)容布局返回true,否則返回false。 */ private boolean shouldScrollToContentFromLeftMenu() { return xDown - xUp > leftMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應(yīng)該從右側(cè)菜單滾動到內(nèi)容布局,如果手指移動距離大于右側(cè)菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認(rèn)為應(yīng)該從右側(cè)菜單滾動到內(nèi)容布局。 * * @return 如果應(yīng)該從右側(cè)菜單滾動到內(nèi)容布局返回true,否則返回false。 */ private boolean shouldScrollToContentFromRightMenu() { return xUp - xDown > rightMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 創(chuàng)建VelocityTracker對象,并將觸摸事件加入到VelocityTracker當(dāng)中。 * * @param event * 右側(cè)布局監(jiān)聽控件的滑動事件 */ private void createVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 獲取手指在綁定布局上的滑動速度。 * * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 */ private int getScrollVelocity() { mVelocityTracker.computeCurrentVelocity(1000); int velocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocity); } /** * 回收VelocityTracker對象。 */ private void recycleVelocityTracker() { mVelocityTracker.recycle(); mVelocityTracker = null; } /** * 使用可以獲得焦點的控件在滑動的時候失去焦點。 */ private void unFocusBindView() { if (mBindView != null) { mBindView.setPressed(false); mBindView.setFocusable(false); mBindView.setFocusableInTouchMode(false); } } class LeftMenuScrollTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer... speed) { int rightMargin = contentLayoutParams.rightMargin; // 根據(jù)傳入的速度來滾動界面,當(dāng)滾動到達邊界值時,跳出循環(huán)。 while (true) { rightMargin = rightMargin speed[0]; if (rightMargin < -leftMenuLayoutParams.width) { rightMargin = -leftMenuLayoutParams.width; break; } if (rightMargin > 0) { rightMargin = 0; break; } publishProgress(rightMargin); // 為了要有滾動效果產(chǎn)生,每次循環(huán)使線程睡眠一段時間,這樣肉眼才能夠看到滾動動畫。 sleep(15); } if (speed[0] > 0) { isLeftMenuVisible = false; } else { isLeftMenuVisible = true; } isSliding = false; return rightMargin; } @Override protected void onProgressUpdate(Integer... rightMargin) { contentLayoutParams.rightMargin = rightMargin[0]; contentLayout.setLayoutParams(contentLayoutParams); unFocusBindView(); } @Override protected void onPostExecute(Integer rightMargin) { contentLayoutParams.rightMargin = rightMargin; contentLayout.setLayoutParams(contentLayoutParams); } } class RightMenuScrollTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer... speed) { int leftMargin = contentLayoutParams.leftMargin; // 根據(jù)傳入的速度來滾動界面,當(dāng)滾動到達邊界值時,跳出循環(huán)。 while (true) { leftMargin = leftMargin speed[0]; if (leftMargin < -rightMenuLayoutParams.width) { leftMargin = -rightMenuLayoutParams.width; break; } if (leftMargin > 0) { leftMargin = 0; break; } publishProgress(leftMargin); // 為了要有滾動效果產(chǎn)生,每次循環(huán)使線程睡眠一段時間,這樣肉眼才能夠看到滾動動畫。 sleep(15); } if (speed[0] > 0) { isRightMenuVisible = false; } else { isRightMenuVisible = true; } isSliding = false; return leftMargin; } @Override protected void onProgressUpdate(Integer... leftMargin) { contentLayoutParams.leftMargin = leftMargin[0]; contentLayout.setLayoutParams(contentLayoutParams); unFocusBindView(); } @Override protected void onPostExecute(Integer leftMargin) { contentLayoutParams.leftMargin = leftMargin; contentLayout.setLayoutParams(contentLayoutParams); } } /** * 使當(dāng)前線程睡眠指定的毫秒數(shù)。 * * @param millis * 指定當(dāng)前線程睡眠多久,以毫秒為單位 */ private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } }
代碼片段和文件信息
/**?Automatically?generated?file.?DO?NOT?MODIFY?*/
package?com.example.bidirslidinglayout;
public?final?class?BuildConfig?{
????public?final?static?boolean?DEBUG?=?true;
}
?屬性????????????大小?????日期????時間???名稱
-----------?---------??----------?-----??----
?????文件????????364??2013-08-21?19:08??BidirSlidingLayout\.classpath
?????文件????????854??2013-08-21?19:08??BidirSlidingLayout\.project
?????文件????????900??2013-08-21?19:08??BidirSlidingLayout\AndroidManifest.xm
?????文件????????900??2013-08-21?19:09??BidirSlidingLayout\bin\AndroidManifest.xm
?????文件?????199753??2013-08-26?13:43??BidirSlidingLayout\bin\BidirSlidingLayout.apk
?????文件???????2816??2013-08-26?17:34??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\BidirSlidingLayout$LeftMenuScrollTask.class
?????文件???????2818??2013-08-26?17:34??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\BidirSlidingLayout$RightMenuScrollTask.class
?????文件???????9043??2013-08-26?17:34??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\BidirSlidingLayout.class
?????文件????????369??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\BuildConfig.class
?????文件???????1813??2013-08-26?13:46??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\MainActivity.class
?????文件????????376??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$attr.class
?????文件????????497??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$dimen.class
?????文件????????443??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$drawable.class
?????文件????????606??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$id.class
?????文件????????439??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$layout.class
?????文件????????424??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$menu.class
?????文件????????508??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$string.class
?????文件????????467??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R$st
?????文件????????793??2013-08-26?09:07??BidirSlidingLayout\bin\classes\com\example\bidirslidinglayout\R.class
?????文件?????598440??2013-08-26?13:42??BidirSlidingLayout\bin\classes.dex
?????文件?????151838??2013-08-22?13:55??BidirSlidingLayout\bin\dexedLibs\android-support-v4-cd0bcb705e4459eaa6cf7902e4dfc5a4.jar
?????文件????????943??2013-08-22?13:55??BidirSlidingLayout\bin\dexedLibs\annotations-7e80f6f7c9d949b206a3796ca0225e80.jar
?????文件????????119??2013-08-26?09:07??BidirSlidingLayout\bin\jarlist.cache
?????文件???????5964??2013-08-22?13:55??BidirSlidingLayout\bin\res\drawable-hdpi\ic_launcher.png
?????文件???????3112??2013-08-22?13:55??BidirSlidingLayout\bin\res\drawable-mdpi\ic_launcher.png
?????文件???????9355??2013-08-22?13:55??BidirSlidingLayout\bin\res\drawable-xhdpi\ic_launcher.png
?????文件??????17889??2013-08-22?13:55??BidirSlidingLayout\bin\res\drawable-xxhdpi\ic_launcher.png
?????文件??????41183??2013-08-26?13:43??BidirSlidingLayout\bin\resources.ap_
?????文件????????172??2013-08-21?19:09??BidirSlidingLayout\gen\com\example\bidirslidinglayout\BuildConfig.java
?????文件???????2354??2013-08-26?09:07??BidirSlidingLayout\gen\com\example\bidirslidinglayout\R.java
............此處省略53個文件信息
評論
共有 條評論