1. 程式人生 > >Android雙向滑動選單特效

Android雙向滑動選單特效

在一個Activity的佈局中需要有三部分,一個是左側選單的佈局,一個是右側選單的佈局,一個是內容佈局。左側選單居螢幕左邊緣對齊,右側選單居螢幕右邊緣對齊,然後內容佈局佔滿整個螢幕,並壓在了左側選單和右側選單的上面。當用戶手指向右滑動時,將右側選單隱藏,左側選單顯示,然後通過偏移內容佈局的位置,就可以讓左側選單展現出來。同樣的道理,當用戶手指向左滑動時,將左側選單隱藏,右側選單顯示,也是通過偏移內容佈局的位置,就可以讓右側選單展現出來。原理示意圖所下所示:


介紹完了原理,我們就開始動手實現吧。新建一個Android專案,專案名就叫做BidirSlidingLayout。然後新建我們最主要的BidirSlidingLayout類,這個類就是實現雙向滑動選單功能的核心類,程式碼如下所示:


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();
		}
	}
}
以上程式碼註釋已經寫得非常詳細,我再來簡單解釋一下。首先在onLayout()方法中分別獲取到左側選單、右側選單和內容佈局的引數,並將內容佈局的寬度重定義成螢幕的寬度,這樣就可以保證內容佈局既能覆蓋住下面的選單佈局,還能偏移出螢幕。然後在onTouch()方法中監聽觸屏事件,以判斷使用者手勢的意圖。這裡事先定義好了幾種滑動狀態,DO_NOTHING表示沒有進行任何滑動,SHOW_LEFT_MENU表示使用者想要滑出左側選單,SHOW_RIGHT_MENU表示使用者想要滑出右側選單,HIDE_LEFT_MENU表示使用者想要隱藏左側選單,HIDE_RIGHT_MENU表示使用者想要隱藏右側選單,在checkSlideState()方法中判斷出使用者到底是想進行哪一種滑動操作,並給slideState變數賦值,然後根據slideState的值決定如何偏移內容佈局。接著當用戶手指離開螢幕時,會根據當前的滑動距離,決定後續的滾動方向,通過LeftMenuScrollTask和RightMenuScrollTask來完成完整的滑動過程。
然後我們看一下setScrollEvent方法,這個方法接收一個View作為引數,然後為這個View綁定了一個touch事件。這是什麼意思呢?讓我們來想象一個場景,如果內容佈局是一個LinearLayout,我可以通過監聽LinearLayout上的touch事件來控制它的偏移。但是如果內容佈局的LinearLayout裡面加入了一個ListView,而這個ListView又充滿了整個LinearLayout,這個時候LinearLayout將不可能再被touch到了,這個時候我們就需要將touch事件註冊到ListView上。setScrollEvent方法也就是提供了一個註冊介面,touch事件將會註冊到傳入的View上。

接下來開啟或新建activity_main.xml檔案,加入如下程式碼:

<com.example.bidirslidinglayout.BidirSlidingLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/bidir_sliding_layout"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <RelativeLayout
        android:id="@+id/left_menu"
        android:layout_width="270dip"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:background="#00ccff"
        android:visibility="invisible" >

        <TextView
            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" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/right_menu"
        android:layout_width="270dip"
        android:layout_height="fill_parent"
        android:layout_alignParentRight="true"
        android:background="#00ffcc"
        android:visibility="invisible" >

        <TextView
            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" />
    </RelativeLayout>

    <LinearLayout
        android:id="@+id/content"
        android:layout_width="320dip"
        android:layout_height="fill_parent"
        android:background="#e9e9e9" >

        <ListView
            android:id="@+id/contentList"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scrollbars="none"
            android:cacheColorHint="#00000000" >
        </ListView>
    </LinearLayout>

</com.example.bidirslidinglayout.BidirSlidingLayout>
可以看到,我們使用了自定義的BidirSlidingLayout作為根佈局,然後依次加入了三個子佈局分別作為左側選單、右側選單和內容的佈局。左側選單和右側選單中都只是簡單地放入了一個TextView用於顯示一段文字,內容佈局中放入了一個ListView。注意要讓左側選單和父佈局左邊緣對齊,右側選單和父佈局右邊緣對齊。最後開啟或者建立MainActivity作為程式的主Activity,程式碼如下所示:
public class MainActivity extends Activity {

	/**
	 * 雙向滑動選單佈局
	 */
	private BidirSlidingLayout bidirSldingLayout;

	/**
	 * 在內容佈局上顯示的ListView
	 */
	private ListView contentList;

