【 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