关闭

Fragment Navigation Drawer

510人阅读 评论(0) 收藏 举报
分类:
左侧导航抽屉框的使用

    在常见的导航范例中(作者开发的集锦),我们讨论了在android app 中可以使用的导航结构,而在其中最灵活的要数Navigation Drawer。 在谷歌的2015年I/O 大会上,谷歌发布了之前创建Navigation Drawer的文档指导更加简单的Navigation View.

    随着Android 5.0 Lolipop版本的发布,新式导航抽屉的设计风格的高度跨越了整个屏幕的高度,超过了ActionBar并且重叠了透明的StatusBar。当你自己在设计自己的导航抽屉风格时,可以参考导航抽屉的主要涉及风格文档 。
使用
    可以定义多种fragments 和抽屉项目列表中的多个选项,点击每一个项目都可以在activity的内容视图上显示出相应的fragment
建立
    在使用谷歌新发布的NavigationView 之前,确保你已经安装了Android M版本发布的谷歌设计支持库。NavigationView已经可以兼容Android 2.1之前的所有版本。
下载导航抽屉选项图标
下载下面的图标并且将它们复制粘贴到drawable文件夹中,或者用 New Image Asset 对话框为每一个密度都创建版本。
建立 抽屉资源
创建一个 menu/drawer_view.xml 文件(注意是menu 文件夹里):
<menuxmlns:android="http://schemas.android.com/apk/res/android">
<groupandroid:checkableBehavior="single">
<item android:id="@+id/nav_first_fragment"
android:icon="@drawable/ic_one"
android:title="First"/>
<item android:id="@+id/nav_second_fragment"
android:icon="@drawable/ic_two"android:title="Second"/>
<item android:id="@+id/nav_third_fragment"
android:icon="@drawable/ic_three"
android:title="Third"/></group>
</menu>
你可以通过设置android:checked="true" 属性,在上面的item中选择一个作为默认勾选项
同时你也可以在上面文件中继续创建副标题和组元素,具体如下:
<itemandroid:title="Sub items"><menu><itemandroid:icon="@drawable/ic_dashboard"android:title="Sub item 1"/><itemandroid:icon="@drawable/ic_forum"android:title="Sub item 2"/></menu></item>
抽屉导航的标题栏设置
导航视图也有接受标题布局引用的典型属性,举例来说,你可以建立一个layout/nav_header.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="?attr/colorPrimaryDark"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark"
    android:orientation="vertical"
    android:gravity="bottom">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Header"
        android:textColor="@android:color/white"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

</LinearLayout>

定义Fragments
接下来,你可以定义展示在抽屉内部的fragments,这些fragments 可以是你定义在应用中的任意支持的fragment,同时它们必须继承  android.support.v4.app.Fragment  

建立 ToolBar
为了可以让你的导航抽屉可以跨越ActionBar, 我们需要在AppCompat V21 library 定义使用新的Toolbar物件。Toolbar能够嵌进视图层里从而可以让抽屉划过Actionbar

用下面的代码创建一个新的布局文件 res/layout/toobar.xml

<android.support.v7.widget.Toolbar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/toolbar"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:minHeight="?attr/actionBarSize"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:background="?attr/colorPrimaryDark">
</android.support.v7.widget.Toolbar
?attr 的使用 : ?attr表示引用的是当前主题中的资源。
注意到 fitsSystemWindow 属性,当设置成true的时候视图会被布置跟 StatusBar 和 ActionBar 存在一样,举例来说,最顶层的UI界面就会有一定的宽度使导航条不被遮挡。如果没有这个属性,ToolBar 就不会留空白。
    
一般来讲我们是希望在内容视图上方留有导航条的,因此我们会将ToolBar 的  fitsSystemWindows属性设置为true.
既然使用了ToolBar 作为ActionBar ,那么就需要将默认的ActionBar屏蔽掉,<应该是有很多方法的> ,这里通过在 styles.xml   文件里设置应用主题来实现
<resources>
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">#673AB7</item>
        <item name="colorPrimaryDark">#512DA8</item>
        <item name="colorAccent">#FF4081</item>
    </style>
</resources>
在这里你可以决定你自己的颜色主题通过 Material Pallete 网站(非常好的颜色搭配网站)来选择你的Primary 和Dark Primary 颜色。在本文例子中,作者选择了紫色基调的颜色。
注意: 如果你忘记了在styles.xml 将actionbar 去掉,你就会得到这样的异常信息,This Activity already has an action bar supplied by the window decor. Do not request Window.FEATURE_ACTION_BAR and set windowActionBar to false in your theme to use a Toolbar instead. 如果你看到这条信息,请参照上面的步骤。
在Activity 中建立抽屉
接下来,我们就要使用 res/layout/activity_main.xml 的布局文件来建立基本的导航抽屉了。注意ToolBar 是通过include标签添加到主要的内容视图中的第一个孩子视图。    

