Android 进阶之路Android开发程序员

【 Android 】优雅的实现 Navigation 栏目切换

2017-05-06  本文已影响484人  Tyhoo_Wu

做安卓开发的都应该将 Google 在gitHub上共享的代码当作参考Base。

提到 Navigation 想必都不陌生,利用 DrawerLayout + NavigationView 可以方便开发者实现侧边栏抽屉效果。在加上 Material Design 模式可以增加用户体验。

参考示例图:


1.gif

这个示例给大家的意义感觉一定是,我做过类似的,不就是一个 Activity 里面嵌套几个
Fragment 实现 Item 切换嘛。然而实际不是这样,本示例不同于网上其他的参考代码,基于 Google 在 GitHub 上面的 Sample 代码,整个工程全部使用 Activity 实现切换。

示例代码已上传至GitHub:https://github.com/cnwutianhao/GoogleNavigation
欢迎 Start、Fork!

好了,话说了一堆,开始上干货!

1.导入必要的库

compile 'com.android.support:design:25.3.1'

</br>
2.设置主题(遵循4.4和5.0两版的标题栏透明化\沉浸式效果)
API 21之前的style

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- Customize your theme here. -->
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

API 21之后的style:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
    <item name="android:statusBarColor">@android:color/transparent</item>
    <item name="android:windowDrawsSystemBarBackgrounds">true</item>
    <item name="android:windowTranslucentStatus">true</item>
    <item name="android:windowContentTransitions">true</item>
</style>

</br>
3.布局实现(DrawerLayout做最外层),这里我只举一个例子,其他页面大同小异

<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:context=".FirstActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include
            android:id="@+id/toolbar_container"
            layout="@layout/customize_toolbar" />

        <FrameLayout
            android:id="@+id/container"
            android:layout_width="match_parent"
            android:background="@android:color/holo_blue_light"
            android:layout_height="match_parent"
            android:layout_below="@+id/toolbar_container" />

    </RelativeLayout>

    <android.support.design.widget.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/menu_drawer" />

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

自定义Toolbar

<android.support.design.widget.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

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

自定义 NavigationView (标题头 + Item)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="192dp"
    android:background="?attr/colorPrimaryDark"
    android:gravity="bottom"
    android:orientation="vertical"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:text="@string/app_name"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1" />

</LinearLayout>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <group android:checkableBehavior="single">
        <item
            android:id="@+id/navigation_first"
            android:checked="true"
            android:icon="@mipmap/ic_launcher_round"
            android:title="FirstPage" />
        <item
            android:id="@+id/navigation_second"
            android:icon="@mipmap/ic_launcher_round"
            android:title="SecondPage" />
        <item
            android:id="@+id/navigation_third"
            android:icon="@mipmap/ic_launcher_round"
            android:title="ThirdPage" />
    </group>
</menu>

</br>
4.自定义 BaseAcitivty(代码共通化处理),基于 Google Sample 。

public class BaseActivity extends AppCompatActivity {

    private static final String TAG = "BaseActivity";

    private Toolbar mToolbar;

    /**
     * ActionBarDrawerToggle  是 DrawerLayout.DrawerListener 实现。和 NavigationDrawer 搭配使用,
     * 推荐用这个方法,符合 Material Design规范。
     */
    private ActionBarDrawerToggle mDrawerToggle;

    private DrawerLayout mDrawerLayout;

    private boolean mToolbarInitialized;

