Drawer 详解 ·Material Design Part 3

   2016-11-13 0
核心提示:这是一个系列文章,在这个系列里,我会按打造一个Material Design App的路线介绍所有应当掌握和值得掌握的系统组件。你会在这些文章里了解到这些组件的使用和内部实现原理,以及它们背后所反映的Material Design的设计思想,希望你会喜欢。往期回顾 Material

 Drawer 详解 ·Material Design Part 3

这是一个系列文章,在这个系列里,我会按打造一个Material Design App的路线介绍所有应当掌握和值得掌握的系统组件。

你会在这些文章里了解到这些组件的使用和内部实现原理,以及它们背后所反映的Material Design的设计思想,希望你会喜欢。

往期回顾

Material Design Part 1 · TextInput详解

Material Design Part 2 · Toolbar详解

Tips

我的每一篇博客都会提供详尽的API介绍,如果你想快速查阅某个功能的API或如何实现,建议Ctrl+F(Commad+F)在本页面搜索关键字查找。

Thanks for reading~!

前言

上一篇我介绍了有关Toolbar的知识点,在Toolbar的最左侧,可以有一个导航按钮的存在,它可以是一个向上键,可以是个菜单开关或者其他任何的样式。

通常在App首页,我们更多的见到它以一个菜单样式的图案呈现,点击该按钮,会从App的侧边弹出一个新页面,我们称之为“抽屉”。

 Drawer 详解 ·Material Design Part 3 在这篇文章里,我会 介绍抽屉在APP中的作用,以及如何通过 Drawerlayout&NavigationView来实现抽屉,顺便说下我采用的抽屉菜单延伸至状态栏的方法。

目录

  1. Navigation Drawer简介
  2. DrawerLayout详解
  3. 代码示例(一)
  4. NavigationView介绍
  5. 将DrawerLayout延伸至状态栏
  6. 代码示例(二)
  7. 几句额外的

Navigation Drawer简介

就像我一直强调的, Material Design的世界就是一个真实世界的缩影。一个优秀的Material Design组件,要么反映了真实世界里事物的功能和特性,要么反映了真实世界里的行为逻辑。

举个例子,大家都使用过抽屉,我们通常会将 比较常用但又不需要每时每刻都使用 的东西放在抽屉里。在需要的时候,我们能准确且稳定地在指定地点找到;不需要的时候可以很好地隐藏和存放,不占用更多的可见空间。在我们的app里,也一定有数个这样的操作入口:它们 比较常用但又不需要每时每刻都看见 。这时,我们自然就需要一个地方来存放它们,这就是我今天想要介绍的“抽屉导航栏”。

为了实现这样的效果,Android官方提供了 DrawerLayout ,通过它我们便可以轻松创造出一个“抽屉”,下面就让我们一起来详细了解一下它。

DrawerLayout详解

首先是使用,DrawerLayout的使用可以非常简单,仅需要如下的XML代码便能实现两侧都可以拉出抽屉的效果:

<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">
    <!-- 主内容 -->
    <LinearLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <!-- 抽屉导航栏1 -->
    <RelativeLayout android:id="@+id/left_drawer"
        android:layout_width="240dp"
        android:layout_height="100dp"
        android:layout_gravity="start"/>

    <!-- 抽屉导航栏2 -->
    <RelativeLayout android:id="@+id/right_drawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="end"/>

</android.support.v4.widget.DrawerLayout>

不过我们还是需要记住几点:

  1. 在Drawerlayout中, 主内容视图必须是第一个子视图 。因为XML里是按层叠顺序进行排序的,后面的内容会盖过前面的内容,而抽屉视图应位于页面顶部。
  2. 抽屉视图必须通过"android:layout_gravity"指定其重心位置 ,建议使用"start"&"end"来支持RTL(从右到左)语言。
  3. 主内容视图的长宽都应“match_parent”,因为当抽屉收起时它代表整个UI界面。
  4. 抽屉部分的高度应设为match_parent,而宽度不应超过320dp,以便让用户仍可以看到部分主内容。

