-
大小: 0.98M文件類型: .rar金幣: 1下載: 0 次發布日期: 2021-02-01
- 標簽: 菜單??Android??BidirSliding??滑動??
資源簡介
BidirSlidingLayout_Android雙向滑動菜單完全解析,教你如何一分鐘實現雙向滑動特效
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 { /** * 滾動顯示和隱藏左側布局時,手指滑動需要達到的速度。 */ public static final int SNAP_VELOCITY = 200; /** * 滑動狀態的一種,表示未進行任何滑動。 */ public static final int DO_NOTHING = 0; /** * 滑動狀態的一種,表示正在滑出左側菜單。 */ public static final int SHOW_LEFT_MENU = 1; /** * 滑動狀態的一種,表示正在滑出右側菜單。 */ public static final int SHOW_RIGHT_MENU = 2; /** * 滑動狀態的一種,表示正在隱藏左側菜單。 */ public static final int HIDE_LEFT_MENU = 3; /** * 滑動狀態的一種,表示正在隱藏右側菜單。 */ public static final int HIDE_RIGHT_MENU = 4; /** * 記錄當前的滑動狀態 */ private int slideState; /** * 屏幕寬度值。 */ private int screenWidth; /** * 在被判定為滾動之前用戶手指可以移動的最大值。 */ private int touchSlop; /** * 記錄手指按下時的橫坐標。 */ private float xDown; /** * 記錄手指按下時的縱坐標。 */ private float yDown; /** * 記錄手指移動時的橫坐標。 */ private float xMove; /** * 記錄手指移動時的縱坐標。 */ private float yMove; /** * 記錄手機抬起時的橫坐標。 */ private float xUp; /** * 左側菜單當前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isLeftMenuVisible; /** * 右側菜單當前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isRightMenuVisible; /** * 是否正在滑動。 */ private boolean isSliding; /** * 左側菜單布局對象。 */ private View leftMenuLayout; /** * 右側菜單布局對象。 */ private View rightMenuLayout; /** * 內容布局對象。 */ private View contentLayout; /** * 用于監聽滑動事件的View。 */ private View mBindView; /** * 左側菜單布局的參數。 */ private MarginLayoutParams leftMenuLayoutParams; /** * 右側菜單布局的參數。 */ private MarginLayoutParams rightMenuLayoutParams; /** * 內容布局的參數。 */ private RelativeLayout.LayoutParams contentLayoutParams; /** * 用于計算手指滑動的速度。 */ private VelocityTracker mVelocityTracker; /** * 重寫BidirSlidingLayout的構造函數,其中獲取了屏幕的寬度和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(); } /** * 綁定監聽滑動事件的View。 * * @param bindView * 需要綁定的View對象。 */ public void setScrollEvent(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(this); } /** * 將界面滾動到左側菜單界面,滾動速度設定為-30. */ public void scrollToLeftMenu() { new LeftMenuScrollTask().execute(-30); } /** * 將界面滾動到右側菜單界面,滾動速度設定為-30. */ public void scrollToRightMenu() { new RightMenuScrollTask().execute(-30); } /** * 將界面從左側菜單滾動到內容界面,滾動速度設定為30. */ public void scrollToContentFromLeftMenu() { new LeftMenuScrollTask().execute(30); } /** * 將界面從右側菜單滾動到內容界面,滾動速度設定為30. */ public void scrollToContentFromRightMenu() { new RightMenuScrollTask().execute(30); } /** * 左側菜單是否完全顯示出來,滑動過程中此值無效。 * * @return 左側菜單完全顯示返回true,否則返回false。 */ public boolean isLeftLayoutVisible() { return isLeftMenuVisible; } /** * 右側菜單是否完全顯示出來,滑動過程中此值無效。 * * @return 右側菜單完全顯示返回true,否則返回false。 */ public boolean isRightLayoutVisible() { return isRightMenuVisible; } /** * 在onLayout中重新設定左側菜單、右側菜單、以及內容布局的參數。 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 獲取左側菜單布局對象 leftMenuLayout = getChildAt(0); leftMenuLayoutParams = (MarginLayoutParams) leftMenuLayout.getLayoutParams(); // 獲取右側菜單布局對象 rightMenuLayout = getChildAt(1); rightMenuLayoutParams = (MarginLayoutParams) rightMenuLayout.getLayoutParams(); // 獲取內容布局對象 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: // 手指按下時,記錄按下時的坐標 xDown = event.getRawX(); yDown = event.getRawY(); // 將滑動狀態初始化為DO_NOTHING slideState = DO_NOTHING; break; case MotionEvent.ACTION_MOVE: xMove = event.getRawX(); yMove = event.getRawY(); // 手指移動時,對比按下時的坐標,計算出移動的距離。 int moveDistanceX = (int) (xMove - xDown); int moveDistanceY = (int) (yMove - yDown); // 檢查當前的滑動狀態 checkSlideState(moveDistanceX, moveDistanceY); // 根據當前滑動狀態決定如何偏移內容布局 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) { // 手指抬起時,進行判斷當前手勢的意圖 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) { // 當左側菜單顯示時,如果用戶點擊一下內容部分,則直接滾動到內容界面 scrollToContentFromLeftMenu(); } else if (upDistanceX < touchSlop && isRightMenuVisible) { // 當右側菜單顯示時,如果用戶點擊一下內容部分,則直接滾動到內容界面 scrollToContentFromRightMenu(); } recycleVelocityTracker(); break; } if (v.isEnabled()) { if (isSliding) { // 正在滑動時讓控件得不到焦點 unFocusBindView(); return true; } if (isLeftMenuVisible || isRightMenuVisible) { // 當左側或右側布局顯示時,將綁定控件的事件屏蔽掉 return true; } return false; } return true; } /** * 根據手指移動的距離,判斷當前用戶的滑動意圖,然后給slideState賦值成相應的滑動狀態值。 * * @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); // 如果用戶想要滑動左側菜單,將左側菜單顯示,右側菜單隱藏 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); // 如果用戶想要滑動右側菜單,將右側菜單顯示,左側菜單隱藏 rightMenuLayout.setVisibility(View.VISIBLE); leftMenuLayout.setVisibility(View.GONE); } } } /** * 在滑動過程中檢查左側菜單的邊界值,防止綁定布局滑出屏幕。 */ private void checkLeftMenuBorder() { if (contentLayoutParams.rightMargin > 0) { contentLayoutParams.rightMargin = 0; } else if (contentLayoutParams.rightMargin < -leftMenuLayoutParams.width) { contentLayoutParams.rightMargin = -leftMenuLayoutParams.width; } } /** * 在滑動過程中檢查右側菜單的邊界值,防止綁定布局滑出屏幕。 */ private void checkRightMenuBorder() { if (contentLayoutParams.leftMargin > 0) { contentLayoutParams.leftMargin = 0; } else if (contentLayoutParams.leftMargin < -rightMenuLayoutParams.width) { contentLayoutParams.leftMargin = -rightMenuLayoutParams.width; } } /** * 判斷是否應該滾動將左側菜單展示出來。如果手指移動距離大于左側菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認為應該滾動將左側菜單展示出來。 * * @return 如果應該將左側菜單展示出來返回true,否則返回false。 */ private boolean shouldScrollToLeftMenu() { return xUp - xDown > leftMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應該滾動將右側菜單展示出來。如果手指移動距離大于右側菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認為應該滾動將右側菜單展示出來。 * * @return 如果應該將右側菜單展示出來返回true,否則返回false。 */ private boolean shouldScrollToRightMenu() { return xDown - xUp > rightMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應該從左側菜單滾動到內容布局,如果手指移動距離大于左側菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認為應該從左側菜單滾動到內容布局。 * * @return 如果應該從左側菜單滾動到內容布局返回true,否則返回false。 */ private boolean shouldScrollToContentFromLeftMenu() { return xDown - xUp > leftMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應該從右側菜單滾動到內容布局,如果手指移動距離大于右側菜單寬度的1/2,或者手指移動速度大于SNAP_VELOCITY, * 就認為應該從右側菜單滾動到內容布局。 * * @return 如果應該從右側菜單滾動到內容布局返回true,否則返回false。 */ private boolean shouldScrollToContentFromRightMenu() { return xUp - xDown > rightMenuLayoutParams.width / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 創建VelocityTracker對象,并將觸摸事件加入到VelocityTracker當中。 * * @param event * 右側布局監聽控件的滑動事件 */ 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; // 根據傳入的速度來滾動界面,當滾動到達邊界值時,跳出循環。 while (true) { rightMargin = rightMargin speed[0]; if (rightMargin < -leftMenuLayoutParams.width) { rightMargin = -leftMenuLayoutParams.width; break; } if (rightMargin > 0) { rightMargin = 0; break; } publishProgress(rightMargin); // 為了要有滾動效果產生,每次循環使線程睡眠一段時間,這樣肉眼才能夠看到滾動動畫。 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; // 根據傳入的速度來滾動界面,當滾動到達邊界值時,跳出循環。 while (true) { leftMargin = leftMargin speed[0]; if (leftMargin < -rightMenuLayoutParams.width) { leftMargin = -rightMenuLayoutParams.width; break; } if (leftMargin > 0) { leftMargin = 0; break; } publishProgress(leftMargin); // 為了要有滾動效果產生,每次循環使線程睡眠一段時間,這樣肉眼才能夠看到滾動動畫。 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); } } /** * 使當前線程睡眠指定的毫秒數。 * * @param millis * 指定當前線程睡眠多久,以毫秒為單位 */ 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個文件信息
- 上一篇:android listview下拉刷新
- 下一篇:斷點續傳 單任務斷點
評論
共有 條評論