Android DrawerLayout
DrawerLayout
DrawerLayout,官方给我们提供的一个侧滑菜单
控件,3.0以后引入。为了兼容低版本,可以使用v4兼容包里的DrawerLayout。随着Material Design设计规范的发布,越来越多的应用用到了侧滑菜单的动画效果。Toolbar和DrawerLayout的组合可以实现非常好的效果。
首先来看下总体的效果,网易云音乐App:
网易云音乐.pngDrawerLayout基本使用
一般情况下,DrawerLayout第一个子视图就是主内容布局,也就是打开App第一眼看到的布局;而第二个子视图就是侧滑菜单布局。区分这两个布局的关键属性android:layout_gravity
.设置了这个属性的布局,就是侧滑菜单布局,而它的值决定了侧滑的方向。常规规范在编写布局文件时,第一个子布局是内容布局。
当android:gravity值为start|left时,侧滑菜单侧滑方式是左侧滑出;相反值为end|right时,侧滑菜单侧滑方式为右侧滑出。在编写主内容布局文件时,高宽必须是match_parent否则会报错
DrawerLayout activity布局文件
<?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:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/id_drawer_layout_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/light_menu_header"
android:minHeight="?android:actionBarSize"
android:paddingTop="@dimen/padding_top"
android:gravity="center_vertical"
android:elevation="4dp"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:title="@string/zhuhu_name"/>
<FrameLayout
android:id="@+id/id_content"
android:layout_width="match_parent"
android:layout_height="match_parent"></FrameLayout>
</LinearLayout>
<include
layout="@layout/draw_menu_layout"/>
</android.support.v4.widget.DrawerLayout>
Drawerlayout Menu布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="280dp"
android:layout_gravity="start"
android:layout_height="match_parent"
android:clickable="true"
android:orientation="vertical">
<LinearLayout
android:id="@+id/id_draw_menu_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/light_menu_header"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingTop="@dimen/padding_top">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginRight="15dp"
android:orientation="horizontal">
<ImageView
android:layout_width="36dp"
android:layout_height="36dp"
android:background="@drawable/ic_account_circle_white_24dp"/>
<TextView
android:id="@+id/id_draw_menu_item_login_tv"
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginLeft="10dp"
android:gravity="center_vertical"
android:text="@string/draw_menu_item_login_tv"
android:textColor="@android:color/white"
android:textSize="18sp"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="horizontal">
<TextView
android:id="@+id/id_draw_menu_item_backup"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="@drawable/ic_star_white_24dp"
android:gravity="center"
android:text="@string/draw_menu_item_backup"
android:textColor="@android:color/white"
android:textSize="15sp"/>
<TextView
android:id="@+id/id_draw_menu_item_download"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_weight="1"
android:drawableLeft="@drawable/ic_file_download_white_24dp"
android:gravity="center"
android:text="@string/draw_menu_item_download"
android:textColor="@android:color/white"
android:textSize="15sp"/>
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/id_draw_menu_item_main_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/light_menu_index_background"
android:paddingBottom="10dp"
android:paddingLeft="80dp"
android:paddingTop="10dp"
android:text="@string/draw_menu_item_main"
android:textColor="@android:color/holo_blue_dark"
android:textSize="18sp"
/>
<ListView
android:id="@+id/id_draw_menu_item_list_select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:divider="@android:color/transparent"
android:scrollbars="none"></ListView>
</LinearLayout>
上述布局文件中有两个关键属性:android:clickable
和android:layout_width
。将android:clickable
设置为true是为了消费点击在DrawerLayout Menu Header布局上的事件(很多应用menu头部是一个图片,本身不能消费事件。这样事件就传递到了Menu布局下方的内容布局,会导致误操作)。android:layout_width
属性决定了侧滑菜单的宽度,官方建议一般小于320dp。
Activity中初始化
package android.example.com.toolbarusage;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
/**
* Created by syt on 16/10/16.
*/
public class ZhuHuDrawerActivity extends AppCompatActivity {
private Toolbar mToolbar;
private DrawerLayout mDrawerlayout;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
translucentStatusBar();
setContentView(R.layout.activity_drawer_layout);
initView();
}
private void initView() {
mToolbar = (Toolbar) findViewById(R.id.id_drawer_layout_toolbar);
//mToolbar.setMinimumHeight(calculateToolbarHeight());
//mToolbar.inflateMenu(R.menu.draw_layout_menu);
setSupportActionBar(mToolbar);
mToolbar.setNavigationIcon(R.mipmap.ic_drawer_home);
mDrawerlayout = (DrawerLayout) findViewById(R.id.id_drawer_layout);
//DrawerLayout监听器
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this,
mDrawerlayout,
mToolbar,
R.string.app_name,
R.string.app_name
);
mDrawerlayout.addDrawerListener(toggle);
toggle.syncState();
}
private int calculateToolbarHeight() {
int idStatusBar = getResources().getIdentifier("status_bar_height", "dimen", "android");
int statusBarHeight = getResources().getDimensionPixelSize(idStatusBar);
TypedArray ta = obtainStyledAttributes(new int[]{android.R.attr.actionBarSize});
int actionBarHeight = ta.getDimensionPixelSize(0, 0);
return statusBarHeight + actionBarHeight;
}
/**
* 实现5.0以上状态栏透明(默认状态是半透明)
*/
private void translucentStatusBar() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ViewGroup decorView = (ViewGroup) getWindow().getDecorView();
int option =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
decorView.setSystemUiVisibility(option);
getWindow().setStatusBarColor(ContextCompat.getColor(
getApplicationContext(),
R.color.light_menu_header
)
);
}
}
/**
* 当使用setSupportActionBar()时,调用Toolbar.inflateMenu()时无效果,必须用此方法实现
* @param menu
* @return
*/
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.draw_layout_menu, menu);
return true;
}
}
代码分析:先去除AppCompatActivity自带的ActionBar,通过findVewiById()获取到Toolbar空间对象后,调用setSupportActionBar()方法替换掉默认的ActionBar。这样操作之后又一个问题,直接调用Toolbar.inflateMenu()后Menu并没有显示出来,而是要通过onCreateOptionsMenu()去加载menu布局文件才可以实现效果。最后创建ActionBarDrawerToggle监听器,并且绑定DrawerLayout对象,实现监听侧滑菜单的显示和隐藏事件。同时适配了5.0以上的修改了状态栏颜色,保证Toolbar和DrawerLayout Menu实现状态栏的沉浸。
最终效果:
DrawerLayout.gifActionBarDrawerToggle
监听侧滑菜单事件可以使用DrawerLayout.DrawerListener,并重写相应的回调方法,实现监听相应的侧滑菜单栏状态的事件。那为什么用ActionBarDrawerToggle
ActionBarDrawerToggle,它是DrawerLayout.DrawerListener的具体实现类
ActionBarDrawerToggle是一个开关,用于打开/关闭DrawerLayout抽屉
ActionBarDrawerToggle 提供了一个方便的方式来配合DrawerLayout和ActionBar,以实现推荐的抽屉功能。即点击ActionBar的home按钮,即可弹出DrawerLayout抽屉。
虽然这里的demo用的是Toolbar,但是通过这个监听器,一样可以实现上述效果。我猜测原因是setSupportActionBar()方法的作用。
在Activity中的两个回调函数中使用它:
onConfigurationChanged
,
onOptionsItemSelected
。
调用ActionBarDrawerToggle.syncState() 在Activity的onPostCreate()中:指示,ActionBarDrawerToggle与DrawerLayout的状态同步,并将ActionBarDrawerToggle中的drawer图标设置为ActionBar的Home-Button的icon。
可以通过重写ActionBarDrawerToggle的onDrawerOpened()和onDrawerClosed()以监听抽屉拉出
或隐藏事件。
问题总结
DrawerLayout.setDrawerListener()
该方法已经废弃,可以使用addDrawerListener()。
setSupportActionBar()
调用此方法以后,原先加载的menu布局不显示,需要通过onCreateOptionsMenu()方法来实现。
DrawerLayout Menu状态栏沉浸
之前为了给Toolbar实现状态栏沉浸效果,将状态栏悬浮在DecorView之上,所以当滑出侧滑菜单栏时,状态栏遮挡住了一部分显示内容。解决方案就是给DrawerLayout Menu布局文件的header布局添加一个paddingTop属性,其值为状态栏高度。
误操作
给DrawerLayout Menu根布局设置android:clickable
属性,其值设置为true。是为了消费点击在DrawerLayout Menu Header布局上的事件(很多应用menu头部是一个图片,本身不能消费事件。这样事件就传递到了Menu布局下方的内容布局,会导致误操作)。
参考
Android开发:最详细的 NavigationDrawer 开发实践总结