当然,仅仅只是这样对我们的日常开发显然是不够的,我们需要对Drawerlayout有更多的细节处的掌控,对此Drawerlayout提供了简便的API,下面我们就来了解一下。

常用方法详解

  • openDrawer()方法用于打开“抽屉”,有四种参数形式:
    • (View drawerView): 打开传入的抽屉视图。
    • (int gravity): 打开传入的相应gravity上的抽屉视图。
    • (View drawerView, boolean animate): 打开传入的抽屉视图,第二个参数用于控制是否开启视图滑入动画。
    • (int gravity, boolean animate): 打开传入的相应gravity上的抽屉视图,第二个参数用于控制是否开启视图滑入动画。
  • closeDrawer()方法用于关闭“抽屉”,有四种参数形式:
    • closeDrawer(View drawerView): 关闭传入的抽屉视图。
    • closeDrawer(int gravity): 关闭传入的相应gravity上的抽屉视图。
    • closeDrawer(View drawView, boolean animate): 关闭传入的抽屉视图,第二个参数用于控制是否开启视图退出动画。
    • closeDrawer(int gravity, boolean animate): 关闭传入的相应gravity上的抽屉视图,第二个参数用于控制是否开启视图退出动画。
  • closeDrawers(): 关闭所有的抽屉视图。
  • setDrawerTitle(int edgeGravity, CharSequence title): 依据传入的gravity来给相应的Drawer设置title,设置title的目的是用于无障碍服务(Accessibility Service)。
  • CharSequence getDrawerTitle(int edgeGravity): 依据传入的gravity来获取相应Drawer的title。
  • setDrawerElevation(float elevation): 设置抽屉视图的Z轴高度,带来的视觉上的变化为 抽屉视图的阴影发生了改变
  • float getDrawerElevation(): 用于获取抽屉视图的Z轴高度,返回值为float型。
  • boolean isDrawerOpen(View drawer / int drawerGravity): 根据传入的参数判断相应的Drawer是否处于开启状态。
  • boolean isDrawerVisible(View drawer / int drawerGravity): 根据传入的参数判断相应的Drawer当前在屏幕上是否可见。
  • setScrimColor(int color): 设置当抽屉打开时,未被抽屉遮住部分的颜色。
  • setDrawerLockMode(int lockMode / int lockMode,View drawerView / int lockMode, int edgeGravity): 该方法用于设置抽屉的锁定模式,只传入锁定模式而不指定是哪个抽屉则会将两侧的抽屉都设置一遍。
    锁定模式有四种:
    • LOCK_MODE_LOCKED_CLOSED:关闭抽屉的滑动操作并将抽屉收起
    • LOCK_MODE_LOCKED_OPEN:关闭抽屉的滑动操作并将抽屉打开
    • LOCK_MODE_UNDEFINED:将锁定模式重新设置到默认状态
    • LOCK_MODE_UNLOCKED:解锁对抽屉的滑动操作锁定
      注: 关闭抽屉的滑动操作即使

      通过滑动 打开/关闭 抽屉的操作失效

  • addDrawerListener(DrawerLayout.DrawerListener listener): 为Drawerlayout添加监听器监听抽屉的开启关闭等事件。
  • removeDrawerListener(DrawerLayout.DrawerListener listener): 移除传入的DrawerLayout的相应监听器。
Drawer监听器

上面的常用API介绍最后两条提到了为DrawerLayout添加监听器,下面就来介绍一下Drawer的监听器——DrawerListener。

该接口提供了四个回调方法供复写,分别是:

  • onDrawerOpened: 当抽屉处于完全打开的状态时该方法会被回调
  • onDrawerClosed: 当抽屉处于完全关闭的状态时该方法会被回调
  • onDrawerSlide: 当抽屉被滑动时该方法会回调
  • onDrawerStateChanged: 当抽屉的状态发生变化时该方法会被回调

使用DrawerListener需要将以上四个方法都实现,但如果你只需要对其中的个别方法增加逻辑话,你可以使用 SimpleDrawerListener ,它继承自DrawerListener,不需要你实现全部四个方法而是只需复写你需要用到的方法。

