1 DrawerLayout说明
1.1继承体系:java.lang.Object <-android.view.View <- android.view.ViewGroup <- android.support.v4.widget.DrawerLayout
1.2JavaDoc中的说明:DrawerLayout是一个包含了窗口内容(window content)的顶层容器,它可以让窗口内容与窗口边缘拉出的“自绘”(“drawer”)视图交互。
Drawer的位置和布局是可控的,在子视图的参数中使用android:layout_gravity设定你希望Drawer从父视图的哪边滑出:left或right(或者start/end,在支持布局方向设置的平台版本中)(注:这里的left/right和start/end是android:layout_gravity的具体取值,推荐使用start/end)。
为了使用DrawerLayout,你需要把一个宽高值都是match_parent的主内容视图(primary content view)作为布局的第一个子组件。然后在主内容视图后添加Drawer子组件并配置合适的layout_gravity参数。Drawer通常使用match_parent的高(height)和一个固定值的宽(width)(注:固定值的宽代表了Drawer被滑出后可显示的宽度)。
接口DrawerLayout.DrawerListener可以用于监听Drawer状态和动作的变化。在Drawer动画过程中避免执行耗时的操作,否则可能造成卡顿;可以在STATE_IDLE状态下执行耗时操作。抽象类DrawerLayout.SimpleDrawerListener为每一个回调方法提供了默认的无操作的实现。
就如每一个Android设计指南所描述的那样,通常一个Drawer的滑出位置在left/start时,其内容是应用的导航信息,而如果滑出位置在right/end时,其内容是可以对当前主内容执行的操作。Action Bar提供了类似的左边导航右边动作的语法糖(注:通过类ActionBarDrawerToggle,该类实现了DrawerListener接口)。
1.3小结:通过对DrawerLayout的说明可以看出,该布局同LinaerLayout等布局一样,也是顶层布局,不同的是其内部子组件有严格的顺序,第一个子组件(view)代表了显示的主内容,可以是一个layout或者一个简单的view,第二个子组件代表了侧滑出的菜单,也可以是一个layout或者简单的view。侧滑的方向由第二个子组件的layout_gravity参数决定,取值是left/right或start/end(建议用start/end,避免和原来的布局方向取值混淆)。通过实现DrawerLayout.DrawerListener接口,可以回调各种有用的方法。
1.4 注:这里的Drawer代指侧滑菜单。
1.5 小结:经测试,侧滑菜单和主内容区可以不严格按顺序来,但是如果第一个子组件指定layout_gravity属性,而且包含listview的话,则listview不能拖动,只有当包含listview的子组件位置在所有子组件的最后时,listview可以响应拖动,而主布局(DrawerLayout)中其他未指定layout_gravity属性的子组件都是内容区。如果指定两个同layout_gravity属性的组件,APP不会报错,但是侧滑以后会无响应。
通过查看源码,侧滑菜单拖动依赖于android.support.v4.widget.ViewDragHelper,其内部变量”private static final int EDGE_SIZE = 20; // dp”定义了侧滑菜单拖动的边距,即在边缘多少dp的地方按下后可以触发拖动,如果在边缘按下BASE_SETTLE_DURATION=256毫秒后,会弹出侧滑菜单,然后可以拖动,如果快速从边缘按下并拖动,则直接出现拖动中的侧滑菜单。具体菜单的绘制过程都在该类中。
通过反射可以修改边缘感应距离:
Field f = drawerLayout.getClass().getDeclaredField("mLeftDragger");
f.setAccessible(true);
Class drag = f.getType();
Field edge = drag.getDeclaredField("mEdgeSize");
edge.setAccessible(true);
int width = listview.getLayoutParams().width/2;
edge.setInt(f.get(drawerLayout), width);
如果修改的距离太大,可能会发生拖不出侧滑菜单的bug,原因是距离太大,在感应区的触摸时间超过了BASE_SETTLE_DURATION,进入了响应长按弹出侧滑菜单的逻辑,也可以通过反射修改,这里就需要根据具体需要去做修改了。
侧滑菜单必须设置背景色,否则默认是透明背景。
1.6状态常量:STATE_DRAGGING:表明用户正在拖动Drawer。STATE_IDLE:表明Drawer目前空闲,被固定。STATE_SETTLING:表明Drawer马上要进入最终位置。
1.7主要方法:1)void setScrimColor(int color) :设置未被侧滑菜单遮挡的主内容区的覆盖色,默认是渐变灰,如果设置为android.R.color.transparent,则未覆盖区为透明色,即不改变未覆盖区的显示。
2)void setDrawerShadow(Drawable shadowDrawable, int gravity) :设置侧滑菜单在拖动侧边缘的显示图片,比如从start位拖出,可以在拖动边缘显示一个向右的箭头图片。
2 DrawerLayout.DrawerListener接口说明
2.1 说明:该接口用于监视Drawer
2.2 主要回调方法:1)abstract void OnDrawerClosed(View drawerView)
说明:当Drawer完全关闭时回调
参数:drawerView 被关闭的Drawer视图
2)abstract void OnDrawerOpened(View drawerView)
说明:当Drawer完全打开时回调,此时Drawer处在可以交互的位置(注:即可以点击Drawer内的子组件了)
参数:同上
3)abstract void OnDrawerSlide(View drawerView, float slideOffset)
说明:当Drawer的位置改变时回调
参数:同上。slideOffset 当前Drawer的新偏移量占其宽的比值,取值0-1
4)abstract void OnDrawerStateChanged(int newState)
说明:当Drawer状态改变时回调
参数:新状态,取值STATE_IDLE,STATE_DRAGGING,STATE_SETTLING
2.3 主要的实现类:ActionBarDrawerToggle, DrawerLayout.SimpleDrawerListener
3 ActionBarDrawerToggle说明
注:SimpleDrawerListener是抽象类,方便开发者实现接口而不用实现所有接口
3.1 JavaDoc中的说明:该类实现了一种方法可以把DrawerLayout和框架ActionBar关联在一起,推荐用该方法实现导航视图设计。
为了使用本类,可以在你的Activity中创建一个实例,然后在适当的时候调用onConfigurationChanged和onOptionsItemSelected。
在onRestoreInstanceState被调用后,在你的Activity的onPostCreate方法中调用本类的syncState()方法来同步关联的DrawerLayout的状态。
本类可以完全当做一个DrawerListener。
3.2 主要方法:public ActionBarDrawerToggle(Activity activity, DrawerLayout drawerLayout, int drawerImageRes, int openDrawerContentDescRes, int closeDrawerContentDescRes)
说明:给定的activity会和指定的DrawerLayout关联。Drawer指示器图标(drawer indicator drawable)在Drawer打开时会触发动画,指示了Drawer在打开状态时离屏显示,在关闭状态时在屏显示。字符串资源用来描述Drawer的open/close动作。
参数:activity:Drawer托管的activity。drawerLayout:给定的Activity的ActionBar关联的DrawerLayout。drawerImageRes:一个Drawable资源用来做Drawer指示器。
void syncState()
说明:同步Drawer指示器和关联DrawerLayout的状态。该方法必须在Activity的onPostCreate方法中调用,可以在DrawerLayout实例的状态恢复后同步二者状态,而在其他时间,本类不会收到状态通知(比如,如果你在一段时间内停止分发Drawer的事件)。
4 SDK中的示例
public class DrawerLayoutActivity extends Activity {
private DrawerLayout mDrawerLayout;
private ListView mDrawer;//侧滑菜单
private TextView mContent;//主内容区
private ActionBarHelper mActionBar;//Activity的ActionBar封装
private ActionBarDrawerToggle mDrawerToggle;//连接DrawerLayout和ActionBar的DrawerListener
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawer_layout);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawer = (ListView) findViewById(R.id.start_drawer);
mContent = (TextView) findViewById(R.id.content_text);
mDrawerLayout.setDrawerListener(new DemoDrawerListener());
//设置侧滑菜单边缘的阴影
mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);
mDrawerLayout.setDrawerTitle(GravityCompat.START, getString(R.string.drawer_title));
//这里的侧滑菜单是一个ListView,所以设置他的adapter
mDrawer.setAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1,
Shakespeare.TITLES));
mDrawer.setOnItemClickListener(new DrawerItemClickListener());
mActionBar = createActionBarHelper();
mActionBar.init();
mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close);
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
// Sync the toggle state after onRestoreInstanceState has occurred.
mDrawerToggle.syncState();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
/*
* The action bar home/up action should open or close the drawer.
* mDrawerToggle will take care of this.
*/
if (mDrawerToggle.onOptionsItemSelected(item)) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
mDrawerToggle.onConfigurationChanged(newConfig);
}
/**
* This list item click listener implements very simple view switching by changing
* the primary content text. The drawer is closed when a selection is made.
*/
private class DrawerItemClickListener implements ListView.OnItemClickListener {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
mContent.setText(Shakespeare.DIALOGUE[position]);
mActionBar.setTitle(Shakespeare.TITLES[position]);
mDrawerLayout.closeDrawer(mDrawer);
}
}
/**
* A drawer listener can be used to respond to drawer events such as becoming
* fully opened or closed. You should always prefer to perform expensive operations
* such as drastic relayout when no animation is currently in progress, either before
* or after the drawer animates.
*
* When using ActionBarDrawerToggle, all DrawerLayout listener methods should be forwarded
* if the ActionBarDrawerToggle is not used as the DrawerLayout listener directly.
*/
private class DemoDrawerListener implements DrawerLayout.DrawerListener {
@Override
public void onDrawerOpened(View drawerView) {
mDrawerToggle.onDrawerOpened(drawerView);
mActionBar.onDrawerOpened();
}
@Override
public void onDrawerClosed(View drawerView) {
mDrawerToggle.onDrawerClosed(drawerView);
mActionBar.onDrawerClosed();
}
@Override
public void onDrawerSlide(View drawerView, float slideOffset) {
mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
}
@Override
public void onDrawerStateChanged(int newState) {
mDrawerToggle.onDrawerStateChanged(newState);
}
}
/**
* Create a compatible helper that will manipulate the action bar if available.
*/
private ActionBarHelper createActionBarHelper() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
return new ActionBarHelperICS();
} else {
return new ActionBarHelper();
}
}
/**
* Stub action bar helper; this does nothing.
*/
private class ActionBarHelper {
public void init() {}
public void onDrawerClosed() {}
public void onDrawerOpened() {}
public void setTitle(CharSequence title) {}
}
/**
* Action bar helper for use on ICS and newer devices.
*/
private class ActionBarHelperICS extends ActionBarHelper {
private final ActionBar mActionBar;
private CharSequence mDrawerTitle;
private CharSequence mTitle;
ActionBarHelperICS() {
mActionBar = getActionBar();
}
@Override
public void init() {
mActionBar.setDisplayHomeAsUpEnabled(true);
mActionBar.setHomeButtonEnabled(true);
mTitle = mDrawerTitle = getTitle();
}
/**
* When the drawer is closed we restore the action bar state reflecting
* the specific contents in view.
*/
@Override
public void onDrawerClosed() {
super.onDrawerClosed();
mActionBar.setTitle(mTitle);
}
/**
* When the drawer is open we set the action bar to a generic title.
* The action bar should only contain data relevant at the top level of
* the nav hierarchy represented by the drawer, as the rest of your content
* will be dimmed down and non-interactive.
*/
@Override
public void onDrawerOpened() {
super.onDrawerOpened();
mActionBar.setTitle(mDrawerTitle);
}
@Override
public void setTitle(CharSequence title) {
mTitle = title;
}
}
}