Android侧滑菜单完整详细示例(基础版)
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>
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。