ActionBarDrawerToggle

如果你的页面包含了Toolbar(ActionBar),那么我更建议你使用ActionBarDrawerToggle。从它的名字也可以看出,这是当我们需要将ActionBar和Drawer结合使用的最佳途径。

首先, ActionBarDrawerToggle该类实现了DrawerListener接口 ,所以其实你可以直接将它当作DrawerListener来使用,它能做到DrawerListener可以做的任何事。同时,会有一个抽屉样式的indicator位于Toolbar(ActionBar)上,根据抽屉的打开和关闭,会有一个很自然的演变动画给予用户当前抽屉开或闭的明确提示。当然,如果你不想要那个图标,也可以自己指定一个新的图案(详情见常用方法介绍)。

需要注意两点:

  1. 该类应配合 onConfiguration Changed()onOptionsItemSelected() 使用,具体原因我直接在下面代码的注释里写吧
  2. 如果使用了该类,应在 onPostCreate() 方法里调用 syncState() 来将指示器(indicator)的状态和Drawer同步。

常用方法

  • boolean isDrawerIndicatorEnabled(): 返回当前指示器是否可用(可见)
  • setDrawerIndicatorEnabled(boolean enable): 设置指示器是否可用(可见)
  • setHomeAsUpIndicator(Drawable indicator / int resId):默认的指示器不可用时 为Drawer设置一个新的指示器
  • setToolbarNavigationClickListener(View.OnClickListener onToolbarNavigationClickListener): 为指示器绑定点击事件监听

代码示例(一)

效果图

 Drawer 详解 ·Material Design Part 3

效果视频

activity_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_drawer_test"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <include layout="@layout/content_toolbar" />

    <android.support.v4.widget.DrawerLayout
        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:gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="设置蒙板颜色"
                android:textColor="@color/textPrimary"
                android:textSize="30sp" />

            <RadioGroup
                android:id="@+id/radio_group"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="30dp">

                <RadioButton
                    android:id="@+id/red_rbtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="红色"
                    android:textColor="@color/colorRed"
                    android:textSize="32sp" />

                <RadioButton
                    android:id="@+id/green_rbtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="绿色"
                    android:textColor="@color/colorGreen"
                    android:textSize="32sp" />

                <RadioButton
                    android:id="@+id/purple_rbtn"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="紫色"
                    android:textColor="@color/colorPurpleLight"
                    android:textSize="32sp" />
            </RadioGroup>

        </LinearLayout>


        <!-- 抽屉导航栏1 -->
        <LinearLayout
            android:id="@+id/left_drawer"
            android:layout_width="400dp"
            android:layout_height="300dp"
            android:layout_gravity="start"
            android:background="@color/colorPrimaryLight"
            android:gravity="center"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="左侧导航栏"
                android:textColor="@color/textPrimary"
                android:textSize="30sp" />

            <Button
                android:id="@+id/close_start_drawer_btn"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="关闭" />
        </LinearLayout>

        <!-- 抽屉导航栏2 -->
        <RelativeLayout
            android:id="@+id/right_drawer"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="end"
            android:background="@color/colorAccent">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="右侧导航栏"
                android:textColor="@color/textPrimary"
                android:textSize="30sp" />
        </RelativeLayout>
    </android.support.v4.widget.DrawerLayout>

</LinearLayout>

Activity.java

public class DrawerTestActivity extends AppCompatActivity {

    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.radio_group)
    RadioGroup radioGroup;
    @BindView(R.id.drawer_layout)
    DrawerLayout drawerLayout;
    @BindView(R.id.close_start_drawer_btn)
    Button closeStartDrawerBtn;
    @BindView(R.id.left_drawer)
    LinearLayout leftDrawer;

    private ActionBarDrawerToggle mToggle;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drawer_test);
        ButterKnife.bind(this);
        setSupportActionBar(toolbar);

        //配置ActionBarDrawerToggle
        toggleSettings();

        //配置RadioButton
        scrimColorSettings();
    }

    private void toggleSettings() {
        mToggle = new ActionBarDrawerToggle(this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawerLayout.addDrawerListener(mToggle);
        mToggle.syncState();
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        mToggle.syncState();
    }

    @Override
    public void onConfiguration
Changed(Configuration newConfig) {
        super.onConfiguration
Changed(newConfig);
        mToggle.onConfiguration
Changed(newConfig);
    }


    private void scrimColorSettings() {
        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                switch (checkedId) {
                    case R.id.red_rbtn:
                        drawerLayout.setScrimColor(getResources().getColor(R.color.colorRed));
                        break;
                    case R.id.green_rbtn:
                        drawerLayout.setScrimColor(getResources().getColor(R.color.colorGreen));
                        break;
                    case R.id.purple_rbtn:
                        drawerLayout.setScrimColor(getResources().getColor(R.color.colorPurpleLight));
                        break;
                }
            }
        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_drawer_test_menu, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @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 (mToggle.onOptionsItemSelected(item)) {
            return true;
        }
        switch (item.getItemId()) {
            case R.id.action_drawers_close:
                drawerLayout.closeDrawers();
                break;
            case R.id.action_drawer_locked_close:
                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
                break;
            case R.id.action_drawer_locked_open:
                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN);
                break;
            case R.id.action_drawer_unlocked:
                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
                break;
            case R.id.action_drawer_reset:
                drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED);
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @OnClick(R.id.close_start_drawer_btn)
    public void onClick() {
        drawerLayout.closeDrawer(leftDrawer);
    }
}

NavigationView介绍

我们已经知道了如何使用DrawerLayout在侧边弹出页面,接下来我们就要实现抽屉的具体效果了。那么在Material Design里,对抽屉的具体细节要求都是什么呢?看图...  Drawer 详解 ·Material Design Part 3

有如此细节的要求,自然应该有个实现这些细节的组件配合。为此,Google提供了NavigationView组件,将NavigationView与Drawerlayout配合使用,我们便可以轻松实现抽屉菜单。

NavigationView可分为两部分——头部布局和菜单点击项布局。由于NavigationView使用比较简单和呆板,我决定尝试FAQ的方式来介绍这个组件的使用。

FAQ

  1. 如何指定头布局和菜单点击项布局?
    答:可在XML布局里使用 app:headerLayout="@layout/layout文件" 来指定头布局,使用 app:menu="@menu/menu文件" 来指定菜单点击项;
    在Java代码里可以使用 inflateHeaderView(View view) 来指定头布局,使用 inflateMenu(int resId) 来指定菜单布局。
  2. 如何监听和处理菜单点击事件?
    NavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    //在此根据每个item的id处理相应的逻辑即可
                    return true;
                }
            });
  3. 如何改变菜单中图标的颜色?
    答: app:itemIconTint="@color/颜色"
  4. 如何让菜单中的图标颜色还原成原本真实的颜色?
    答:

    NavigationView.setItemIconTintList(null)

  5. 如何设置头部布局中的控件点击事件?
    答:首先,使用NavigationView .getHeaderView(0) 获得头部布局,然后使用该布局的findViewById()获取相应控件绑定点击事件即可。

以上是使用NavigationView通常会遇到的问题,如果你还有其他的问题,欢迎在评论里交流。如果你还是对它的使用觉得云里雾里,那么看我最后的代码吧~:blush:

将DrawerLayout延伸至状态栏

这是一个引起过广泛讨论的问题,有关于这方面的文章和问题也有很多,当然也有不止一种解决方案,我采用的解决思路是:

  1. 将状态栏设为透明
  2. 根据StatusBar的值为Toolbar设置相应的paddingTop
  3. 为作为根布局的DrawerLayout设置 setFitsSystemWindows(true)
  4. 为作为根布局的Drawerlayout设置

    setClipToPadding(false)

有关setFitsSystemWindows(true)和setClipToPadding(false)的知识点,可以看 Why would I want to fitsSystemWindows? ,这篇文章特地针对Drawerlayout讲得非常清楚,同时该文已经有人进行了翻译,链接也在此附上 中文翻译版

具体实现和效果请看下面的代码示例。

代码示例(二)

