侧滑菜单是Android应用中出现的比较多的功能,下面来看一下是怎么实现的。
Android Studio里面有现成的模板,给项目取名TestSlidePage,可以看一下它生成的东西
从MainActivity里面看一下主要的布局文件setContentView(R.layout.activity_main);
是activity_main。
而activity_main.xml里面的内容如下:
<?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"
xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout"
android:layout_width="match_parent" android:layout_height="match_parent"
android:fitsSystemWindows="true" tools:openDrawer="start">
<include layout="@layout/app_bar_main" android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.NavigationView android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_gravity="start" android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" />
</android.support.v4.widget.DrawerLayout>
整体是一个DrawerLayout,它里面是两个view,第一个是你的主要布局内容,第二个是侧滑出来的布局内容。这样就可以实现抽屉效果。
此处侧滑出来的控件是NavigationView,是用于导航,其实也可以是别的view,比如说listview等。
下面说一下NavigationView
<android.support.design.widget.NavigationView android:id="@+id/nav_view"
android:layout_width="wrap_content" android:layout_height="match_parent"
android:layout_gravity="start" android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" />
看到它里面有属性app:headerLayout="@layout/nav_header_main"
设置了导航的头部布局(就是图中的绿色部分),看一下nav_header_main.xml这个布局文件,内容也确实是一个图片带两个文字
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="@dimen/nav_header_height"
android:background="@drawable/side_nav_bar"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:theme="@style/ThemeOverlay.AppCompat.Dark" android:orientation="vertical"
android:gravity="bottom">
<ImageView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing"
android:src="@android:drawable/sym_def_app_icon" android:id="@+id/imageView" />
<TextView android:layout_width="match_parent" android:layout_height="wrap_content"
android:paddingTop="@dimen/nav_header_vertical_spacing" android:text="Android Studio"
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="android.studio@android.com" android:id="@+id/textView" />
</LinearLayout>
那么导航头部下面的这些东西是怎么来的呢?
原来是app:menu="@menu/activity_main_drawer"
指定了导航里面的菜单,可以看到这个菜单文件里面的内容是下面这样的:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item android:id="@+id/nav_camara" android:icon="@android:drawable/ic_menu_camera"
android:title="Import" />
<item android:id="@+id/nav_gallery" android:icon="@android:drawable/ic_menu_gallery"
android:title="Gallery" />
<item android:id="@+id/nav_slideshow" android:icon="@android:drawable/ic_menu_slideshow"
android:title="Slideshow" />
<item android:id="@+id/nav_manage" android:icon="@android:drawable/ic_menu_manage"
android:title="Tools" />
</group>
<item android:title="Communicate">
<menu>
<item android:id="@+id/nav_share" android:icon="@android:drawable/ic_menu_share"
android:title="Share" />
<item android:id="@+id/nav_send" android:icon="@android:drawable/ic_menu_send"
android:title="Send" />
</menu>
</item>
</menu>
菜单分为两个组,所以图上会有组与组之间的分隔线。菜单的item里面android:icon
用来设置菜单的图标,android:title
用来设置菜单的文字。当菜单项很多的时候,侧滑页面是可以上下滑动的。
假如说,我的菜单很少,只有两个,所以怎么都不可能会出现滑动的情况,而且我还希望在侧滑页面底部有一个类似“退出登录”的按钮,那怎么办?
如果把“退出登录”作为一个菜单项,那么它就会和其他菜单项靠得很近,达不到我想要的位于侧滑页底部的效果。
那么可以用添加子视图的方法解决。将“退出登录”按钮作为子视图加到NavigationView上。
之前进过一个坑,就是使用LinearLayout布局,无论怎么设置边距、位置等,按钮始终还是在NavigationView上方,o(╯□╰)o,o(╯□╰)o,o(╯□╰)o,o(╯□╰)o
LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
Button bt_logOut = new Button(this);
bt_logOut.setText(R.string.home_slide_logout);
LinearLayout layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.bottomMargin = 20;
linearLayout.addView(bt_logOut);
navigationView.addView(linearLayout,layoutParams);
后来在stack overflow上找到了答案
原来NavigationView是基于FrameLayout的,所以不应该用LinearLayout,而是应该是FrameLayout
FrameLayout frameLayout = new FrameLayout(this);
this.addLogOutButtonToNaviView();
Button bt_logOut = new Button(this);
bt_logOut.setText(R.string.home_slide_logout);
bt_logOut.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);
layoutParams.gravity = Gravity.BOTTOM;
layoutParams.bottomMargin = 20;
layoutParams.leftMargin = 20;
layoutParams.rightMargin = 20;
frameLayout.addView(bt_logOut);
navigationView.addView(frameLayout,layoutParams);
这样就达到了将“退出登录”按钮放在侧滑页底部的效果
最最重要的,有了主页,有了侧滑页,还要有滑动的过程。
使用ActionBarDrawerToggle
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
if (getSupportActionBar() != null){
getSupportActionBar().setHomeButtonEnabled(true);//设置返回键可用(三杠)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);//显示后退位置的图标
}
drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer,toolbar, R.string.home_slide_open, R.string.home_slide_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
当按下导航栏上的那个三杠按钮的时候,执行抽屉的开和关的操作
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//如果按下的是home位置按钮(三杠或者后退)
if (id == android.R.id.home){
if(drawer.isDrawerOpen(GravityCompat.START)){
drawer.closeDrawers();//收起侧面菜单
}else{
drawer.openDrawer(GravityCompat.START);//弹出侧面菜单
}
return true;//返回true表示此菜单的事件已经完成,不需要再传递给别人
}
return super.onOptionsItemSelected(item);
}
NavigationView里面的几个菜单,虽然我给它们设置了图标,但是却没在界面上显示!!!!
后来我把图片改成24dp的,就能显示了----------具体原因还待看一看
当选中导航栏的某个菜单的时候,对其进行监听,可以去执行相应的操作
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
@Override
public boolean onNavigationItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case xxx: {
Intent intent = new Intent(HomeActivity.this, XXX.class);
startActivity(intent);
}
break;
case yyy: {
Intent intent = new Intent(HomeActivity.this, YYY.class);
startActivity(intent);
}
break;
default:
break;
}
return true;
}
});
在navigationview上设置navigationItemSelectedListener,可以获取选中的那个菜单。
但是上方的代码会有一个小小的问题,选中了某个菜单之后,它就是选中的状态,放开后也还是选中状态。而我的需求是选中后就恢复原状。
可以在onNavigationItemSelected被触发的时候,在里面添加一行代码item.setCheckable(false);
这样就能在选中后恢复原状。
【吐槽:我本来以为是item.setChecked(false);
这个方法可以让被选中的恢复原状,但它没效果。%>_<%】
后来我发现一个问题,menu左边的图标,不管图片是什么颜色,显示出来的时候都是灰色的【%>_<%怎么回事!】
原来navigationview有以下两个属性,分别可以设置图标的颜色和menu的字体颜色
app:itemIconTint="@color/color_slide_navi_menu_item_icon_tint"
app:itemTextColor="@color/color_slide_navi_menu_item_text"
app:itemIconTint
用来设置图标的颜色
app:itemTextColor
用来设置菜单字体的颜色