根据需求实现类似QQ侧滑效果,之前看到过很多实现方式通过SlidingMenu,但是既然官方推出了自己的专属控件,那么使用DrawerLayout就是不二选择。且看下文。
一、先来看看官方文档解释
DrawerLayout充当窗口内容的顶层容器,允许交互式“抽屉”的观点,以从窗口的边缘拉出。抽屉的定位和布局是使用控制机器人:layout_gravity 属性对应到您想要的抽屉,从出现的观点哪边儿的观点:左或右。(或者在启动支持布局方向平台版本/结束)。要使用DrawerLayout,定位你的主要内容视图的第一个孩子,宽度和高度match_parent。添加抽屉为孩子意见的主要内容视图后,设置layout_gravity适当。抽屉通常使用match_parent的高度与宽度固定。DrawerLayout.DrawerListener可以用来监测抽屉意见的状态和运动。避免进行昂贵的操作,如动画,因为它可能会导致口吃过程中的布局; 尝试在执行昂贵的操作STATE_IDLE状态。DrawerLayout.SimpleDrawerListener提供每个回调方法的默认/无操作实现。
二、再来看看继承关系
三、效果图(真实图和效果图)
说明: 此图来自网络。
说明: 此图是实现的效果截图,在模拟器上运行的,建议换作真机看效果,状态栏的渐变更明显。
说明: 此图是模拟器运行的完整效果图,建议使用真机看效果。涉及的知识点:状态栏的设置,渐变效果;属性动画;TabLayout使用;最主要的DrawerLayout的使用。
下面就来看代码片段
主页面的布局
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/id_drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_main_top"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#14B6F6"
android:gravity="center_vertical">
<ImageView
android:id="@+id/iv_navigation_icon"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_marginLeft="10dp"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/tv_center"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_centerHorizontal="true"
android:gravity="center"
android:text="消息 电话"
android:textColor="#ffffff"
android:textSize="15sp" />
<TextView
android:id="@+id/tv_right"
android:layout_width="35dp"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_marginRight="8dp"
android:gravity="center"
android:textColor="#ffffff"
android:textSize="15sp" />
</RelativeLayout>
<android.support.design.widget.TabLayout
android:id="@+id/tab_main_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:tabGravity="center"
app:tabIndicatorHeight="0dp"
app:tabSelectedTextColor="@color/colorPrimary">
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/skin_tab_icon_conversation_selected" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/skin_tab_icon_contact_selected" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="@drawable/skin_tab_icon_plugin_selected" />
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp_main_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/tab_main_bottom"
android:layout_below="@id/rl_main_top">
</android.support.v4.view.ViewPager>
</RelativeLayout>
<include layout="@layout/draw_menu_layout" />
</android.support.v4.widget.DrawerLayout>
说明: 在这里可以看到,最外层布局是DrawerLayout,里面嵌套了两层布局,一层是RelativeLayout,一层是include进来的draw_menu_layout,这里需要说明一下,drawerlayout的子布局第一层是主页面的内容,第二层是侧滑的内容,所以才有上面的布局。当然也可以有第三层布局,作为第二层的子布局使用。
左侧布局内容
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:clickable="true"
android:tag="left">
<ImageView
android:id="@+id/iv_drawer_bg"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/fnn" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/id_draw_menu_header"
android:layout_width="match_parent"
android:layout_height="200dp">
<TextView
android:id="@+id/id_draw_menu_item_backup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="100dp"
android:drawableLeft="@drawable/ic_launcher"
android:drawablePadding="15dp"
android:gravity="center"
android:text="code小生"
android:textColor="@android:color/white"
android:textSize="15sp" />
<ImageView
android:id="@+id/id_draw_menu_item_download"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentRight="true"
android:layout_marginRight="15dp"
android:layout_marginTop="35dp"
android:scaleType="centerCrop"
android:src="@drawable/qrcode_min_cm" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white">
<TextView
android:id="@+id/id_draw_menu_item_main_tv"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_alignParentBottom="true"
android:gravity="center_vertical"
android:paddingLeft="15dp"
android:text="设置"
android:textSize="18sp" />
<ListView
android:id="@+id/id_draw_menu_item_list_select"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_above="@id/id_draw_menu_item_main_tv"
android:divider="@android:color/transparent"
android:overScrollMode="never"
android:scrollbars="none" />
</RelativeLayout>
</LinearLayout>
</FrameLayout>
说明: 这里就是一个不同的布局了,和平时写法没啥区别。需要注意的是使用FrameLayout的原因。
下面就来看看我们的主页代码:
package com.example.mjj.drawerlayoutqq;
import android.animation.ObjectAnimator;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.TabLayout;
import android.support.v4.view.GravityCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* 仿QQ6.6.0版本侧滑效果
* <p>
* DrawerLayout内容偏移,背景动画,主页面导航图标渐变,状态栏渐变色.
* <p>
* Created by Mjj on 2016/12/7.
*/
public class MainActivity extends AppCompatActivity {
private SystemBarTintManager tintManager;
private DrawerLayout mDrawerLayout;
private ImageView ivNavigation;
private ImageView ivDrawerBg; // 侧边有动画效果的背景图片
private TextView tvCenter, tvRight;
private int[] tabIcons = {R.drawable.ngq, R.drawable.nti, R.drawable.nbb};
private TabLayout tabLayout;
private ViewPager viewPager;
private ListView listView;
private String strings[] = {"开通会员", "QQ钱包", "个性装扮", "我的收藏", "我的相册", "我的文件", "我的日程", "我的名片夹"};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setTranslucentStatus(true);
tintManager = new SystemBarTintManager(this);
tintManager.setStatusBarTintEnabled(true);
tintManager.setStatusBarTintColor(Color.parseColor("#14B6F6"));
}
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
ivNavigation = (ImageView) findViewById(R.id.iv_navigation_icon);
mDrawerLayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
// 设置侧滑背景图片的动画
ivDrawerBg = (ImageView) findViewById(R.id.iv_drawer_bg);
float curTranslationY = ivDrawerBg.getTranslationY();
ObjectAnimator animator = ObjectAnimator.ofFloat(ivDrawerBg, "translationY", curTranslationY,
-70f, 60, curTranslationY);
animator.setDuration(5000);
animator.setRepeatCount(ObjectAnimator.INFINITE);
animator.start();
tvCenter = (TextView) findViewById(R.id.tv_center);
tvRight = (TextView) findViewById(R.id.tv_right);
tabLayout = (TabLayout) findViewById(R.id.tab_main_bottom);
viewPager = (ViewPager) findViewById(R.id.vp_main_contents);
// 同时添加图标和文字需要自定义view,此种方式无效,和适配器关联的时候,就被隐藏掉了.
// tabLayout.addTab(tabLayout.newTab().setIcon(tabIcons[0]));
// tabLayout.addTab(tabLayout.newTab().setIcon(tabIcons[1]));
// tabLayout.addTab(tabLayout.newTab().setIcon(tabIcons[2]));
MyFragmentPagerAdapter adapter = new MyFragmentPagerAdapter(getSupportFragmentManager(),
tabIcons, this);
viewPager.setAdapter(adapter);
tabLayout.setupWithViewPager(viewPager);
//设置监听
ivNavigation.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
toggle();
}
});
listView = (ListView) mDrawerLayout.findViewById(R.id.id_draw_menu_item_list_select);
listView.setAdapter(new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, strings));
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, strings[i], Toast.LENGTH_SHORT).show();
}
});
mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
/**
* @param drawerView
* @param slideOffset 偏移(0-1)
*/
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
// 导航图标渐变效果
ivNavigation.setAlpha(1 - slideOffset);
// 判断是否左菜单并设置移动(如果不这样设置,则主页面的内容不会向右移动)
if (drawerView.getTag().equals("left")) {
View content = mDrawerLayout.getChildAt(0);
int offset = (int) (drawerView.getWidth() * slideOffset);
content.setTranslationX(offset);
// 缩放效果(之前QQ效果)
// content.setTranslationX(1 - slideOffset * 0.5f);
// content.setTranslationY(1 - slideOffset * 0.5f);
}
tintManager.setStatusBarAlpha(1 - slideOffset);
}
@Override
public void onDrawerOpened(View drawerView) {
}
@Override
public void onDrawerClosed(View drawerView) {
}
/**
* 当抽屉滑动状态改变的时候被调用
* 状态值是STATE_IDLE(闲置-0),STATE_DRAGGING(拖拽-1),STATE_SETTLING(固定-2)中之一。
* 抽屉打开的时候,点击抽屉,drawer的状态就会变成STATE_DRAGGING,然后变成STATE_IDLE.
*
* @param newState
*/
@Override
public void onDrawerStateChanged(int newState) {
}
});
}
// 设置状态栏透明状态
private void setTranslucentStatus(boolean on) {
Window win = getWindow();
WindowManager.LayoutParams winParams = win.getAttributes();
final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
if (on) {
winParams.flags |= bits;
} else {
winParams.flags &= ~bits;
}
win.setAttributes(winParams);
}
/**
* 自定义NavigationIcon设置关联DrawerLayout
*/
private void toggle() {
int drawerLockMode = mDrawerLayout.getDrawerLockMode(GravityCompat.START);
if (mDrawerLayout.isDrawerVisible(GravityCompat.START)
&& (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_OPEN)) {
mDrawerLayout.closeDrawer(GravityCompat.START);
} else if (drawerLockMode != DrawerLayout.LOCK_MODE_LOCKED_CLOSED) {
mDrawerLayout.openDrawer(GravityCompat.START);
}
}
}
说明: 就是最核心的代码了,关于每个部分的功能都有注释,就不多说了。完整代码已上传至github,微信关注“code小生”查看链接地址。
建议:关于TabLayout使用不是很清楚的可以看看《TabLayout两种添加tab方式,结合ViewPager+Fragment实现常见界面视图》、《Android App之底部tab导航常用实现方案总结》关于本文想更近一步阅读,请看《Android之高仿QQ6.6.0侧滑效果(背景动画、透明+沉浸式状态栏、渐变效果》