    private int mItemToOpenWhenDrawerCloses = -1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG, "Activity onCreate");
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (!mToolbarInitialized) {
            throw new IllegalStateException("You must run super.initializeToolbar at " +
                    "the end of your onCreate method");
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        // Whenever the fragment back stack changes, we may need to update the
        // action bar toggle: only top level screens show the hamburger-like icon, inner
        // screens - either Activities or fragments - show the "Up" icon instead.
        getFragmentManager().addOnBackStackChangedListener(backStackChangedListener);
    }

    @Override
    public void onPause() {
        super.onPause();
        getFragmentManager().removeOnBackStackChangedListener(backStackChangedListener);
    }

    /**
     * onPostCreate() + onConfigurationChanged() 作用
     * 1.改变android.R.id.home返回图标
     * 2.Drawer拉出、隐藏,带有android.R.id.home动画效果。
     * 3.监听Drawer拉出、隐藏。
     */
    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        if (mDrawerToggle != null) {
            mDrawerToggle.syncState();
        }
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        if (mDrawerToggle != null) {
            mDrawerToggle.onConfigurationChanged(newConfig);
        }
    }

    /**
     * 加上这句使左上角 Menu 键好用
     */
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (mDrawerToggle != null && mDrawerToggle.onOptionsItemSelected(item)) {
            return true;
        }
        // If not handled by drawerToggle, home needs to be handled by returning to previous
        if (item != null && item.getItemId() == android.R.id.home) {
            onBackPressed();
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onBackPressed() {
        // If the drawer is open, back will close it
        if (mDrawerLayout != null && mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
            mDrawerLayout.closeDrawers();
            return;
        }
        // Otherwise, it may return to the previous fragment stack
        FragmentManager fragmentManager = getFragmentManager();
        if (fragmentManager.getBackStackEntryCount() > 0) {
            fragmentManager.popBackStack();
        } else {
            // Lastly, it will rely on the system behavior for back
            super.onBackPressed();
        }
    }

    private final FragmentManager.OnBackStackChangedListener backStackChangedListener =
            new FragmentManager.OnBackStackChangedListener() {
                @Override
                public void onBackStackChanged() {
                    updateDrawerToggle();
                }
            };

    protected void updateDrawerToggle() {
        if (mDrawerToggle == null) {
            return;
        }
        boolean isRoot = getFragmentManager().getBackStackEntryCount() == 0;
        mDrawerToggle.setDrawerIndicatorEnabled(isRoot);
        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayShowHomeEnabled(!isRoot);
            getSupportActionBar().setDisplayHomeAsUpEnabled(!isRoot);
            getSupportActionBar().setHomeButtonEnabled(!isRoot);
        }
        if (isRoot) {
            mDrawerToggle.syncState();
        }
    }

    /**
     *对外暴露的方法,自定义Toolbar 结合 ActionBarDrawerToggle ,来封装抽屉式。
     */
    protected void initializeToolbar() {
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        if (mToolbar == null) {
            throw new IllegalStateException("Layout is required to include a Toolbar with id " +
                    "'toolbar'");
        }

//        mToolbar.inflateMenu(R.menu.main);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        if (mDrawerLayout != null) {
            NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
            if (navigationView == null) {
                throw new IllegalStateException("Layout requires a NavigationView " +
                        "with id 'nav_view'");
            }

            // Create an ActionBarDrawerToggle that will handle opening/closing of the drawer:
            mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout,
                    mToolbar, R.string.open_content_drawer, R.string.close_content_drawer);
            mDrawerLayout.setDrawerListener(drawerListener);
            populateDrawerItems(navigationView);
            setSupportActionBar(mToolbar);
            updateDrawerToggle();
        } else {
            setSupportActionBar(mToolbar);
        }

        mToolbarInitialized = true;
    }

    private final DrawerLayout.DrawerListener drawerListener = new DrawerLayout.DrawerListener() {
        @Override
        public void onDrawerSlide(View drawerView, float slideOffset) {
            if (mDrawerToggle != null) mDrawerToggle.onDrawerSlide(drawerView, slideOffset);
        }

        @Override
        public void onDrawerOpened(View drawerView) {
            if (mDrawerToggle != null) mDrawerToggle.onDrawerOpened(drawerView);
            if (getSupportActionBar() != null) getSupportActionBar().setTitle(R.string.app_name);
        }

        @Override
        public void onDrawerClosed(View drawerView) {
            if (mDrawerToggle != null) mDrawerToggle.onDrawerClosed(drawerView);
            if (mItemToOpenWhenDrawerCloses >= 0) {
                Bundle extras = ActivityOptions.makeCustomAnimation(
                        BaseActivity.this, R.anim.fade_in, R.anim.fade_out).toBundle();
                Class activityClass = null;
                switch (mItemToOpenWhenDrawerCloses) {
                    case R.id.navigation_first:
                        activityClass = FirstActivity.class;
                        break;
                    case R.id.navigation_second:
                        activityClass = SecondActivity.class;
                        break;
                    case R.id.navigation_third:
                        activityClass = ThirdActivity.class;
                        break;
                }
                if (activityClass != null) {
                    startActivity(new Intent(BaseActivity.this, activityClass), extras);
                    finish();
                }
            }
        }

        @Override
        public void onDrawerStateChanged(int newState) {
            if (mDrawerToggle != null) mDrawerToggle.onDrawerStateChanged(newState);
        }
    };

    private void populateDrawerItems(NavigationView navigationView) {
        navigationView.setNavigationItemSelectedListener(
                new NavigationView.OnNavigationItemSelectedListener() {
                    @Override
                    public boolean onNavigationItemSelected(MenuItem menuItem) {
                        menuItem.setChecked(true);
                        mItemToOpenWhenDrawerCloses = menuItem.getItemId();
                        mDrawerLayout.closeDrawers();
                        return true;
                    }
                });
        if (FirstActivity.class.isAssignableFrom(getClass())) {
            navigationView.setCheckedItem(R.id.navigation_first);
        } else if (SecondActivity.class.isAssignableFrom(getClass())) {
            navigationView.setCheckedItem(R.id.navigation_second);
        } else if (ThirdActivity.class.isAssignableFrom(getClass())) {
            navigationView.setCheckedItem(R.id.navigation_third);
        }
    }
}

</br>
5.每一个 Activity 继承 BaseActivity ,并实现里面的 initializeToolbar() 方法。

public class FirstActivity extends BaseActivity {

    private static final String TAG = "MainActivity";

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

        initializeToolbar();
    }

}

</br>
优雅的抽屉栏Item切换就实现了。
</br>
将本片文章进行改造,使用 Java 8 + Data-Binding + RxAndroid 进行编写,
请点击 优雅的实现Navigation栏目切换(升级版)
</br>
实例:
Retrofit2 + OkHttp3 + Gson + Glide + Butter knife + Material Design打造的天气查询App
https://github.com/cnwutianhao/ForecastGlobalWeather

上一篇 下一篇

猜你喜欢

热点阅读