	/**
	 * ListView的介面卡
	 */
	private ArrayAdapter<String> contentListAdapter;

	/**
	 * 用於填充contentListAdapter的資料來源。
	 */
	private String[] contentItems = { "Content Item 1", "Content Item 2", "Content Item 3",
			"Content Item 4", "Content Item 5", "Content Item 6", "Content Item 7",
			"Content Item 8", "Content Item 9", "Content Item 10", "Content Item 11",
			"Content Item 12", "Content Item 13", "Content Item 14", "Content Item 15",
			"Content Item 16" };

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		bidirSldingLayout = (BidirSlidingLayout) findViewById(R.id.bidir_sliding_layout);
		contentList = (ListView) findViewById(R.id.contentList);
		contentListAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,
				contentItems);
		contentList.setAdapter(contentListAdapter);
		bidirSldingLayout.setScrollEvent(contentList);
	}

}
這裡我們給ListView填充了幾條資料,又通過findViewById()方法獲取到了BidirSlidingLayout物件,然後呼叫它的setScrollEvent()方法,將ListView進行繫結,這樣就可以通過左右滑動ListView來展示左側選單和右側選單了。好了,全部編碼工作都已完成,現在讓我們執行一下程式吧,效果如下圖所示:

以後我們在專案的任何地方都可以輕鬆加入雙向滑動選單功能,只需要以下兩步即可:

1. 在Acitivty的layout中引入我們自定義的BidirSlidingLayout佈局,並且給這個佈局要加入三個直接子元素。

2. 在Activity中通過setScrollEvent方法,給一個View註冊touch事件。

相關推薦

Android雙向滑動選單特效

在一個Activity的佈局中需要有三部分,一個是左側選單的佈局,一個是右側選單的佈局,一個是內容佈局。左側選單居螢幕左邊緣對齊,右側選單居螢幕右邊緣對齊,然後內容佈局佔滿整個螢幕,並壓在了左側選單和右側選單的上面。當用戶手指向右滑動時,將右側選單隱藏,左側選單顯示,然後通

Android雙向滑動選單 實現雙向滑動特效

如果您認為本部落格不錯,讀後覺得有收穫,不妨打賞讚助我一下,讓我有動力繼續寫出高質量的部落格。 贈人玫瑰,手有餘香。分享技術,傳遞快樂。 有心課堂,傳遞的不僅僅是技術! QQ交流群:250468947 有心課堂會員,請加入VIP QQ交流

Android雙向滑動選單完全解析,教你如何一分鐘實現雙向滑動特效

記得在很早之前,我寫了一篇關於Android滑動選單的文章,其中有一個朋友在評論中留言,希望我可以幫他將這個滑動選單改成雙向滑動的方式。當時也沒想花太多時間,簡單修改了一下就發給了他,結果沒想到後來卻有一大批的朋友都來問我要這份雙向滑動選單的程式碼。由於這份程式碼寫得很不用心

Android滑動選單特效實現,仿人人客戶端側滑效果,史上最簡單的側滑實現

                人人客戶端有一個特效還是挺吸引人的,在主介面手指向右滑動,就可以將選單展示出來,而主介面會被隱藏大部分,但是仍有左側的一小部分同選單一起展示。據說人人客戶端的這個特效是從facebook客戶端模仿來的,至於facebook是不是又從其它地方模仿來的就不得而知了。好,今天我們就一

Android滑動選單特效實現,側滑選單實現

人人客戶端有一個特效還是挺吸引人的,在主介面手指向右滑動,就可以將選單展示出來,而主介面會被隱藏大部分,但是仍有左側的一小部分同選單一起展示。 據說人人客戶端的這個特效是從facebook客戶端模仿來的,至於facebook是不是又從其它地方模仿來的就不得而知了。好,今天我們就一起來實現這個效果,總之我

Android滑動選單特效實現,仿人人客戶端側滑效果

人人客戶端有一個特效還是挺吸引人的,在主介面手指向右滑動,就可以將選單展示出來,而主介面會被隱藏大部分,但是仍有左側的一小部分同選單一起展示。 據說人人客戶端的這個特效是從facebook客戶端模仿來的,至於facebook是不是又從其它地方模仿來的就不得而

Android 3D滑動選單完全解析,實現推拉門式的立體特效

轉載地址:http://blog.csdn.net/guolin_blog/article/details/10471245 下面還是回到正題,首先來講一下這次的實現原理吧,其實傳統的滑動選單功能就是把選單部分放在了下面,主佈局放在了上面,然後根據手指滑動的距離來偏

