Android 原生导航 III-Drawer导航
2016-01-31 22:58
489 查看
概述:
Drawer导航栏是用于显示主要导航的控件, 它出现在屏幕的左侧. 大多数时候是隐藏的, 当用户从左侧滑入屏幕或者点击App bar上的图标, 它才会显示. 在Google App中它非常的常用, 也是Android上非常推崇的一种导航方式.规格:
如上图, 包括四个部分:
1. 名字: Roboto Medium, 14sp, #FFFFFF.
2. Email: Roboto Regular, 14sp,#FFFFFF.
3. 列表项: Roboto Medium, 14sp, 87% #000000.
4. 副标题: Roboto Medium, 14sp, 54% #000000. 对其到16dp关键线.
内容:
高度: Drawer会占满屏幕的高度, 但是会位于状态栏后方, 位于状态栏后面的部分依然可见, 但是会变暗. 下图是它显示出来的样子.选中的状态: 在一个条目被选中之后, 该条目会变成符合app主颜色的颜色(或者 #000000 100%)来表示自己被选中了. 选中项的所在行会被高亮.
分隔线: 所有在drawer中的分隔线都是铺满整个drawer的, 并且在其上方和下方都有8dp的填充.
上图第一个是分隔线的样子, 第二个是它所占空间的大小. 这里有8个dp的填充.
Drawer行为:
永久显示(桌面系统默认推荐使用): 这种情况下drawer永久显示在屏幕左侧而不能被隐藏. 包括三种风格: 全高度drawer, 在appbar下面的drawer以及悬浮的drawer, 较少使用.持续导航: 这种drawer可以切换显示和隐藏状态. 默认情况下隐藏, 当选择菜单图标的时候才显示, 并且当用户手动关闭它之前会持续显示. 这种导航可以应用于所有比手机大的屏幕上.
持续导航的迷你模式: 这种模式下, 持续导航改变了它的宽度. 它的隐藏状态会以一个迷你drawer的形式固定在屏幕左边, 并被app bar盖住. 当扩展时它跟标准的drawer一样. 适用于那些需要快速访问的app.下图左边是它的隐藏状态, 右图是显示状态.
临时导航: 这种drawer可以在显示和隐藏间切换, 默认情况下隐藏, 打开的时候会临时覆盖其它的内容, 直到用户选择其中的一个条目. 推荐使用: 平板. 必须使用: 手机. 通常在手机上见到的应该是这种.
Drawer的使用:
下面介绍如何使用支持库中的DrawerLayout API来实现一个drawer导航.创建一个Drawerlayout:
要想使用drawer layout, 得先在布局中使用DrawerLayout对象作为layout的根元素. 然后在其中添加一个view作为主界面的内容, 也就是当drawer隐藏的时候显示的基础layout, 之后再添加另一个view作为drawer显示的时候的内容. 栗如, 下面这个layout使用了一个DrawerLayout, 并且包含了两个子view: 一个FrameLayout作为主界面, 还有一个ListView作为导航drawer.<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- The maincontent view -->
<FrameLayout
android:id="@+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!-- Thenavigation drawer -->
<ListView android:id="@+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="@android:color/transparent"
android:dividerHeight="0dp"
android:background="#111"/>
</android.support.v4.widget.DrawerLayout>
这里有几个很重要的地方需要注意:
1. 主界面的view(也就是栗子中的FrameLayout)必须是DrawerLayout中的第一个子view(XML order impliesz-ordering and the drawer
must be on top of the content).
2. 主界面的view必须设置为填满parent的长和宽, 因为它是drawer隐藏的时候全部的界面.
3. Drawer view(上栗中的ListView)必须用android:layout_gravity属性指定它的横向重力. 为了支持从右到左读的语言(RTL),指定该值为”start”而不是”left”(这样的话当layout是RTL的时候, drawer就可以从右边显示出来了).
4. Drawer view需要使用dp作为单位来指定它的宽度, 并且高度是填满parent的. Drawer的宽度不应该超过320dp,这样用户就可以总能看到主界面的一部分了.
初始化DrawerList:
在我们的activity中, 首先要完成的事情就是初始化drawer的list. 如何实现取决于app的内容, 但是drawer经常跟ListView配合使用, list应该通过一个Adapter初始化(比如ArrayAdapter或者SimpleCursorAdapter). 栗子:public class MainActivity extends Activity { private String[] mPlanetTitles; private DrawerLayout mDrawerLayout; private ListView mDrawerList; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mPlanetTitles = getResources().getStringArray(R.array.planets_array); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerList = (ListView) findViewById(R.id.left_drawer); // Set the adapter for the list view mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, mPlanetTitles)); // Set the list's click listener mDrawerList.setOnItemClickListener(new DrawerItemClickListener()); ... } }
该栗子中调用了setOnItemClickListener()方法来接收drawer的list中的点击事件.
处理导航点击事件:
当用户选择了drawer中list的一项时, 系统会调用OnItemClickListener中的onItemClick(). 在该方法中的逻辑取决于app内容. 下面的栗子中, 选择一个条目会插入一个Fragment到主界面的view中.private class DrawerItemClickListener implements ListView.OnItemClickListener { @Override public void onItemClick(AdapterView parent, View view, int position, long id) { selectItem(position); } } /** Swaps fragments in the main content view */ private void selectItem(int position) { // Create a new fragment and specify the planet to show based on position Fragment fragment = new PlanetFragment(); Bundle args = new Bundle(); args.putInt(PlanetFragment.ARG_PLANET_NUMBER, position); fragment.setArguments(args); // Insert the fragment by replacing any existing fragment FragmentManager fragmentManager = getFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.content_frame, fragment) .commit(); // Highlight the selected item, update the title, and close the drawer mDrawerList.setItemChecked(position, true); setTitle(mPlanetTitles[position]); mDrawerLayout.closeDrawer(mDrawerList); } @Override public void setTitle(CharSequence title) { mTitle = title; getActionBar().setTitle(mTitle); }
监听Drawer的显示和隐藏事件:
要监听drawer的显示和隐藏事件, 需要在DrawerLayout上调用setDrawerListener(), 并传递给它DrawerLayout.DrawerListener的实现. 该接口提供了可以监听drawer事件的接口, 比如onDrawerOpened()和onDrawerClosed(),分别对应drawer的显示和隐藏.但是如果我们的app还包含action bar, 那么我们可以使用ActionBarDrawerToggle类代替DrawerLayout.DrawerListener. ActionBarDrawerToggle类实现了DrawerLayout.DrawerListener接口, 所以我们还是可以使用这些方法, 使用该类还有利于action bar图标跟drawer的交互(下一小节讨论).
我们应该在drawer显示的时候修改action bar的内容, 比如修改title和移除action item. 下面的代码演示了如何通过ActionBarDrawerToggle使用DrawerLayout.DrawerListener接口中的回调方法:
public class MainActivity extends Activity { private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; private CharSequence mDrawerTitle; private CharSequence mTitle; ... @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ... mTitle = mDrawerTitle = getTitle(); mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.drawable.ic_drawer, R.string.drawer_open, R.string.drawer_close) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); getActionBar().setTitle(mTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); getActionBar().setTitle(mDrawerTitle); invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu() } }; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); } /* Called whenever we call invalidateOptionsMenu() */ @Override public boolean onPrepareOptionsMenu(Menu menu) { // If the nav drawer is open, hide action items related to the content view boolean drawerOpen = mDrawerLayout.isDrawerOpen(mDrawerList); menu.findItem(R.id.action_websearch).setVisible(!drawerOpen); return super.onPrepareOptionsMenu(menu); } }
使用APP图标来显示或隐藏Drawer:
用户可以通过从左到右的滑动手势来显示drawer, 但是如果我们同时还使用了action bar, 那么我们应该允许用户通过点击action bar上的app图标来显示或隐藏drawer. 并且app图标应该用一个专用的图标来提醒用户drawer的存在. 我们可以使用ActionBarDrawerToggle来实现这个功能. 想要这个类能按照我们的想法工作, 需要用构造方法来创建一个它的对象, 该方法需要这些参数:1. Drawer所在的Activity.
2. DrawerLayout.
3. 一个drawable资源作为drawer的指示器. 标准的drawer图标可以在这里找到.
4. 一个String资源用于描述”显示drawer”操作.
5. 一个String资源用于描述”隐藏drawer”操作.
然后, 不管我们是否创建一个ActionBarDrawerToggle的子类作为drawer的监听器, 都得在activity的生命周期中调用一些ActionBarDrawerToggle的方法:
public class MainActivity extends Activity { private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mDrawerToggle; ... public void onCreate(Bundle savedInstanceState) { ... mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerToggle = new ActionBarDrawerToggle( this, /* host Activity */ mDrawerLayout, /* DrawerLayout object */ R.drawable.ic_drawer, /* nav drawer icon to replace 'Up' caret */ R.string.drawer_open, /* "open drawer" description */ R.string.drawer_close /* "close drawer" description */ ) { /** Called when a drawer has settled in a completely closed state. */ public void onDrawerClosed(View view) { super.onDrawerClosed(view); getActionBar().setTitle(mTitle); } /** Called when a drawer has settled in a completely open state. */ public void onDrawerOpened(View drawerView) { super.onDrawerOpened(drawerView); getActionBar().setTitle(mDrawerTitle); } }; // Set the drawer toggle as the DrawerListener mDrawerLayout.setDrawerListener(mDrawerToggle); getActionBar().setDisplayHomeAsUpEnabled(true); getActionBar().setHomeButtonEnabled(true); } @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); // Sync the toggle state after onRestoreInstanceState has occurred. mDrawerToggle.syncState(); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); mDrawerToggle.onConfigurationChanged(newConfig); } @Override public boolean onOptionsItemSelected(MenuItem item) { // Pass the event to ActionBarDrawerToggle, if it returns // true, then it has handled the app icon touch event if (mDrawerToggle.onOptionsItemSelected(item)) { return true; } // Handle your other action bar items... return super.onOptionsItemSelected(item); } ... }
总结:
Drawer使用四步:1. 定义DrawerLayout. 包括正文和Drawer本身.
2. 初始化Drawer.
3. 处理点击事件.
4. 处理ActionBar和Drawer的关联.
参考: https://developer.android.com/training/implementing-navigation/nav-drawer.html
https://www.google.com/design/spec/patterns/navigation-drawer.html
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories