編輯: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系統的手機,今天早上要刪個文件,結果手快,點錯了……就這樣糾結著到了公司。其實筆記本電腦硬盤裡面有一些數據、照片恢復軟
android如何使用BroadcastReceiver後台實現來電通話記錄的監聽
android如何使用BroadcastReceiver後台實現來電通話記錄的監聽並存取到sqllite數據庫通過Contentprovilder實現接口Broadcas
Android GUI系統之SurfaceFlinger(13) VSync信號的處理
1.1.1 VSync信號的處理經過上一小節的分析,現在我們已經明白了系統是如何通過硬件設備或者軟件模擬來產生VSync信號的,也明白了它的流轉過程。VSync最終會被E
android+json+php+mysql實現用戶反饋功能
相信每個項目都會有用戶反饋建議等功能,這個實現的方法很多,下面是我實現的方法,供大家交流。首 先看具體界面,三個字段。名字,郵箱為選填,可以為空,建議不能為空。如有需要可