效果图(Android 4.4)

 Drawer 详解 ·Material Design Part 3 效果图(Android 7.0)

5.0以上都一样,就不全放了。

 Drawer 详解 ·Material Design Part 3 layout.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"
    tools:openDrawer="start">

    <include
        layout="@layout/content_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:background="@color/colorBlueGrey"
        app:headerLayout="@layout/nav_header_drawer_demo"
        app:itemBackground="@color/colorYellowLight"
        app:itemIconTint="@color/colorRed"
        app:itemTextColor="@color/colorGreen"
        app:menu="@menu/activity_drawer_demo_drawer" />

</android.support.v4.widget.DrawerLayout>


content_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="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar xmlns:app="http://schemas.android.com/apk/res-auto"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:minHeight="?actionBarSize"
            android:paddingTop="@dimen/status_bar_height"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

    </android.support.design.widget.AppBarLayout>
</LinearLayout>

这里的dimen根据v23来区分即可。



Activity.java

public class DrawerDemoActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    ActionBarDrawerToggle toggle;

    @BindView(R.id.toolbar)
    Toolbar toolbar;
    @BindView(R.id.nav_view)
    NavigationView navView;
    @BindView(R.id.drawer_layout)
    DrawerLayout drawerLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_drawer_demo);
        ButterKnife.bind(this);
        setSupportActionBar(toolbar);

        handlingStatusBar();
        toggleSettings();
        navView.setNavigationItemSelectedListener(this);

    }

    /**
     * 说明:DrawerLayout延伸至StatusBar并正常显示
     */
    private void handlingStatusBar() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            WindowManager.LayoutParams localLayoutParams = getWindow().getAttributes();
            localLayoutParams.flags = (WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS | localLayoutParams.flags);

            drawerLayout.setFitsSystemWindows(true);
            drawerLayout.setClipToPadding(false);
        }
    }



    /**
     * 说明:配置ActionBarDrawerToggle
     */
    private void toggleSettings() {
        toggle = new ActionBarDrawerToggle(
                this, drawerLayout, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
        drawerLayout.addDrawerListener(toggle);
        toggle.syncState();
    }


    @Override
    public void onBackPressed() {
        DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @SuppressWarnings("StatementWithEmptyBody")
    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.nav_camera) {

        } else if (id == R.id.nav_gallery) {

        } else if (id == R.id.nav_slideshow) {

        } else if (id == R.id.nav_manage) {

        } else if (id == R.id.nav_share) {

        } else if (id == R.id.nav_send) {

        }

        drawerLayout.closeDrawer(GravityCompat.START);
        return true;
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        // Sync the toggle state after onRestoreInstanceState has occurred.
        toggle.syncState();
    }

    @Override
    public void onConfiguration
