編輯:Android開發教程
之前我向大家介紹了史上最簡單的滑動菜單的實現方式,相信大家都還記得。如果忘記了其中的實現原理 或者還沒看過的朋友,請先去看一遍之前的文章 Android滑動菜單特效實現,仿人人客戶端側滑效果,史上 最簡單的側滑實現 ,因為我們今天要實現的滑動菜單框架也是基於同樣的原理的。
之前的文章中在 最後也提到了,如果是你的應用程序中有很多個Activity都需要加入滑動菜單的功能,那麼每個Activity都 要寫上百行的代碼才能實現效果,再簡單的滑動菜單實現方案也沒用。因此我們今天要實現一個滑動菜單的 框架,然後在任何Activity中都可以一分鐘引入滑動菜單功能。
首先還是講一下實現原理。說是滑動 菜單的框架,其實說白了也很簡單,就是我們自定義一個布局,在這個自定義布局中實現好滑動菜單的功能 ,然後只要在Activity的布局文件裡面引入我們自定義的布局,這個Activity就擁有了滑動菜單的功能了。 原理講完了,是不是很簡單?下面我們來動手實現吧。
在Eclipse中新建一個Android項目,項目名就 叫做RenRenSlidingLayout。
新建一個類,名叫SlidingLayout,這個類是繼承自LinearLayout的,並 且實現了OnTouchListener接口,具體代碼如下:
public class SlidingLayout extends LinearLayout implements OnTouchListener { /** * 滾動顯示和隱藏左側布局時,手指滑動需要達到的速度。 */ 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 SlidingLayout(Context context, AttributeSet attrs) { super(context, attrs); WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); screenWidth = wm.getDefaultDisplay().getWidth(); } /** * 綁定監聽側滑事件的View,即在綁定的View進行滑動才可以顯示和隱藏左側布局。 * * @param bindView * 需要綁定的View對象。 */ public void setScrollEvent(View bindView) { mBindView = bindView; mBindView.setOnTouchListener(this); } /** * 將屏幕滾動到左側布局界面,滾動速度設定為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); } } @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(); } /** * 判斷當前手勢的意圖是不是想顯示右側布局。如果手指移動的距離是負數,且當前左側布局是可見的,則認為當前手勢是想要顯示右側布局。 * * @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<Integer, Integer, Integer> { @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毫秒,這樣肉眼才能夠看到滾動動畫。 sleep(20); } 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); } } /** * 使當前線程睡眠指定的毫秒數。 * * @param millis * 指定當前線程睡眠多久,以毫秒為單位 */ private void sleep(long millis) { try { Thread.sleep(millis); } catch (InterruptedException e) { e.printStackTrace(); } } }
游俠用的是Android系統的手機,今天早上要刪個文件,結果手快,點錯了……就這樣糾結著到了公司。其實筆記本電腦硬盤裡面有一些數據、照片恢復軟
android如何使用BroadcastReceiver後台實現來電通話記錄的監聽並存取到sqllite數據庫通過Contentprovilder實現接口Broadcas
1.1.1 VSync信號的處理經過上一小節的分析,現在我們已經明白了系統是如何通過硬件設備或者軟件模擬來產生VSync信號的,也明白了它的流轉過程。VSync最終會被E
相信每個項目都會有用戶反饋建議等功能,這個實現的方法很多,下面是我實現的方法,供大家交流。首 先看具體界面,三個字段。名字,郵箱為選填,可以為空,建議不能為空。如有需要可