在上一篇文章中,我们介绍了怎么使用DrawerLayout来实现一个简单的侧拉菜单(使用DrawerLayout实现侧拉菜单),也就是我们常说的抽屉效果,GitHub上类似效果的实现方式非常多,实现出来的效果也是非常的绚丽,但是万变不离其宗,Google提供给我们的DrawerLayout才是最基本的,我们今天就来介绍一下怎样通过DrawerLayout来实现QQ5.0的侧拉效果。先来看一张效果图:
好,这是一个我们即将要实现的效果图,关于这个效果,大部分都是很简单的,都是使用了我们在上一篇博客中介绍的DrawerLayout技术来做的,所以如果你还没读过上一篇博客,建议先去看一下DrawerLayout的使用方法(使用DrawerLayout实现侧拉菜单),了解了DrawerLayout的使用方法之后,那么要实现QQ5.0的侧拉效果就如同探囊取物一般。
好了,废话不多说,我们就先来看看怎么实现这样一个效果。
先来看看主布局文件:
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawerLayout" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/background" > <RelativeLayout android:id="@+id/mContent" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/mContent_iv" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_margin="0dp" android:background="@drawable/content_iv" android:padding="0dp" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:onClick="onClick" /> </RelativeLayout> <fragment android:id="@+id/left_fragment" android:name="com.lenve.qqdrawerlayout.LeftMenuFragment" android:layout_width="220dp" android:layout_height="match_parent" android:layout_gravity="left" android:tag="LEFTMENU" /> </android.support.v4.widget.DrawerLayout>
整个布局文件还是比较简单的,DrawerLayout中一共分为两大块,第一块就是主布局文件,先是是一张图片(这里我直接截了QQ页面的一张图),然后就是一个Button,注意这个Button的位置在整个页面的左上角,Button的背景设置为null,所以我们看不到Button,但是这个Button会响应我们的点击事件,第二部分是一个Fragment,这个Fragment我们看android:layout_gravity="left"属性就知道它是左边菜单栏的Fragment,在上一篇博客中,左边的菜单栏我们并没有使用Fragment,而是直接使用了布局文件,其实这里写成Fragment是比较好的,方便扩展,也方便管理。
好了,既然左边是一个Fragment,我们就来看看这个Fragment长什么样子?
先看看Fragment的布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ListView android:id="@+id/left_menu_lv" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="150dp" > </ListView> </RelativeLayout>
就一个ListView,够简单吧,再看看listview中item的布局文件:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/left_iv" android:layout_width="48dp" android:layout_height="48dp" android:layout_marginLeft="20dp" android:padding="12dp" android:src="@drawable/svip" /> <TextView android:id="@+id/left_tv" android:layout_width="wrap_content" android:layout_height="48dp" android:layout_marginLeft="20dp" android:layout_toRightOf="@id/left_iv" android:gravity="center" android:text="开通会员" android:textColor="@android:color/white" android:textSize="14sp" /> </RelativeLayout>
item的布局文件也是比较简单的,左边一个ImageView,右边一个TextView,我们再看看Fragment:
public class LeftMenuFragment extends Fragment { private List<LeftMenu> list = null; private ListView lv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); initData(); } /** * 初始化数据 */ private void initData() { list = new ArrayList<LeftMenu>(); list.add(new LeftMenu(R.drawable.svip, "开通会员")); list.add(new LeftMenu(R.drawable.qianbao, "QQ钱包")); list.add(new LeftMenu(R.drawable.zhuangban, "个性装扮")); list.add(new LeftMenu(R.drawable.shoucang, "我的收藏")); list.add(new LeftMenu(R.drawable.xiangce, "我的相册")); list.add(new LeftMenu(R.drawable.wenjian, "我的文件")); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.left_menu, container, false); lv = (ListView) view.findViewById(R.id.left_menu_lv); LeftMenuAdapter adapter = new LeftMenuAdapter(list); lv.setAdapter(adapter); return view; } }
Fragment整体也是比较简单的,在onCreate方法中拿到模拟数据,然后在onCreateView方法中给listview设置Adapter,在这里我们使用了一个JavaBean,这个JavaBean主要用来存储每一个item中的数据:
public class LeftMenu { private int imageView; private String text; public int getImageView() { return imageView; } public void setImageView(int imageView) { this.imageView = imageView; } public String getText() { return text; } public void setText(String text) { this.text = text; } public LeftMenu(int imageView, String text) { this.imageView = imageView; this.text = text; } public LeftMenu() { } }
在这个JavaBean中,我们存储Image的资源id,然后在使用的时候直接调用这个资源id即可,再看看Adapter:
public class LeftMenuAdapter extends BaseAdapter { private List<LeftMenu> list; public LeftMenuAdapter(List<LeftMenu> list) { this.list = list; } @Override public int getCount() { return list.size(); } @Override public Object getItem(int position) { return list.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; if (convertView == null) { convertView = LayoutInflater.from(parent.getContext()).inflate( R.layout.listview_item, null); holder = new ViewHolder(); holder.iv = (ImageView) convertView.findViewById(R.id.left_iv); holder.tv = (TextView) convertView.findViewById(R.id.left_tv); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } holder.iv.setImageResource(list.get(position).getImageView()); holder.tv.setText(list.get(position).getText()); return convertView; } class ViewHolder { ImageView iv; TextView tv; } }
这个Adapter很简单,我就不多说了。
nineoldandroids
这些都看完了,就该说说我们的MainActivity,首先我们得大概说说这种效果是怎么实现的,说到这里,我就得给大家介绍一个非常非常出名的动画类了,那就是nineoldandroids,我们在GitHub上可以直接下载到这个动画类的源码(https://github.com/JakeWharton/NineOldAndroids),如果你想用jar包,一会直接下载本项目源码就能拿到了。
nineoldandroids中有一些非常好用的方法,比如:
ViewHelper.setScaleX(View view, float scaleX) ViewHelper.setScaleY(View view, float scaleY)
通过这个方法,我们可以对一个View进行缩放,第一个参数是我们要缩放的View,第二个参数是缩放比例,还有一个方法:
ViewHelper.setAlpha(View view, float alpha)通过这个方法,我们可以设置一个View的透明度,第一个参数是我们要改变透明度的View,第二个参数是透明度。还有一个方法:
ViewHelper.setTranslationX(View view, float translationX)通过这个方法,我们可以对一个View进行平移操作,第一个参数是要平移的View,第二参数是在X轴平移多少。还有一个方法:
ViewHelper.setScaleX(View view, float scaleX) ViewHelper.setScaleY(View view, float scaleY)
通过这个方法,我们可以设置View变化时的一个轴心。
为什么介绍这几个方法呢?原因很简单,因为我们即将用到。
好了,介绍完这几个方法之后,我们的MainActivity就可以登场了。只需要简单的几个动画我们就可以实现QQ的这种侧拉效果了。
在MainActivity中我们先拿到DrawerLayout的一个实例:
drawerLayout = (DrawerLayout) this.findViewById(R.id.drawerLayout);然后我们要给DrawerLayout设置一个监听事件,当菜单出场的时候我们调整主布局的位置:
drawerLayout.setDrawerListener(new DrawerListener() { @Override public void onDrawerStateChanged(int newState) { Log.i("lenve", "onDrawerStateChanged"); } @Override public void onDrawerSlide(View drawerView, float slideOffset) { slideAnim(drawerView, slideOffset); Log.i("lenve", "onDrawerSlide"); } @Override public void onDrawerOpened(View drawerView) { Log.i("lenve", "onDrawerOpened"); } @Override public void onDrawerClosed(View drawerView) { Log.i("lenve", "onDrawerClosed"); } });
这个监听事件一共要实现其中的四个方法,看名字我们大概也知道这四个方法是干什么的,那我们看看这四个方法的执行时机:
当我们打开菜单的时候,先执行onDrawerStateChanged,然后不断执行onDrawerSlide,第三步会执行onDrawerOpened,最后执行onDrawerStateChanged,当我们关闭菜单的时候,先执行onDrawerStateChanged,然后不断执行onDrawerSlide,第三步会执行onDrawerClosed,最后执行onDrawerStateChanged,好了,方法的执行时机如果大家还有疑问可以通过打印日志来查看各个方法的执行时机。好了,我们的动画逻辑要在onDrawerSlide方法中来完成,先来说说这个方法的这两个参数,第一个参数是一个View,这个View其实就是左边侧拉菜单的View,第二参数是偏移量,可以简单理解为左边菜单拉出来的比例,它的取值是从0到1。介绍完这个之后,我们来看看slideAnim(drawerView, slideOffset);方法,看看最核心的代码有多么简单:
private void slideAnim(View drawerView, float slideOffset) { View contentView = drawerLayout.getChildAt(0); // slideOffset表示菜单项滑出来的比例,打开菜单时取值为0->1,关闭菜单时取值为1->0 float scale = 1 - slideOffset; float rightScale = 0.8f + scale * 0.2f; float leftScale = 1 - 0.3f * scale; ViewHelper.setScaleX(drawerView, leftScale); ViewHelper.setScaleY(drawerView, leftScale); ViewHelper.setAlpha(drawerView, 0.6f + 0.4f * (1 - scale)); ViewHelper.setTranslationX(contentView, drawerView.getMeasuredWidth() * (1 - scale)); ViewHelper.setPivotX(contentView, 0); ViewHelper.setPivotY(contentView, contentView.getMeasuredHeight() / 2); contentView.invalidate(); ViewHelper.setScaleX(contentView, rightScale); ViewHelper.setScaleY(contentView, rightScale); }好了,我们可以看到rightScale取值是从1到0.8,那么大家注意rightScale最后用在了contentView上,所以对应的一个显示效果就是contentView从最初大小变为最初大小的0.8倍,leftScale取值是从0.7到1。leftScale最后用在了drawerView,也就是左边的侧拉菜单View,那么对应的显示效果就是左边的菜单View一开始只有原本布局的0.7倍,在菜单慢慢往出滑动的时候,它逐渐变大,直到变为原本的大小(这个时候菜单View已经完全显示出来了)。另外几个动画设置都比较简单,大家参照我们上面对这几个方法的讲解来理解。
好了,搞定这些之后,还剩最后一个东东,就是左上角的点击事件,这个我们在上一篇博客中已经介绍过了,这里我就直接贴代码:
public void onClick(View v) { drawerLayout.openDrawer(Gravity.LEFT); }
所有这些工作做完之后,一个仿QQ5.0侧拉的Demo已经出炉了。。。。
好了,今天就说到这里,大家有什么问题,可以留言讨论。
Demo下载https://github.com/lenve/QQDrawerLayout