Changed(Configuration newConfig) {
        super.onConfiguration
Changed(newConfig);
        toggle.onConfiguration
Changed(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 (toggle.onOptionsItemSelected(item)) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

几句额外的

从Bottom Navigation加入MD规范开始,我就看到过有关“抛弃Drawer”的说法,最近又看到了几篇这样的文章。其中不乏一些好的思考,但有些观点还是过于激进了,甚至有些人云亦云。这里想分享下自己的看法:

首先,我们在 设计产品的时候,做出的一切决定都应该基于产品本身 :当前页面的业务场景是什么,什么才是最符合需求的交互形式? 至于组件和界面只是产品的呈现手段和方式,最终目的都是为了给出一个最符合需求的产品。

再者,在学习和遵循Material Design时,除了知道怎么使用各种组件, 更应该理解这些组件真正的作用是什么,设计思想是什么 。只有这样,我们才能在设计APP时做出更加正确的决定。 一个符合Material Design的APP绝不会是由各种MD组件随意堆砌而成的。 至于说到抛弃Drawer,我认为更是完全没有必要,不然你看看Android 7.0的设置界面?

下一篇我会介绍有关 Bottom Navigation 的知识点,也算是个对比。希望看完这两篇后,能够帮助你在构思界面时做出更好的决定。

参考文章

Creating a Navigation Drawer | Android官方文档

DrawerLayout | Android官方文档

Why would I want to fitsSystemWindows?

http:// weixin.qq.com/r/yTgrM-D ESK03rbRQ923b (二维码自动识别)

微信公众号: BugDev「bugdev」

欢迎关注啦~

 
标签: 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 安卓中通知功能的具体实现
    安卓中通知功能的具体实现
    通知[Notification]是Android中比较有特色的功能,当某个应用程序希望给用户发出一些提示信息,而该应用程序又不在前台运行时,就可以借助通知实现。使用通知的步骤1、需要一个NotificationManager来获得NotificationManager manager = (NotificationManager
    02-05 安卓开发
  • Android view系统分析-setContentView
    Android view系统分析-setContentView
    第一天上班,列了一下今年要学习的东西。主要就是深入学习Android相关的系统源代码,夯实基础。对于学习Android系统源代码,也没什么大概,就从我们平常使用最基础的东西学起,也就是从view这个切入点开始学习Android的源码,在没分析源码之前,我们有的时候
    02-05 安卓开发
  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • Android NDK 层发起 HTTP 请求的问题及解决
    Android NDK 层发起 HTTP 请求的问题及解决
    前言新的一年,大家新年快乐~~鸡年大吉!本次给大家带来何老师的最新文章~虽然何老师还在过节,但依然放心不下广大开发者,在此佳节还未结束之际,给大家带来最新的技术分享~ 事件的起因不说了,总之是需要实现一个 NDK 层的网络请求。为了多端适用,还是选择
  • Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
    Android插件化(六): OpenAtlasの改写aapt以防
    引言Android应用程序的编译中,负责资源打包的是aapt,如果不对打包后的资源ID进行控制,就会导致插件中的资源ID冲突。所以,我们需要改写aapt的源码,以达到通过某种方式传递资源ID的Package ID,通过aapt打包时获取到这个Package ID并且应用才插件资源的命名
    02-05 安卓开发
  • Android架构(一)MVP架构在Android中的实践
    Android架构(一)MVP架构在Android中的实践
    为什么要重视程序的架构设计 对程序进行架构设计的原因,归根结底是为了 提高生产力 。通过设计是程序模块化,做到模块内部的 高聚合 和模块之间的 低耦合 (如依赖注入就是低耦合的集中体现)。 这样做的好处是使得程序开发过程中,开发人员主需要专注于一点,
    02-05 安卓开发
  • 安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件
    安卓逆向系列教程 4.2 分析锁机软件 作者: 飞龙 这个教程中我们要分析一个锁机软件。像这种软件都比较简单,完全可以顺着入口看下去,但我这里还是用关键点来定位。首先这个软件的截图是这样,进入这个界面之后,除非退出模拟器,否则没办法回到桌面。上面那
    02-05 安卓开发
  • Android插件化(二):OpenAtlas插件安装过程分析
    Android插件化(二):OpenAtlas插件安装过程分析
    在前一篇博客 Android插件化(一):OpenAtlas架构以及实现原理概要 中,我们对应Android插件化存在的问题,实现原理,以及目前的实现方案进行了简单的叙述。从这篇开始,我们要深入到OpenAtlas的源码中进行插件安装过程的分析。 插件的安装分为3种:宿主启动时立
    02-05 安卓开发
  • [译] Android API 指南
    [译] Android API 指南
    众所周知,Android开发者有中文网站了,API 指南一眼看去最左侧的菜单都是中文,然而点进去内容还是很多是英文,并没有全部翻译,我这里整理了API 指南的目录,便于查看,如果之前还没有通读,现在可以好好看一遍。注意,如果标题带有英文,说明官方还没有翻
  • 使用FileProvider解决file:// URI引起的FileUriExposedException
    使用FileProvider解决file:// URI引起的FileUri
    问题以下是一段简单的代码,它调用系统的相机app来拍摄照片:void takePhoto(String cameraPhotoPath) {File cameraPhoto = new File(cameraPhotoPath);Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);takePhotoIntent.putExtra(Medi
    02-05 安卓开发
点击排行