<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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <!-- The ActionBar -->
        <include
            layout="@layout/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <!-- The main content view -->
        <FrameLayout
            android:id="@+id/flContent"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    <!-- The navigation drawer -->
    <android.support.design.widget.NavigationView
        android:id="@+id/nvView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/white"
        app:headerLayout="@layout/nav_header"   
app:menu="@menu/drawer_view" 
/>
</android.support.v4.widget.DrawerLayout>
 现在,让我们在自己的Activity上建立抽屉,同时我们也可以建立目录的图标

public class MainActivity extends AppCompatActivity {
	private DrawerLayout mDrawer;
	private Toolbar toolbar;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		// Set a Toolbar to replace the ActionBar.

		toolbar = (Toolbar) findViewById(R.id.toolbar);
		setSupportActionBar(toolbar);

		// Find our drawer view

		mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);

		// Set the menu icon instead of the launcher icon.

		final ActionBar ab = getSupportActionBar();
		ab.setHomeAsUpIndicator(R.drawable.ic_menu);
	 	ab.setDisplayHomeAsUpEnabled(true);

	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		// Uncomment to inflate menu items to Action Bar

		// inflater.inflate(R.menu.main, menu);

		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// The action bar home/up action should open or close the drawer.

		switch (item.getItemId()) {
			case android.R.id.home:
				mDrawer.openDrawer(GravityCompat.START);
				return true;
		}

		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onPostCreate(Bundle savedInstanceState) {
		super.onPostCreate(savedInstanceState);
	}
}
在目录条目之间切换
使用一个handler来对导航元素上的点击事件进行反应并且置换fragment

@Override
    protected void onCreate(Bundle savedInstanceState) {

         // Find our drawer view

        nvDrawer = (NavigationView) findViewById(R.id.nvView);
        // Setup drawer view

        setupDrawerContent(nvDrawer);
    }

    private void setupDrawerContent(NavigationView navigationView) {
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        selectDrawerItem(menuItem);
                        return true;
                    }
                });
    }

    public void selectDrawerItem(MenuItem menuItem) {
        // Create a new fragment and specify the planet to show based on

        // position

        Fragment fragment = null;

        Class fragmentClass;
        switch(menuItem.getItemId()) {
            case R.id.nav_first_fragment:
                fragmentClass = FirstFragment.class;
                break;
            case R.id.nav_second_fragment:
                fragmentClass = SecondFragment.class;
                break;
            case R.id.nav_third_fragment:
                fragmentClass = ThirdFragment.class;
                break;
            default:
                fragmentClass = FirstFragment.class;
        }

        try {
            fragment = (Fragment) fragmentClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }

        // Insert the fragment by replacing any existing fragment

        FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit();

        // Highlight the selected item, update the title, and close the drawer

        menuItem.setChecked(true);
        setTitle(menuItem.getTitle());
        dlDrawer.closeDrawers();  // 这里写错了么? nvDrawer?closeDrawers() 是不是要另外写?how?
    }

汉堡包图标的动画效果

为了让汉堡包图标(三条横线的图标)拥有动画效果从而表明抽屉是打开的还是关闭的,我们需要使用类 ActionBarDrawrToggle
在 res/values/strings.xml 中添加:
<resources>
    <string name="drawer_open">Open navigation drawer</string>
    <string name="drawer_close">Close navigation drawer</string>
</resources>
我们需要将图标的布局和Toolbar联系起来
protected void onCreate(Bundle savedInstanceState) { 
// Set a Toolbar to replace the ActionBar.

	toolbar = (Toolbar) findViewById(R.id.toolbar);
	setSupportActionBar(toolbar);

	// Find our drawer view

	dlDrawer = (DrawerLayout) findViewById(R.id.drawer_layout); // 这里又变成这个名字了
	drawerToggle = setupDrawerToggle();

	// Tie DrawerLayout events to the ActionBarToggle

	dlDrawer.setDrawerListener(drawerToggle);  // 所谓的联系起来
 }

 private ActionBarDrawerToggle setupDrawerToggle() {
      return new ActionBarDrawerToggle(this, dlDrawer, toolbar, R.string.drawer_open,  R.string.drawer_close);
 }
