Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 自定義布局實現側滑菜單2

自定義布局實現側滑菜單2

編輯:關於Android編程

我們在上一節已經說了側滑菜單的實現原理,並且實現了單側菜單,這一節我們就完善項目,實現雙向側滑菜單。原理我們都說了,不明白的看上節,好了,直接上代碼
/** * 這個類和SlidingLayout作用一樣,只是沒有實現觸摸監聽事件,直接在121行設置實現了 */ public class SlidingLayout1 extends LinearLayout { /** * 滾動顯示和隱藏左側布局時,手指滑動需要達到的速度。 */ public static final int SNAP_VELOCITY = 200; /** * 屏幕寬度值。 */ private int screenWidth; /** * 左側布局最多可以滑動到的左邊緣。值由左側布局的寬度來定,marginLeft到達此值之後,不能再減少。 */ private int leftEdge; /** * 左側布局最多可以滑動到的右邊緣。值恆為0,即marginLeft到達0之後,不能增加。 */ private int rightEdge = 0; /** * 左側布局完全顯示時,留給右側布局的寬度值。 */ private int leftLayoutPadding = 80; /** * 記錄手指按下時的橫坐標。 */ private float xDown; /** * 記錄手指移動時的橫坐標。 */ private float xMove; /** * 記錄手機抬起時的橫坐標。 */ private float xUp; /** * 左側布局當前是顯示還是隱藏。只有完全顯示或隱藏時才會更改此值,滑動過程中此值無效。 */ private boolean isLeftLayoutVisible; /** * 左側布局對象。 */ private View leftLayout; /** * 右側布局對象。 */ private View rightLayout; /** * 用於監聽側滑事件的View。 */ private View mBindView; /** * 左側布局的參數,通過此參數來重新確定左側布局的寬度,以及更改leftMargin的值。 */ private MarginLayoutParams leftLayoutParams; /** * 右側布局的參數,通過此參數來重新確定右側布局的寬度。 */ private MarginLayoutParams rightLayoutParams; /** * 用於計算手指滑動的速度。 */ private VelocityTracker mVelocityTracker; /** * 重寫SlidingLayout的構造函數,其中獲取了屏幕的寬度。 * * @param context * @param attrs */ public SlidingLayout1(Context context, AttributeSet attrs) { super(context, attrs); WindowManager wm = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); Display display = wm.getDefaultDisplay(); DisplayMetrics metrics = new DisplayMetrics(); display.getMetrics(metrics); // 獲得屏幕寬和高 screenWidth = metrics.widthPixels; // screenWidth = wm.getDefaultDisplay().getWidth(); } /** * 綁定監聽側滑事件的View,即在綁定的View進行滑動才可以顯示和隱藏左側布局。 * * @param bindView 需要綁定的View對象。 */ public void setScrollEvent(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 手指按下時,記錄按下時的橫坐標 xDown = event.getRawX(); break; case MotionEvent.ACTION_MOVE: // 手指移動時,對比按下時的橫坐標,計算出移動的距離,來調整左側布局的leftMargin值,從而顯示和隱藏左側布局 xMove = event.getRawX(); int distanceX = (int) (xMove - xDown); if (isLeftLayoutVisible) { leftLayoutParams.leftMargin = distanceX; } else { leftLayoutParams.leftMargin = leftEdge + distanceX; } if (leftLayoutParams.leftMargin < leftEdge) { leftLayoutParams.leftMargin = leftEdge; } else if (leftLayoutParams.leftMargin > rightEdge) { leftLayoutParams.leftMargin = rightEdge; } leftLayout.setLayoutParams(leftLayoutParams); break; case MotionEvent.ACTION_UP: // 手指抬起時,進行判斷當前手勢的意圖,從而決定是滾動到左側布局,還是滾動到右側布局 xUp = event.getRawX(); if (wantToShowLeftLayout()) { if (shouldScrollToLeftLayout()) { scrollToLeftLayout(); } else { scrollToRightLayout(); } } else if (wantToShowRightLayout()) { if (shouldScrollToContent()) { scrollToRightLayout(); } else { scrollToLeftLayout(); } } recycleVelocityTracker(); break; } return isBindBasicLayout(); } }); } /** * 將屏幕滾動到左側布局界面,滾動速度設定為30. */ public void scrollToLeftLayout() { new ScrollTask().execute(30); } /** * 將屏幕滾動到右側布局界面,滾動速度設定為-30. */ public void scrollToRightLayout() { new ScrollTask().execute(-30); } /** * 左側布局是否完全顯示出來,或完全隱藏,滑動過程中此值無效。 * * @return 左側布局完全顯示返回true,完全隱藏返回false。 */ public boolean isLeftLayoutVisible() { return isLeftLayoutVisible; } /** * 在onLayout中重新設定左側布局和右側布局的參數。 */ @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (changed) { // 獲取左側布局對象 leftLayout = getChildAt(0); leftLayoutParams = (MarginLayoutParams) leftLayout .getLayoutParams(); // 重置左側布局對象的寬度為屏幕寬度減去leftLayoutPadding leftLayoutParams.width = screenWidth - leftLayoutPadding; // 設置最左邊距為負的左側布局的寬度 leftEdge = -leftLayoutParams.width; leftLayoutParams.leftMargin = leftEdge; leftLayout.setLayoutParams(leftLayoutParams); // 獲取右側布局對象 rightLayout = getChildAt(1); rightLayoutParams = (MarginLayoutParams) rightLayout .getLayoutParams(); rightLayoutParams.width = screenWidth; rightLayout.setLayoutParams(rightLayoutParams); } } /** * 判斷當前手勢的意圖是不是想顯示右側布局。如果手指移動的距離是負數,且當前左側布局是可見的,則認為當前手勢是想要顯示右側布局。 * * @return 當前手勢想顯示右側布局返回true,否則返回false。 */ private boolean wantToShowRightLayout() { return xUp - xDown < 0 && isLeftLayoutVisible; } /** * 判斷當前手勢的意圖是不是想顯示左側布局。如果手指移動的距離是正數,且當前左側布局是不可見的,則認為當前手勢是想要顯示左側布局。 * * @return 當前手勢想顯示左側布局返回true,否則返回false。 */ private boolean wantToShowLeftLayout() { return xUp - xDown > 0 && !isLeftLayoutVisible; } /** * 判斷是否應該滾動將左側布局展示出來。如果手指移動距離大於屏幕的1/2,或者手指移動速度大於SNAP_VELOCITY, * 就認為應該滾動將左側布局展示出來。 * * @return 如果應該滾動將左側布局展示出來返回true,否則返回false。 */ private boolean shouldScrollToLeftLayout() { return xUp - xDown > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷是否應該滾動將右側布局展示出來。如果手指移動距離加上leftLayoutPadding大於屏幕的1/2, * 或者手指移動速度大於SNAP_VELOCITY, 就認為應該滾動將右側布局展示出來。 * * @return 如果應該滾動將右側布局展示出來返回true,否則返回false。 */ private boolean shouldScrollToContent() { return xDown - xUp + leftLayoutPadding > screenWidth / 2 || getScrollVelocity() > SNAP_VELOCITY; } /** * 判斷綁定滑動事件的View是不是一個基礎layout,不支持自定義layout,只支持四種基本layout, * AbsoluteLayout已被棄用。 * * @return 如果綁定滑動事件的View是LinearLayout, RelativeLayout, FrameLayout, * TableLayout之一就返回true,否則返回false。 */ private boolean isBindBasicLayout() { if (mBindView == null) { return false; } String viewName = mBindView.getClass().getName(); return viewName.equals(LinearLayout.class.getName()) || viewName.equals(RelativeLayout.class.getName()) || viewName.equals(FrameLayout.class.getName()) || viewName.equals(TableLayout.class.getName()); } /** * 創建VelocityTracker對象,並將觸摸事件加入到VelocityTracker當中。 * * @param event 右側布局監聽控件的滑動事件 */ private void createVelocityTracker(MotionEvent event) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 獲取手指在右側布局的監聽View上的滑動速度。 * * @return 滑動速度,以每秒鐘移動了多少像素值為單位。 */ private int getScrollVelocity() { mVelocityTracker.computeCurrentVelocity(1000); int velocity = (int) mVelocityTracker.getXVelocity(); return Math.abs(velocity); } /** * 回收VelocityTracker對象。 */ private void recycleVelocityTracker() { mVelocityTracker.recycle(); mVelocityTracker = null; } class ScrollTask extends AsyncTask { @Override protected Integer doInBackground(Integer... speed) { int leftMargin = leftLayoutParams.leftMargin; // 根據傳入的速度來滾動界面,當滾動到達左邊界或右邊界時,跳出循環。 while (true) { leftMargin = leftMargin + speed[0]; if (leftMargin > rightEdge) { leftMargin = rightEdge; break; } if (leftMargin < leftEdge) { leftMargin = leftEdge; break; } publishProgress(leftMargin); // 為了要有滾動效果產生,每次循環使線程睡眠20毫秒,這樣肉眼才能夠看到滾動動畫。 try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } if (speed[0] > 0) { isLeftLayoutVisible = true; } else { isLeftLayoutVisible = false; } return leftMargin; } @Override protected void onProgressUpdate(Integer... leftMargin) { leftLayoutParams.leftMargin = leftMargin[0]; leftLayout.setLayoutParams(leftLayoutParams); } @Override protected void onPostExecute(Integer leftMargin) { leftLayoutParams.leftMargin = leftMargin; leftLayout.setLayoutParams(leftLayoutParams); } } }
首先在onLayout()方法中分別獲取到左側菜單、右側菜單和內容布局的參數,並將內容布局的寬度重定義成屏幕的寬度,這樣就可以保證內容 布局既能覆蓋住下面的菜單布局,還能偏移出屏幕。然後在onTouch()方法中監聽觸屏事件,以判斷用戶手勢的意圖。這裡事先定義好了幾種滑動狀 態,DO_NOTHING表示沒有進行任何滑動,SHOW_LEFT_MENU表示用戶想要滑出左側菜單,SHOW_RIGHT_MENU表示用戶想要滑 出右側菜單,HIDE_LEFT_MENU表示用戶想要隱藏左側菜單,HIDE_RIGHT_MENU表示用戶想要隱藏右側菜單,在 checkSlideState()方法中判斷出用戶到底是想進行哪一種滑動操作,並給slideState變量賦值,然後根據slideState的值 決定如何偏移內容布局。接著當用戶手指離開屏幕時,會根據當前的滑動距離,決定後續的滾動方向,通過LeftMenuScrollTask和 RightMenuScrollTask來完成完整的滑動過程。另外在滑動的過程,內容布局上的事件會被屏蔽掉,主要是通過一系列的return操作實現。   在布局中引用

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/slidingLayout_fragment_double"
android:layout_width="match_parent"
android:layout_height="match_parent" >

    android:layout_width="280dip"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#00ccff"
android:visibility="invisible" >

           android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="This is left menu"
android:textColor="#000000"
android:textSize="28sp" />
       

       android:layout_width="280dip"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:background="#00ffcc"
android:visibility="invisible" >

           android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="This is right menu"
android:textColor="#000000"
android:textSize="28sp" />
       

       android:id="@+id/content"
android:layout_width="320dip"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="#e9e9e9" >
          android:layout_width="match_parent"
android:layout_height="wrap_content"
>
              android:id="@+id/show_left_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="show left"
/>
              android:id="@+id/show_right_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="show right"
/>

android:id="@+id/contentList"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:scrollbars="none"
android:cacheColorHint="#00000000" >
           
       

使用了自定義的SlidingLayout2作為根布局,然後依次加入了三個子布局分別作為左側菜單、右側菜單和內容的布局。左側菜單和右側菜單中都只是簡單地放入了一個TextView用於顯示一段文字,內容布局中放入了一個ListView。注意要讓左側菜單和父布局左邊緣對齊,右側菜單和父布局右邊緣對齊。   用法也是參照上篇的寫法,我也寫在了一個Fragment中:

public class DoubleMenu extends Fragment { /** * 雙向滑動菜單布局 */ private SlidingLayout2 doubleSldingLayout; /** * 在內容布局上顯示的ListView */ private ListView contentList; /** * ListView的適配器 */ private ArrayAdapter contentListAdapter; /** * 用於填充contentListAdapter的數據源。 */ private List dataList; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_double, container, false); doubleSldingLayout = (SlidingLayout2) view.findViewById(R.id.slidingLayout_fragment_double); Button showLeftButton = (Button) view.findViewById(R.id.show_left_button); Button showRightButton = (Button) view.findViewById(R.id.show_right_button); contentList = (ListView) view.findViewById(R.id.contentList); dataList=new ArrayList<>(); for (int i=0;i<15;i++) dataList.add("Item "+i); contentListAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, dataList); contentList.setAdapter(contentListAdapter); doubleSldingLayout.setScrollEvent(contentList); showLeftButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (doubleSldingLayout.isLeftLayoutVisible()) { doubleSldingLayout.scrollToContentFromLeftMenu(); } else { doubleSldingLayout.initShowLeftState(); doubleSldingLayout.scrollToLeftMenu(); } } }); showRightButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (doubleSldingLayout.isRightLayoutVisible()) { doubleSldingLayout.scrollToContentFromRightMenu(); } else { doubleSldingLayout.initShowRightState(); doubleSldingLayout.scrollToRightMenu(); } } }); return view; } }

看下效果:

\

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved