【 Android 】Android Material Desi

作者: Tyhoo_Wu | 来源:发表于2017-06-08 00:26 被阅读136次

    现在,越来越多的项目明确要求有 Material Design 意识。一套好的模板可以帮助我们减少开发难度和时间,有更多的时间去解决客户提出的难点。学会这一套示例代码,对你很有帮助!

    本示例项目完全遵循 Material Design 思想。方便开发者,也可以让初学者很容易就上手!
    示例 Gif :

    Material Design Sample.gif
    1. 项目遵循:不使用第三方库,所有导入的库都是安卓原生的!
    2. 项目使用:一个 Activity + 多个 Fragment 的模式。底部导航栏使用 BottomNavigationView ,而非 LinearLayout + TextView 或 Tab 切换等。
    3. 项目实现:主题切换。

    导入 Material Design 所需的原生库:

    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:support-v4:25.3.1'
    compile 'com.android.support:design:25.3.1'
    compile 'com.android.support:preference-v14:25.3.1'
    

    从布局文件说起吧,Material Design 样式,而非简单的 LinearLayout 或 RelativeLayout
    主页面布局代码采用 DrawerLayout + CoordinatorLayout + AppBarLayout + Toolbar + FrameLayout
    我将这部分分成几个小部分进行书写,目的是每一块都清晰,也不会导致因为控件元素过多导致布局代码很长,不宜读,不宜管理。

    <?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:context=".MainActivity"
        tools:openDrawer="start">
    
        <include
            layout="@layout/activity_main_content"
            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/layout_background"
            android:fitsSystemWindows="true"
            app:headerLayout="@layout/nav_header_main"
            app:itemIconTint="@drawable/nav_item_color"
            app:itemTextColor="@drawable/nav_item_color"
            app:menu="@menu/menu_main_drawer" />
    
    </android.support.v4.widget.DrawerLayout>
    
    <?xml version="1.0" encoding="utf-8"?>
    <android.support.design.widget.CoordinatorLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="false"
        tools:context=".MainActivity">
    
        <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
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                android:background="?attr/colorPrimary"
                app:popupTheme="@style/AppTheme.PopupOverlay" />
    
        </android.support.design.widget.AppBarLayout>
    
        <include layout="@layout/activity_main_content_layout" />
    
    </android.support.design.widget.CoordinatorLayout>
    
    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout 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/content_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/layout_background"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:context=".MainActivity"
        tools:showIn="@layout/activity_main_content" />
    

    细心的童鞋会发现,我在主页面布局里面设置了 NavigationView ,即:侧边栏。
    我这里实现的思路是拼接式,即:侧边栏头部 + 侧边栏内容(Menu实现)
    侧边栏头部:

    <?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="@color/colorPrimary"
        android:gravity="bottom"
        android:orientation="vertical"
        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" />
    

    侧边栏内容:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android">
    
        <group
            android:id="@+id/main_group"
            android:checkableBehavior="single">
    
            <item
                android:id="@+id/nav_home"
                android:icon="@mipmap/ic_launcher_round"
                android:title="Home" />
            <item
                android:id="@+id/nav_page_two"
                android:icon="@mipmap/ic_launcher_round"
                android:title="Page Two" />
            <item
                android:id="@+id/nav_page_three"
                android:icon="@mipmap/ic_launcher_round"
                android:title="Page Three" />
    
        </group>
    
        <group
            android:id="@+id/second_group"
            android:checkableBehavior="none">
    
            <item
                android:id="@+id/nav_switch_theme"
                android:icon="@mipmap/ic_launcher_round"
                android:title="Switch theme" />
            <item
                android:id="@+id/nav_settings"
                android:icon="@mipmap/ic_launcher_round"
                android:title="Settings" />
            <item
                android:id="@+id/nav_about"
                android:icon="@mipmap/ic_launcher_round"
                android:title="About" />
    
        </group>
    
    </menu>
    

    布局代码就讲解到这里,如果还有不明白的地方,请在评论栏留言,谢谢。

    主类(核心代码):
    MainActivity 继承 AppCompatActivity , 实现接口 OnNavigationItemSelectedListener 。

    1. 在 onCreate() 里面
      ① 首先要初始化控件:
    private void initViews() {
            mToolbar = (Toolbar) findViewById(R.id.toolbar);
            setSupportActionBar(mToolbar);
    
            mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
            ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                    this,
                    mDrawerLayout,
                    mToolbar,
                    R.string.navigation_drawer_open,
                    R.string.navigation_drawer_close
            );
            mDrawerLayout.addDrawerListener(toggle);
            toggle.syncState();
    
            mNavigationView = (NavigationView) findViewById(R.id.nav_view);
            mNavigationView.setNavigationItemSelectedListener(this);
        }
    

    ② 初始化 Fragment , 这里我们创建三个简单的 Fragment 。

    if (savedInstanceState != null) {
        mHomeFragment =
                (HomeFragment) getSupportFragmentManager().
                        getFragment(savedInstanceState, "HomeFragment");
        mPageTwoFragment =
                (PageTwoFragment) getSupportFragmentManager().
                        getFragment(savedInstanceState, "PageTwoFragment");
        mPageThreeFragment =
                (PageThreeFragment) getSupportFragmentManager().
                        getFragment(savedInstanceState, "PageThreeFragment");
        selectedNavItem = savedInstanceState.getInt(CURRENT_NAV_ITEM);
    } else {
        mHomeFragment =
                (HomeFragment) getSupportFragmentManager().
                        findFragmentById(R.id.content_main);
        if (mHomeFragment == null) {
            mHomeFragment = HomeFragment.newInstance();
        }
        mPageTwoFragment =
                (PageTwoFragment) getSupportFragmentManager().
                        findFragmentById(R.id.content_main);
        if (mPageTwoFragment == null) {
            mPageTwoFragment = PageTwoFragment.newInstance();
        }
        mPageThreeFragment =
                (PageThreeFragment) getSupportFragmentManager().
                        findFragmentById(R.id.content_main);
        if (mPageThreeFragment == null) {
            mPageThreeFragment = PageThreeFragment.newInstance();
        }
    }
    

    ③ 显示需要显示的 Fragment 。

    if (!mHomeFragment.isAdded()) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content_main, mHomeFragment, "HomeFragment")
                .commit();
    }
    if (!mPageTwoFragment.isAdded()) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content_main, mPageTwoFragment, "PageTwoFragment")
                .commit();
    }
    if (!mPageThreeFragment.isAdded()) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.content_main, mPageThreeFragment, "PageThreeFragment")
                .commit();
    }
    

    ④ 显示默认的 Fragment 。

    if (selectedNavItem == 0) {
        showHomeFragment();
    } else if (selectedNavItem == 1) {
        showPageTwoFragment();
    } else if (selectedNavItem == 2) {
        showPageThreeFragment();
    }
    
    1. 控件都初始化完毕之后,我们来设置侧边栏
    @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {}
    

    上面的代码是从接口 OnNavigationItemSelectedListener 里拿到的,但是我看到网上有很多人问这段代码怎么来的,回答的五花八门,我觉得统统是误导!
    在方法里面进行侧边栏的设置,我们的侧边栏有 Home 、 Page Two 、 Page Three 、 Switch Theme 、 Settings 、About 六个部分。
    所以代码实现:

    int id = item.getItemId();
    if (id == R.id.nav_home) {
        showHomeFragment();
    } else if (id == R.id.nav_page_two) {
        showPageTwoFragment();
    } else if (id == R.id.nav_page_three) {
        showPageThreeFragment();
    } else if (id == R.id.nav_switch_theme) {
        mDrawerLayout.addDrawerListener(new DrawerLayout.DrawerListener() {
            @Override
            public void onDrawerSlide(View drawerView, float slideOffset) {
            }
    
            @Override
            public void onDrawerOpened(View drawerView) {
            }
    
            @Override
            public void onDrawerClosed(View drawerView) {
            }
    
            @Override
            public void onDrawerStateChanged(int newState) {
    
            }
        });
    } else if (id == R.id.nav_settings) {
        Intent intent = new Intent(MainActivity.this, PrefsActivity.class);
        intent.putExtra(PrefsActivity.EXTRA_FLAG, PrefsActivity.FLAG_SETTINGS);
        startActivity(intent);
    } else if (id == R.id.nav_about) {
        Intent intent = new Intent(MainActivity.this, PrefsActivity.class);
        intent.putExtra(PrefsActivity.EXTRA_FLAG, PrefsActivity.FLAG_ABOUT);
        startActivity(intent);
    }
    mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
    mDrawerLayout.closeDrawer(GravityCompat.START);
    return true;
    
    1. 显示所要显示的 Fragment
      以一个 Fragment 的显示为例,其他的大同小异:
    private void showHomeFragment() {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.show(mHomeFragment);
        fragmentTransaction.hide(mPageTwoFragment);
        fragmentTransaction.hide(mPageThreeFragment);
        fragmentTransaction.commit();
    
        mToolbar.setTitle(R.string.app_name);
        mNavigationView.setCheckedItem(R.id.nav_home);
    }
    

    主题切换
    自定义 Applciation 继承 Application

    public class App extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            if (PreferenceManager.getDefaultSharedPreferences(
                    getApplicationContext()).getBoolean(Utils.KEY_NIGHT_MODE, false)) {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
            } else {
                AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
            }
        }
    
    }
    

    并在 onDrawerClosed() 里面实现切换主题并存储到 SharedPreferences 操作。

    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
    if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK)
            == Configuration.UI_MODE_NIGHT_YES) {
        sp.edit().putBoolean(Utils.KEY_NIGHT_MODE, false).apply();
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
    } else {
        sp.edit().putBoolean(Utils.KEY_NIGHT_MODE, true).apply();
        AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
    }
    getWindow().setWindowAnimations(R.style.WindowAnimationFadeInOut);
    recreate();
    

    当然好的 Material Design 离不开 style 的设置:

    <resources>
    
        <!-- Base application theme. -->
        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:windowContentTransitions">true</item>
            <item name="android:windowAllowEnterTransitionOverlap">false</item>
            <item name="android:windowAllowReturnTransitionOverlap">false</item>
        </style>
    
        <style name="AppTheme.NoActionBar">
            <item name="windowActionBar">false</item>
            <item name="windowNoTitle">true</item>
            <item name="android:windowDrawsSystemBarBackgrounds">true</item>
            <item name="android:statusBarColor">@android:color/transparent</item>
            <item name="preferenceTheme">@style/MaterialPreferenceTheme</item>
        </style>
    
        <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />
    
        <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
        
        <style name="MaterialPreferenceTheme" parent="PreferenceThemeOverlay.v14.Material">
            <item name="android:fadeScrollbars">true</item>
            <item name="android:scrollbars">vertical</item>
            <item name="android:scrollbarFadeDuration">1</item>
            <item name="android:background">@color/layout_background</item>
            <item name="android:textColorPrimary">@color/colorPrimaryText</item>
            <item name="android:textColorSecondary">@color/colorSecondaryText</item>
            <item name="android:colorAccent">@color/colorAccent</item>
        </style>
    
        <style name="WindowAnimationFadeInOut">
            <item name="android:windowEnterAnimation">@anim/fade_in</item>
            <item name="android:windowExitAnimation">@anim/fade_out</item>
        </style>
    
    </resources>
    

    整体的思想都在代码中体现出来了。如果想进一步学习,研究每一处怎么使用,欢迎下载示例代码学习。示例代码已上传至 GitHub ,欢迎 Star 、Fork !如有不明确的地方,或是我写的你认为有更好的解决办法的地方,欢迎在评论区留言,或者到 GitHub 上留言。开源的世界,共同进步!

    示例代码:https://github.com/cnwutianhao/MaterialDesignBaseMould

    相关文章

      网友评论

        本文标题:【 Android 】Android Material Desi

        本文链接:https://www.haomeiwen.com/subject/xytofxtx.html