您的位置:首页 > 移动开发 > Android开发

Android侧滑菜单完整详细示例(基础版)

2014-09-13 23:01 232 查看
MainActivity如下:

package cc.cd;

import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.app.Activity;
import android.content.Context;
/**
* Demo描述:
* 侧滑菜单SlidingMenu的简单示例
* 即在一个Activity中加入SlidingMenu
*
* 示例说明:
* 示例中一共两个界面menu和content.当进入应用时显示content界面.
* 若手指滑向屏幕右侧则显示menu和隐藏content.反之,同理.
* menu的显示和隐藏主要是修改menu的LayoutParams中的leftMargin从而达到效果.
*
* 步骤整理:
* 1 初始化时,通过设置menu的LayoutParams中的leftMargin使其完全隐藏.
* 2 为content设置Touch监听.
* 3 在move的过程中不断修改menu的LayoutParams中的leftMargin使其按照
*   手势的滑动而显示或者隐藏
* 4 在up时通过异步任务AsyncTask修改menu的LayoutParams中的leftMargin
*   从而实现menu的完全显示或者完全隐藏.
*
* 以上套路还是挺常见的.
*
* 参考资料:
* 1 http://blog.csdn.net/guolin_blog/article/details/8714621 * 2 http://blog.csdn.net/hudashi/article/details/7352157 *   Thank you very much
*
* 备注说明:
* 1 示例中使用的图片亦来自参考资料1
* 2 为简化逻辑,示例中的两个界面均为截图而不是实际View界面
*/
public class MainActivity extends Activity {
private int screenWidth;
private View contentView;
private View menuView;
private float xDown;
private float xMove;
private float xUp;
//当完全显示menu时content的宽度最小值
private int contentViewMinWidth = 80;
//menu是否可见的标志位,该值在滑动过程中无效.
//只有在滑动结束后,完全显示或隐藏menu时才会更改此值
private boolean isMenuVisible=false;
private int menuParamsMaxLeftMargin=0;
private int menuParamsMinLeftMargin=0;
//速度追踪
private VelocityTracker mVelocityTracker;
//阈值
public static final int VELOCITY_THRESHOLD=200;
//TAG
private final static String TAG="MainActivity";
//menu的布局LayoutParams
private LinearLayout.LayoutParams menuLayoutParams;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
}

private void init(){
//获取屏幕宽度
WindowManager windowManager=(WindowManager) getSystemService(Context.WINDOW_SERVICE);
screenWidth=windowManager.getDefaultDisplay().getWidth();

//初始化contentView
contentView=findViewById(R.id.contentLinearLayout);
//将contentView的宽度设置为屏幕的宽度
contentView.getLayoutParams().width=screenWidth;
//设置Touch监听
contentView.setOnTouchListener(new TouchListenerImpl());

//初始化menuView
menuView=findViewById(R.id.menuLinearLayout);
menuLayoutParams=(LinearLayout.LayoutParams) menuView.getLayoutParams();
//设置menuView的宽度.
//设置其宽度为屏幕宽度减去contentView的最小宽度
menuLayoutParams.width=screenWidth-contentViewMinWidth;
//初始化时完全隐藏了menuView.
menuParamsMinLeftMargin=-menuLayoutParams.width;
menuLayoutParams.leftMargin=menuParamsMinLeftMargin;
}

/**
* 关于ACTION_MOVE中distanceX的细节说明.
* 在一次滑动过程中(从手指按下到手指抬起)distanceX的值是持续变大或者变小的.
* 因为int distanceX=(int) (xMove-xDown);
* 这个xDown是按下时的坐标值,在Moving的过程中计算distanceX时一直采用
* 的是xDown减去每时刻的xMove.即在滑动过程中xDown一直不变而xMove是不断
* 在变化的.
* 代码说明:
* if (isMenuVisible) {
*     menuLayoutParams.leftMargin=distanceX;
* } else {
*    menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
* }
* 在最开始时,menu是隐藏的.手指按下,滑向屏幕的右边.调用:
* menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
* 所以这个menuLayoutParams.leftMargin是不断在变大的直到0为止(注意越界判断),此时
* menuView完全显示.这时手指再按下,滑向屏幕的左边.调用:
* menuLayoutParams.leftMargin=distanceX;
* distanceX这个负数一直在减小,它就是menuLayoutParams.leftMargin的值直至
* menuView完全隐藏为止,此时它的值为menuParamsMinLeftMargin(注意越界判断).
*
* 该问题不难,但在此备注一下以防以后反应不过来.
*/
private class TouchListenerImpl implements OnTouchListener{
@Override
public boolean onTouch(View v, MotionEvent event) {
//开始速度追踪
startVelocityTracker(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
xDown=event.getRawX();
break;
case MotionEvent.ACTION_MOVE:
xMove=event.getRawX();
int distanceX=(int) (xMove-xDown);
Log.i(TAG, "xDown="+xDown+",xMove="+xMove+",distanceX="+distanceX);
if (isMenuVisible) {
menuLayoutParams.leftMargin=distanceX;
} else {
menuLayoutParams.leftMargin=menuParamsMinLeftMargin+distanceX;
}

//处理越界的情况
if(menuLayoutParams.leftMargin<menuParamsMinLeftMargin){
menuLayoutParams.leftMargin=menuParamsMinLeftMargin;
}
if(menuLayoutParams.leftMargin>menuParamsMaxLeftMargin){
menuLayoutParams.leftMargin=menuParamsMaxLeftMargin;
}
//设置menuView的LayoutParams
menuView.setLayoutParams(menuLayoutParams);
break;
case MotionEvent.ACTION_UP:
xUp=event.getRawX();

//判断手势意图想显示menu
if (wantToShowMenu()) {
//判断是否显示menu
if (shouldScrollToMenu()) {
scrollToMenu();
} else {
scrollToContent();
}
}

//判断手势意图想显示content
if (wantToShowContent()) {
//判断是否显示content
if (shouldScrollToContent()) {
scrollToContent();
} else {
scrollToMenu();
}
}
//终止速度追踪
stopVelocityTracker();
break;
default:
break;
}
return true;
}

}