接下里,我们需要在屏幕恢复或者是设置改变时保持状态的同步
protected void onPostCreate(Bundle savedInstanceState) {
      super.onPostCreate(savedInstanceState);
      // Sync the toggle state after onRestoreInstanceState has occurred.

      drawerToggle.syncState();
  }

  @Override
  public void onConfigurationChanged(Configuration newConfig) {
      super.onConfigurationChanged(newConfig);
      // Pass any configuration change to the drawer toggles

      drawerToggle.onConfigurationChanged(newConfig);
  }

<仅仅是继承了两个回调函数>
我们需要改变 onOptionsItemSelected()方法 并且允许 ActionBarToggle 来处理这个问题
public boolean onOptionsItemSelected(MenuItem item) {
      if (drawerToggle.onOptionsItemSelected(item)) {
          return true;
      }
      return super.onOptionsItemSelected(item);
  }
 ActionBarToggle 会执行跟之前相同的功能,但添加了更多的检查并允许鼠标点击图标来打开和关闭抽屉。
你不再需要 ic_menu.png 和调用 setHomeAsUpIndicator()来设置目录。  ActionBarDrawerToggle 为你渲染了一个典型DrawerArrowDrawable
让Status Bar 变透明
为了让status Bar 变透明并且让抽屉划过,我们需要设置属性 android:windowTranslucentStatus为true,因为这种方式对 Kitkat之前的设备不支持,我们需要添加res/values-v19/styles.xml 文件为 API 19 和之前的版本。
注意: 如果你只更改 res/values/styles.xml 中的 android:windowTranslucentStatus属性,那么你必须只能构建在版本19或者更改的SDK上,很显然这会限制你支持更老的设备
在res/values-19/styles.xml 中:
<resources>
  <!-- Base application theme. -->
  <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="android:windowTranslucentStatus">true</item>
  </style>
</resources>
    限制你可以将你的app跑起来,你可以看到导航抽屉并且能够在你的fragment中进行选择。
限制
当前版本的设计支持库存在一些限制,    主要问题是系统会在导航目录上高亮你当前的条目, NavigationView的itemBackground属性不能够正确的处理所选的状态,无论是高亮的条目或者不选择的条目,这让这个属性基本对大部分应用失效, 当在导航条目中选择 副标题时,同样高亮不会像预期那样工作,更新副标题中被选择的条目会让高亮 覆盖部分一起消失。 最后处理选择条目的高亮效果是一件麻烦的事情,而这件事情需要应用自己来解决
Fragment 的替代品
尽管很多导航抽屉在内容的展示上选择了fragment, 你也可以使用 RelativieLayout/LinearLayout 如果你希望使用抽屉来覆盖当前所展示的Activity    。
你可以用linearlayout 来代替FrameLayout
<android.support.v4.widget.DrawerLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/drawer_layout">

        <LinearLayout
                android:id="@+id/content_frame"
                android:orientation="horizontal"
                android:layout_width="match_parent"
                android:layout_height="match_parent"/>

        <!-- The navigation drawer -->
        <ListView android:id="@+id/left_drawer"
                  android:layout_width="240dp"
                  android:layout_height="wrap_content"
                  android:layout_gravity="start"
                  android:choiceMode="singleChoice"
                  android:divider="@android:color/transparent"
                  android:dividerHeight="0dp"
                  android:background="#111"/>

</android.support.v4.widget.DrawerLayout>
   
同样:
getFragmentManager().beginTransaction()
       .replace(R.id.content_frame, fragment)
       .commit();
你可以用Layout 的容纳盒直接渲染Activity
LayoutInflater inflater = getLayoutInflater();
LinearLayout container = (LinearLayout) findViewById(R.id.content_frame);
inflater.inflate(R.layout.activity_main, container);
参考











 








 
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:127908次
    • 积分:2759
    • 等级:
    • 排名:第13062名
    • 原创:137篇
    • 转载:84篇
    • 译文:1篇
    • 评论:5条
    个人简介

    github地址:https://github.com/elfxwt/

    邮箱:elfxwt@163.com

    QQ:823886059

    最新评论
  • Android 中webp 的使用

    qq_32704815: 你好,请问能直接在布局文件中把src或者backgroung设置为webP吗?

  • 求职之路

    njj2011: @elfxwt:这么快就跳槽了啊!创业公司什么行业?

  • 求职之路

    elfxwt: @njj2011:额。。真是人生无常,现在的状态是已经离职了校招那家公司,已经跳槽到创业公司了。。

  • 求职之路

    njj2011: 写的很不错,偶然看到,获益良多!BTW,楼主最后签了哪家?