改进Android SlidingMenu实现QQ样式边侧滑抽屉技术
我在之前写的附录文章1中介绍了Android SlidingMenu的使用,Android SlidingMenu作为第三方的边侧滑技术(也被称之为‘抽屉’),在过去的四五年中被广泛使用,但随着后来谷歌官方在Android中以官方支持的形式相继推出了DrawerLayout(附录文章2)和NavigationView(附录文章3)作为抽屉技术的标准实现后,SlidingMenu逐渐在边缘化。在以后的开发中,如果要实现抽屉的边侧滑效果,DrawerLayout和NavigationView是首选。
但是,SlidingMenu在过去毕竟盛行过,一些老旧的APP中作为继承和惯性,还在使用SlidingMenu。如果瞬间说抛弃SlidingMenu,代码切换的时间和工作量有些多。
这篇文章要写的是,如何在不改变SlidingMenu的主题结构情况下,对SlidingMenu进行代码改造和增强,实现类似QQ的边侧滑抽屉效果。
在一段时间,QQ手机客户端的边侧滑(抽屉),和SlidingMenu一样,左滑右滑切换出隐藏的菜单和功能界面,但是需要特别注意的是:QQ的边侧滑与普通的SlidingMenu边侧滑效果不同,QQ的边侧滑、当抽屉打开时候,是一种逐渐缩进缩出的放大/缩小进出效果,同时还带有一定的电影中屏幕逐渐亮/暗效果。本文将在SlidingMenu的基础上改进SlidingMenu代码,作为兼容和增强,实现QQ样式的边侧滑抽屉效果。
写一个布局文件,主布局,通常在实际的开发中,这就是要盛放主体内容的界面activity_main.xml,我简单写一个布局,主要用于测试:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#FF6F00" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textColor="@android:color/white" android:textSize="80sp" android:gravity="center" android:text="Zhang Phil" /> </RelativeLayout>
MainActivity.java:
package zhangphil.slidingmenu; import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu; import com.jeremyfeinstein.slidingmenu.lib.SlidingMenu.CanvasTransformer; import com.nineoldandroids.view.ViewHelper; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PorterDuff.Mode; import android.os.Bundle; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentTransaction; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; public class MainActivity extends FragmentActivity { private SlidingMenu mSlidingMenu; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSlidingMenu = new SlidingMenu(this); mSlidingMenu.setMode(SlidingMenu.LEFT); mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN); mSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT); // 左边 mSlidingMenu.setMenu(R.layout.left_menu); Fragment leftFragment = TestFragment.newInstance(); FragmentManager fm = getSupportFragmentManager(); FragmentTransaction ft = fm.beginTransaction(); ft.replace(R.id.left, leftFragment); ft.commit(); // 核心关键处,这就是对SlideMenu的增强和改进。 addQQSlideStyleSlide(); } // 为SlideMenu添加QQ样式的边侧滑 private void addQQSlideStyleSlide() { CanvasTransformer mTransformer = new CanvasTransformer() { @Override public void transformCanvas(Canvas canvas, float percentOpen) { View mainContent = mSlidingMenu.getContent(); View leftContent = mSlidingMenu.getMenu(); animateView(mainContent, leftContent, percentOpen); } }; // 设置滑动菜单视图的宽度 mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset); mSlidingMenu.setFadeEnabled(false); mSlidingMenu.setBehindCanvasTransformer(mTransformer); } private void animateView(View main, View left, float percent) { float f1 = 1 - percent * 0.3f; ViewHelper.setScaleX(main, f1); ViewHelper.setScaleY(main, f1); ViewHelper.setTranslationX(left, -left.getWidth() / 2.3f + left.getWidth() / 2.3f * percent); ViewHelper.setScaleX(left, 0.5f + 0.5f * percent); ViewHelper.setScaleY(left, 0.5f + 0.5f * percent); ViewHelper.setAlpha(left, percent); getWindow().getDecorView().getBackground().setColorFilter(evaluate(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER); } private Integer evaluate(float fraction, Object startValue, Integer endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | (int) ((startR + (int) (fraction * (endR - startR))) << 16) | (int) ((startG + (int) (fraction * (endG - startG))) << 8) | (int) ((startB + (int) (fraction * (endB - startB)))); } // // 仅仅用于生成测试的Fragment。 // 左边侧边滑打开的抽屉 // public static class TestFragment extends Fragment { public static Fragment newInstance() { Fragment fragment = new TestFragment(); return fragment; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // 仅仅显示一个TextView。 TextView tv = new TextView(getActivity()); tv.setTextColor(Color.WHITE); tv.setText("左边"); tv.setTextSize(60.0f); tv.setGravity(Gravity.CENTER); return tv; } } }
在自己的代码中如何使用SlideMenu本文不再详细介绍,我写的附录文章1有介绍。
如何对SlidingMenu进行改造以实现QQ样式的边侧滑抽屉技术,核心关键处就是我写在MainActivity.java里面的这部分连锁代码:
// 为SlideMenu添加QQ样式的边侧滑 private void addQQSlideStyleSlide() { CanvasTransformer mTransformer = new CanvasTransformer() { @Override public void transformCanvas(Canvas canvas, float percentOpen) { View mainContent = mSlidingMenu.getContent(); View leftContent = mSlidingMenu.getMenu(); animateView(mainContent, leftContent, percentOpen); } }; // 设置滑动菜单视图的宽度 mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset); mSlidingMenu.setFadeEnabled(false); mSlidingMenu.setBehindCanvasTransformer(mTransformer); } private void animateView(View main, View left, float percent) { float f1 = 1 - percent * 0.3f; ViewHelper.setScaleX(main, f1); ViewHelper.setScaleY(main, f1); ViewHelper.setTranslationX(left, -left.getWidth() / 2.3f + left.getWidth() / 2.3f * percent); ViewHelper.setScaleX(left, 0.5f + 0.5f * percent); ViewHelper.setScaleY(left, 0.5f + 0.5f * percent); ViewHelper.setAlpha(left, percent); getWindow().getDecorView().getBackground().setColorFilter(evaluate(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER); } private Integer evaluate(float fraction, Object startValue, Integer endValue) { int startInt = (Integer) startValue; int startA = (startInt >> 24) & 0xff; int startR = (startInt >> 16) & 0xff; int startG = (startInt >> 8) & 0xff; int startB = startInt & 0xff; int endInt = (Integer) endValue; int endA = (endInt >> 24) & 0xff; int endR = (endInt >> 16) & 0xff; int endG = (endInt >> 8) & 0xff; int endB = endInt & 0xff; return (int) ((startA + (int) (fraction * (endA - startA))) << 24) | (int) ((startR + (int) (fraction * (endR - startR))) << 16) | (int) ((startG + (int) (fraction * (endG - startG))) << 8) | (int) ((startB + (int) (fraction * (endB - startB)))); }
以下这部分代码,主要是为了实现背景逐渐点亮/逐渐暗淡的效果。
getWindow().getDecorView().getBackground().setColorFilter(evaluate(percent, Color.BLACK, Color.TRANSPARENT), Mode.SRC_OVER);
需要设置SlideMenu侧滑打开抽屉后的偏移距离,否则左边打开的窗口Menu将和普通的SlideMenu一样将完全展开铺满整个窗口,达不到QQ的那种各个窗口占据一般的效果。
Values/dimens.xml:
<dimen name="slidingmenu_offset">150dp</dimen>
测试的左边侧边滑打开的抽屉后,加载的Fragment使用的布局left.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#4FC3F7" android:orientation="vertical" > <FrameLayout android:id="@+id/left" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#F44336" > </FrameLayout> </LinearLayout>
代码写到这里,像QQ样式的边侧滑打开抽屉技术已经实现,但仍有一个细节未实现。QQ边侧滑打开抽屉时候,注意整个屏幕窗口内的背景图,好像是一个星空,要在自己的代码中也有这个背景,那么需要需要对我们自己的Activity略做修改,修改values目录下styles.xml文件主题,把要实现QQ边侧滑抽屉效果的Activity的Theme背景添加一张图片,图片需要事先放到drawable目录,我的这个例子,事先在drawable目录下放一张zhangphil_background.jpg图片:
<resources> <!-- Base application theme, dependent on API level. This theme is replaced by AppBaseTheme from res/values-vXX/styles.xml on newer devices. --> <style name="AppBaseTheme" parent="android:Theme.Light"> <!-- Theme customizations available in newer API levels can go in res/values-vXX/styles.xml, while customizations related to backward-compatibility can go here. --> </style> <!-- Application theme. --> <style name="AppTheme" parent="AppBaseTheme"> <!-- All customizations that are NOT specific to a particular API-level can go here. --> <!-- zhang phil增加的背景,作为QQ样式边侧滑打开抽屉时候整个窗体背景图--> <item name="android:windowBackground">@drawable/zhangphil_background</item> </style> </resources>
代码运行结果(初始状态 –> 然后,从左往右的边侧滑,抽屉逐渐打开):
附录文章:
1,《集成Android SlidingMenu(SlideMenu)》链接地址:http://blog.csdn.net/zhangphil/article/details/44078805
2,《基于Android官方DrawerLayout实现抽屉导航菜单》链接地址:http://blog.csdn.net/zhangphil/article/details/48710453
3,《Android Material Design: NavigationView抽屉导航菜单》链接地址:http://blog.csdn.net/zhangphil/article/details/48931221