/**
* 判断当前手势是否想显示菜单Menu
* 判断条件:
* 1 抬起坐标大于按下坐标
* 2 menu本身不可见
*/
private boolean wantToShowMenu(){
return ((xUp-xDown>0)&&(!isMenuVisible));
}

/**
* 判断是否应该将menu完整显示出来
* 判断条件:
* 滑动距离大于屏幕的二分之一
* 或者滑动速度大于速度阈值VELOCITY_THRESHOLD
*/
private boolean shouldScrollToMenu(){
return ((xUp-xDown>screenWidth/2)||(getScrollVelocity()>VELOCITY_THRESHOLD));
}

/**
* 将屏幕滚动到menu.即将menu完整显示.
* 按照30的步调不断修改修改menu的LayoutParams中的leftMargin
*/
private void scrollToMenu(){
new ScrollAsyncTask().execute(30);
}

/**
* 判断当前手势是否想显示菜单Content
* 判断条件:
* 1 抬起坐标小于按下坐标
* 2 menu本身可见
*/
private boolean wantToShowContent(){
return ((xUp-xDown<0)&&(isMenuVisible));
}

/**
* 判断是否应该将content完整显示出来
* 判断条件:
* xDown-xUp+contentViewMinWidth大于屏幕的二分之一
* 或者滑动速度大于速度阈值VELOCITY_THRESHOLD
*/
private boolean shouldScrollToContent(){
return ((xDown-xUp+contentViewMinWidth>screenWidth/2)||(getScrollVelocity()>VELOCITY_THRESHOLD));
}

/**
* 将屏幕滚动到content.即将content完整显示
* 按照-30的步调不断修改修改menu的LayoutParams中的leftMargin
*/
private void scrollToContent(){
new ScrollAsyncTask().execute(-30);
}

/**
* 开始速度追踪
*/
private void startVelocityTracker(MotionEvent event){
if (mVelocityTracker==null) {
mVelocityTracker=VelocityTracker.obtain();
}
mVelocityTracker.addMovement(event);
}

/**
* 获取在content上X方向的手指滑动速度
*/
private int getScrollVelocity(){
//设置VelocityTracker单位.1000表示1秒时间内运动的像素
mVelocityTracker.computeCurrentVelocity(1000);
//获取在1秒内X方向所滑动像素值
int xVelocity=(int) mVelocityTracker.getXVelocity();
return Math.abs(xVelocity);
}

/**
* 终止速度追踪
*/
private void stopVelocityTracker(){
if (mVelocityTracker!=null) {
mVelocityTracker.recycle();
mVelocityTracker=null;
}
}

/**
* 利用异步任务不断修改menu的LayoutParams中的leftMargin从而达到一个
* 视图移动的效果
*/
private class ScrollAsyncTask extends AsyncTask<Integer, Integer, Integer>{
@Override
protected Integer doInBackground(Integer... speed) {
int leftMargin=menuLayoutParams.leftMargin;
while(true){
//每次变化的speed
leftMargin=leftMargin+speed[0];
//若越界,则处理越界且跳出循环
if (leftMargin>menuParamsMaxLeftMargin) {
leftMargin=menuParamsMaxLeftMargin;
break;
}
//若越界,则处理越界且跳出循环
if (leftMargin<menuParamsMinLeftMargin) {
leftMargin=menuParamsMinLeftMargin;
break;
}
//通知进度更新
publishProgress(leftMargin);
//线程睡眠25毫秒,便于体现滚动效果
try {
Thread.sleep(25);
} catch (Exception e) {
}
}

//依据滑动的速度设置标志位isMenuVisible
if (speed[0]>0) {
isMenuVisible=true;
}else{
isMenuVisible=false;
}
return leftMargin;
}

@Override
protected void onProgressUpdate(Integer... leftMargin) {
super.onProgressUpdate(leftMargin);
menuLayoutParams.leftMargin=leftMargin[0];
menuView.setLayoutParams(menuLayoutParams);
}

@Override
protected void onPostExecute(Integer leftMargin) {
super.onPostExecute(leftMargin);
menuLayoutParams.leftMargin=leftMargin;
menuView.setLayoutParams(menuLayoutParams);
}
}

}


main.xml如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<LinearLayout
android:id="@+id/menuLinearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/menu"
android:orientation="vertical" />

<LinearLayout
android:id="@+id/contentLinearLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/content"
android:orientation="vertical" />

</LinearLayout>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: