SlidingMenu简介:
menu.setMode(SlidingMenu.LEFT);//设置左滑菜单 menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);//设置滑动的屏幕范围,该设置为全屏区域都可以滑动 menu.setShadowDrawable(R.drawable.shadow);//设置阴影图片 menu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度 menu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度 menu.setBehindWidth(400);//设置SlidingMenu菜单的宽度 menu.setFadeDegree(0.35f);//SlidingMenu滑动时的渐变程度 menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);//使SlidingMenu附加在Activity上 menu.setMenu(R.layout.menu_layout);//设置menu的布局文件 menu.toggle();//动态判断自动关闭或开启SlidingMenu menu.showMenu();//显示SlidingMenu menu.showContent();//显示内容 menu.setOnOpenListener(onOpenListener);//监听slidingmenu打开
menu.OnClosedListener(OnClosedListener);//监听slidingmenu关闭时事件 menu.OnClosedListener(OnClosedListener);//监听slidingmenu关闭后事件
menu.setMode(SlidingMenu.LEFT_RIGHT);属性,然后设置右侧菜单的布局文件 menu.setSecondaryShadowDrawable(R.drawable.shadowright);//右侧菜单的阴影图片
setBehindContentView(R.layout.menu_frame); FragmentTransaction t = this.getSupportFragmentManager().beginTransaction(); leftMenuFragment = new MenuFragment(); t.replace(R.id.menu_frame, leftMenuFragment); t.commit();
if (savedInstanceState == null) {//== null的时候新建Fragment1 contentFragment = new Fragment1(); } else {//不等于null,直接get出来 //不等于null,找出之前保存的当前Activity显示的Fragment contentFragment = getSupportFragmentManager().getFragment(savedInstanceState, "contentFragment"); } //设置内容Fragment getSupportFragmentManager() .beginTransaction() .replace(R.id.content_frame, contentFragment) .commit();
getSupportFragmentManager().putFragment(outState, "contentFragment", contentFragment); 设置SlidingMenu属性 sm = getSlidingMenu(); //如果只显示左侧菜单就是用LEFT,右侧就RIGHT,左右都支持就LEFT_RIGHT sm.setMode(SlidingMenu.LEFT_RIGHT);//设置菜单滑动模式,菜单是出现在左侧还是右侧,还是左右两侧都有 sm.setShadowDrawable(R.drawable.shadow);//设置阴影的图片资源 sm.setShadowWidthRes(R.dimen.shadow_width);//设置阴影图片的宽度 //sm.setBehindWidth(200);//设置菜单的宽 sm.setBehindOffsetRes(R.dimen.slidingmenu_offset);//SlidingMenu划出时主页面显示的剩余宽度 sm.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN);//设置滑动的区域 支持右侧划出菜单: //SlidingMenu可以同时支持划出左右两侧的菜单,互不冲突,而且动画优美,体验良好。 sm.setSecondaryMenu(R.layout.menu_frame2);//设置右侧菜单 sm.setSecondaryShadowDrawable(R.drawable.shadowright);//设置右侧菜单阴影的图片资源 //右侧SlidingMenu的Fragment getSupportFragmentManager().beginTransaction().replace(R.id.menu_frame2, new SampleListFragment()).commit(); 设置ActionBar可以被点击: getSupportActionBar().setHomeButtonEnabled(true);//actionbar主按键可以被点击 getSupportActionBar().setDisplayHomeAsUpEnabled(true);//显示向左的图标 setSlidingActionBarEnabled(false);//左右两侧slidingmenu的fragment是否显示标题栏 切换主页面显示的Fragment: public void switchContent(Fragment f) { //给内容Fragment赋值,并在onSaveInstanceState时保存这个Fragment contentFragment = f; FragmentTransaction t = getSupportFragmentManager().beginTransaction(); t.replace(R.id.content_frame, f); t.commit(); sm.showContent();
slidingMenu menu = new SlidingMenu(this);//直接new,而不是getSlidingMenu menu.setMode(SlidingMenu.LEFT); menu.setTouchModeAbove(SlidingMenu.TOUCHMODE_FULLSCREEN); menu.setShadowDrawable(R.drawable.shadow); menu.setShadowWidthRes(R.dimen.shadow_width); menu.setBehindOffsetRes(R.dimen.slidingmenu_offset); menu.setBehindWidth(400);//设置SlidingMenu菜单的宽度 menu.setFadeDegree(0.35f); menu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);//必须调用 menu.setMenu(R.layout.menu_layout_left);//就是普通的layout布局 menu.setBehindCanvasTransformer(mTransformer);
menu.setSecondaryMenu(R.layout.menu_layout_right); menu.setSecondaryShadowDrawable(R.drawable.shadowright);
mTransformer = new CanvasTransformer() { @Override public void transformCanvas(Canvas canvas, float percentOpen) { float scale = (float) (percentOpen*0.25 + 0.75); canvas.scale(scale, scale, canvas.getWidth()/2, canvas.getHeight()/2); } }; 然后将mTransformer对象设置给SlidingMenu即可,这个是缩放动画: [java] private void initSlidUpCanvasTransformer() { mTransformer = new CanvasTransformer() { @Override public void transformCanvas(Canvas canvas, float percentOpen) { canvas.translate(0, canvas.getHeight()*(1-interp.getInterpolation(percentOpen))); } }; } private static Interpolator interp = new Interpolator() { @Override public float getInterpolation(float t) { t -= 1.0f; return t * t * t + 1.0f; } }; 拉伸动画: [java] mTransformer = new CanvasTransformer() { @Override public void transformCanvas(Canvas canvas, float percentOpen) { canvas.scale(percentOpen, 1, 0, 0); } });
<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" tools:context=".MyActivity"> <LinearLayout android:id="@+id/menu" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ff00ff00" android:orientation="vertical"></LinearLayout> <LinearLayout android:id="@+id/content" android:layout_width="fill_parent" android:layout_height="fill_parent" android:background="#ffff0000" android:orientation="vertical"></LinearLayout> </LinearLayout>
这个布局文件的最外层布局是一个LinearLayout,排列方向是水平方向排列。这个LinearLayout下面嵌套了两个子LinearLayout,分别就是菜单的布局和内容的布局。这里为了要让布局尽量简单,菜单布局和内容布局里面没有加入任何控件。
public class MyActivity extends Activity implements View.OnTouchListener { /** * 手指每单位时间滑动200个像素 */ private static final int SPEED = 200; /** * 屏幕宽度 */ private int mScreenWidth; /** * menu的layout */ private LinearLayout mMenuLayout; /** * content的layout */ private LinearLayout mContentLayout; /** * menu的layout的Paramters */ private LinearLayout.LayoutParams mMenuParams; /** * menu完全显示的时候给content的宽度值 */ private int mMenuPadding = 80; /** * menu最多滑到左边缘,值由menu布局的宽度决定,marginLeft到达此值之后,不能在减少 */ private int mLeftEdge; /** * 测速度的对象 */ private VelocityTracker mVelocityTracker; /** * 手指按下的X坐标 */ private float mXDown; /** * 手指移动时候的X坐标 */ private float mXMove; /** * 手指抬起的X坐标 */ private float mXUp; /** * menu是否再显示 */ private boolean mIsMenuVisible = false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_my); initViewsAndValues(); mContentLayout.setOnTouchListener(this); } /** * 初始化menu和content并且设置他们的位置 */ private void initViewsAndValues() { //得到windowManager WindowManager window = (WindowManager) getSystemService(Context.WINDOW_SERVICE); //得到屏幕宽度 mScreenWidth = window.getDefaultDisplay().getWidth(); //找到控件 mMenuLayout = (LinearLayout) findViewById(R.id.menu); mContentLayout = (LinearLayout) findViewById(R.id.content); //得到menu的paramter mMenuParams = (LinearLayout.LayoutParams) mMenuLayout.getLayoutParams(); //将menu的宽度设置为屏幕宽度减去mMenuPading mMenuParams.width = mScreenWidth - mMenuPadding; //左边缘的值复制为menu宽度的负数,这样的话就可以将menu隐藏 mLeftEdge = -mMenuParams.width; //将margin设置为mLeftEdge mMenuParams.leftMargin = mLeftEdge; //将content显示再屏幕上 mContentLayout.getLayoutParams().width = mScreenWidth; } @Override public boolean onTouch(View view, MotionEvent event) { createVelocityTracker(event); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //记录X坐标 mXDown = event.getRawX(); break; case MotionEvent.ACTION_MOVE: mXMove = event.getRawX(); int distanceX = (int) (mXMove - mXDown); if (mIsMenuVisible) { mMenuParams.leftMargin = distanceX; } else { mMenuParams.leftMargin = mLeftEdge + distanceX; } if (mMenuParams.leftMargin < mLeftEdge) { //因为leftEdge是负数,就是menu已经隐藏完毕了,不能再往左隐藏了 mMenuParams.leftMargin = mLeftEdge; } else if (mMenuParams.leftMargin > 0) { //menu显示完全了,不能再往右移动了 mMenuParams.leftMargin = 0; } mMenuLayout.setLayoutParams(mMenuParams); break; case MotionEvent.ACTION_UP: mXUp = event.getRawX(); if (wantToShowMenu()) { if (shouldScrollToMenu()) { scrollToMenu(); } else { //条件不满足,把menu继续隐藏掉 scrollToContent(); } } else if (wantToShowContent()) { if (shouldScrollToContent()) { scrollToContent(); } else { scrollToMenu(); } } break; } recycleVelocityTracker(); return true; } /** * 创建VelocityTracker对象,针对于content的界面的滑动事件 * * @param event */ private void createVelocityTracker(MotionEvent event) { if (null == mVelocityTracker) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(event); } /** * 判断手势是不是想要显示Content && menu处于显示状态 * * @return */ private boolean wantToShowContent() { return mXUp - mXDown < 0 && mIsMenuVisible; } /** * 是不是要显示menu && menu处于隐藏状态 * * @return */ private boolean wantToShowMenu() { return mXUp - mXDown > 0 && !mIsMenuVisible; } /** * 是否应该滑动出menu * * @return */ private boolean shouldScrollToMenu() { return mXUp - mXDown > mScreenWidth / 2 || getScrollVelocity() > SPEED; } /** * 是否应该让content全部显示出来 * * @return */ private boolean shouldScrollToContent() { return mXDown - mXUp < mScreenWidth / 2 || getScrollVelocity() > SPEED; } /** * 显示出menu */ private void scrollToMenu() { new ScrollAsyncTask().execute(30); } /** * 隐藏掉menu */ private void scrollToContent() { new ScrollAsyncTask().execute(-30); } /** * 得到手指滑动速度,每秒移动多少单位像素 * * @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 ScrollAsyncTask extends AsyncTask<Integer, Integer, Integer> { @Override protected Integer doInBackground(Integer[] speed) { //得到当前margin int leftMargin = mMenuParams.leftMargin; //不断更改margin的值 while (true) { leftMargin = leftMargin + speed[0]; if (leftMargin > 0) { leftMargin = 0; break; } if (leftMargin < mLeftEdge) { leftMargin = mLeftEdge; break; } publishProgress(leftMargin); sleep(); } if (speed[0] > 0) { mIsMenuVisible = true; } else { mIsMenuVisible = false; } return leftMargin; } @Override protected void onPostExecute(Integer integer) { mMenuParams.leftMargin = integer; mMenuLayout.setLayoutParams(mMenuParams); } @Override protected void onProgressUpdate(Integer... values) { mMenuParams.leftMargin = values[0]; mMenuLayout.setLayoutParams(mMenuParams); } } /** * sleep线程睡眠一下 */ private void sleep() { try { Thread.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } } }
首先初始化的时候调用initViewsAndValues方法,在这里面将内容布局的宽度设定为屏幕的宽度,菜单布局的宽度设定为屏幕的宽度减去menuPadding值,这样可以保证在菜单布局展示的时候,仍有一部分内容布局可以看到。如果不在初始化的时候重定义两个布局宽度,就会按照layout文件里面声明的一样,两个布局都是fill_parent,这样就无法实现滑动菜单的效果了。然后将菜单布局的左偏移量设置为负的菜单布局的宽度,这样菜单布局就会被完全隐藏,只有内容布局会显示在界面上。
之后给内容布局注册监听事件,这样当手指在内容布局上滑动的时候就会触发onTouch事件。在onTouch事件里面,根据手指滑动的距离会改变菜单布局的左偏移量,从而控制菜单布局的显示和隐藏。当手指离开屏幕的时候,会判断应该滑动到菜单布局还是内容布局,判断依据是根据手指滑动的距离或者滑动的速度,细节可以看代码中的注释。