android之SlideMenu雙向滑動

slidemenu 表示 sed getx menus 頁面 asynctask event att 開始動手之前先來講一下實現原理,在一個Activity的布局中需要有三部分,一個是左側菜單的布局,一個是右側菜單的布局,一個是內容布局。左側菜單居屏幕左邊緣對齊,右側菜

Android Studio 使用ViewPager + Fragment實現滑動選單Tab效果 --簡易版

描述:         之前有做過一個記賬本APP,拿來練手的,做的很簡單,是用Eclipse開發的;         最近想把這個APP重新完善一下,添加了一些新的功能,並選用Android Studio來開發;         APP已經完善了一部分,現在就想把已經做好的功能整理一下,記錄下來。 效果圖

android 使用Scroller實現美團懸浮框,網易左右滑動選單效果

Scroller類其實就是對View的scrollTo()以及ScrollBy()方法封裝以及優化,比如我們熟悉的網易客戶端主介面的側滑選單,如果要實現當滑動超過一半的時候回彈的效果,而不是一下子就回到了最終點,view的scrollTo(),scrollBy()方法是沒

Android雙向表格可滑動雙向適配

專案需要做這種可滑動的表格,其實我覺得這些線圖更好,沒辦法,領導要求; 我的技術水平還沒法讓我徒手寫一個這樣複雜的控制元件,我在網上找了一個(原文找不到了,這裡就不貼連結了),但是不太完美:第一,表頭只有一個TextView,不能顯示那種分割的表頭;第二:資料只能縱向適配,

Android-自定義滑動選單(抽屜效果)

在Andoird使用Android自帶的那些元件,像SlidingDrawer和DrawerLayout都是抽屜效果的選單,但是在專案很多要實現的功能都收到Android這些自帶元件的限制,導致很難完成專案的需求,自定義的元件,各方面都在自己的控制之下,從而根據

Android-自定義類似excel表格,雙向滑動的ListView

效果圖: 主要程式碼: /** * Created by monty on 2017/8/31. */ public class PanelListLayout extends FrameLayout { private static

Android 雙向左右滑動側邊欄效果

轉自:http://blog.csdn.net/ljx19900116 記得在很早之前,我寫了一篇關於Android滑動選單的文章,其中有一個朋友在評論中留言,希望我可以幫他將這個滑動選單改成雙向滑動的方式。當時也沒想花太多時間,簡單修改了一下就發給了他,結果沒

Android雙向滑動的範圍選擇器(雙向滑動可選擇範圍的SeekBar)實現

一、概述 之前公司app裡面有個功能是一個可以雙向滑動的範圍選擇器,我在網上百度過一些實現方法,感覺各有利弊吧,但是都不太適合我們的需求。所以站在巨人的肩膀上,通過自定義View實現了一個可以適用於絕大多數情況的範圍選擇器來供大家使用。首先,看效果圖:

Android雙向側邊滑動實現

Android雙向滑動實現 記得在很早之前,我寫了一篇關於Android滑動選單的文章,其中有一個朋友在評論中留言,希望我可以幫他將這個滑動選單改成雙向滑動的方式。當時也沒想花太多時間,簡單修改了一下就發給了他,結果沒想到後來卻有一大批的朋友都來問我要這份雙

android 滑動選單SlidingMenu的實現

首先我們看下面檢視:        這種效果大家都不陌生,網上好多都說是仿人人網的,估計人家牛逼出來的早吧,我也參考了一一些例子,實現起來有三種方法,我下面簡單介紹下: 方法一:其實就是對GestureDetector手勢的應用及佈局檔案的設計. 佈局檔案main.xml

Android RadioGroup+ViewPager+ActionBar實現仿微信6.0介面(底部滑動選單欄+導航欄)

轉載請註明原文地址:http://blog.csdn.net/anyfive/article/details/41296341 本文主要使用RadioGroup+ViewPager來實現滑動介面,使用ActionBar來實現頂部選單欄。先上圖(使用GifCam錄製)。

Android ListView滑動刪除及響應事件詳解

源代碼下載 引用 example imp cor toast don float 發現 目標:實現類似QQ,微信的消息列表滑動刪除 具體操作: 1. 主頁面布局 首先在布局文件(本例是activity_main.xml)中引入ListView控件,並指定id(如下代

Android雙向seekbar(帶刻度)

track progress 效果 ide text break dsm blog data- ※效果 ※使用方法 seekBarPressures.setOnSeekBarChangeListener(new SeekBarPres