Android 抽屉布局 + 底部Tab + 自定义 ToolBar

Android 抽屉布局 + 底部Tab + 自定义 ToolBar

Android App 市面上流行的布局往往是如下图所示的底部多Tab页面 + 自定义ToolBar

抽屉布局
好的接下来就为大家展现代码如何实现其效果!

首先需要导入的是design库,可以是:androidx 的引入,或support库的引入

//androidx 引入design库的方式
implementation 'com.google.android.material:material:1.0.0-rc01'

//或者support 引入design库的方式
//implementation ‘com.android.support:design:28.0.0’

首先MainActivity 的 Layout 布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:id="@+id/drawer_layout"
    tools:context=".ui.MainActivity">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include layout="@layout/toolbar"/>
        <include layout="@layout/container"/>

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/navigation_bottom"
            style="@style/Widget.Design.BottomNavigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="bottom"
            android:layout_alignParentBottom="true"
            android:background="@color/viewBackground"
            app:itemIconTint="@drawable/nav_item_color_selector"
            app:itemTextColor="@drawable/nav_item_color_selector"
            app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"
            app:menu="@menu/navigation_bottom"
            />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_gravity="start"
        android:id="@+id/nav_view"
        app:headerLayout="@layout/nav_header"
        app:menu="@menu/menu_drawer"
        android:layout_height="match_parent"
        />

</androidx.drawerlayout.widget.DrawerLayout>

最外层用DrawerLayout 给包起来 ,然后在用CoordinatorLayout(Design 库中的一个可伸展的布局,内部嵌套一个toolbar 和 Fragment 需展示的container 页面),然后在该布局中的底部添加一个BottomNavigationView 的底部多Tab的控件(由google 官方自己封装好的一个多Tab选择切换控件),然后是NavigationView(抽屉布局的具体layout)


toolbar.xml 如下:

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.appbar.AppBarLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.appcompat.widget.AppCompatImageView
            android:id="@+id/iv_close"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"/>

        <TextView
            android:id="@+id/tv_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:visibility="gone"/>

    </androidx.appcompat.widget.Toolbar>

</com.google.android.material.appbar.AppBarLayout>

外部由AppBarLayout 包含,内部是Toolbar,然后是toolbar上的按钮和标题。

以及Toolbar顶部的按钮自定义style.xml :

<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="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
        <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
    </style>

    <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
        <item name="color">@color/White</item>
        <item name="android:textColor">@color/White</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />
</resources>


container.xml

<?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"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/container"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">
</FrameLayout>

就是一个帧布局,用来做Fragment 显示内容的载体


底部BottomNavigationView 的menu 布局,navigation_bottom.xml :需将其放入menu 目录下的

扫描二维码关注公众号,回复: 8812337 查看本文章
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/home" android:icon="@drawable/ic_home_black_24dp" android:title="@string/home"/>
    <item android:id="@+id/wechat" android:icon="@drawable/ic_wechat" android:title="@string/wechat"/>
    <item android:id="@+id/project" android:icon="@drawable/ic_project" android:title="@string/project"/>
    <item android:id="@+id/navigation" android:icon="@drawable/ic_navigation" android:title="@string/navigation"/>
    <item android:id="@+id/knowledge_tree" android:icon="@drawable/ic_dashboard_black_24dp" android:title="@string/konwledge_tree"/>

</menu>

展示其中一个item的drawable ,如ic_wechat,xml ,是svg 矢量图

<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp"
        android:viewportHeight="1024" android:viewportWidth="1024" android:width="24dp">
    <path android:fillColor="#FF000000"
          android:pathData="M860.61,794.82c62.71,-46.85 98.37,-114.12 98.37,-186.61 0,-124.93 -107.69,-228.43 -246.7,-244.21C694.08,220.94 555.25,113.23 388.67,113.23c-179.53,0 -325.59,126.53 -325.59,282.05 0,83.86 41.8,161.66 115.2,215.37 -4.01,17.58 -18.1,54.56 -33.92,88.1 -5.65,11.97 -2.6,26.24 7.44,34.86 5.48,4.71 12.31,7.1 19.17,7.1 5.7,0 11.42,-1.66 16.41,-5.01 43.1,-28.99 97.12,-61.83 114.85,-68.41 28.04,6.66 57.09,10.04 86.45,10.04 4.74,0 9.49,-0.11 14.23,-0.28 34.39,102.41 143.48,177.46 272.39,177.46 25.08,0 49.91,-2.83 73.89,-8.43 15.94,6.41 60.87,33.81 96.59,57.84 4.98,3.35 10.71,5.01 16.41,5.01 6.86,0 13.69,-2.39 19.17,-7.1 10.04,-8.63 13.08,-22.9 7.43,-34.87C876.13,840.12 864.62,810.35 860.61,794.82zM388.67,618.51c-26.48,0 -52.62,-3.24 -77.7,-9.64 -8.7,-2.22 -20.74,-5.28 -78.95,28.73 8.8,-29.17 10.53,-54.35 -9.79,-67.96 -63.76,-42.72 -100.32,-106.27 -100.32,-174.35 0,-123.09 119.67,-223.22 266.76,-223.22 133.45,0 244.9,81.28 263.93,190.63 -145.85,10.07 -261,116.33 -261,245.52 0,3.44 0.1,6.86 0.27,10.26C390.81,618.49 389.74,618.51 388.67,618.51zM815.87,754.43c-17.15,11.49 -18.16,30.71 -12.75,53.2 -31.21,-17.53 -45.37,-21.32 -53.83,-21.32 -3.47,0 -5.98,0.64 -8.32,1.24 -21.19,5.41 -43.28,8.14 -65.67,8.14 -123.99,0 -224.85,-84.1 -224.85,-187.48 0,-103.37 100.87,-187.47 224.85,-187.47s224.85,84.1 224.85,187.47C900.14,665.25 869.42,718.54 815.87,754.43z"/>
    <path android:fillColor="#FF000000"
          android:pathData="M246.39,322.56a39.96,36.19 0,1 0,81.78 0,39.96 36.19,0 1,0 -81.78,0Z"/>
    <path android:fillColor="#FF000000"
          android:pathData="M458.88,322.56a39.96,36.19 0,1 0,81.78 0,39.96 36.19,0 1,0 -81.78,0Z"/>
    <path android:fillColor="#FF000000"
          android:pathData="M566.16,530.37a32.07,29.04 0,1 0,65.63 0,32.07 29.04,0 1,0 -65.63,0Z"/>
    <path android:fillColor="#FF000000"
          android:pathData="M729.48,530.37a32.07,29.04 0,1 0,65.63 0,32.07 29.04,0 1,0 -65.63,0Z"/>
</vector>

以及点击底部Item的selector,nav_item_color_selector.xml :

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:color="@color/colorPrimary" android:state_checked="true"/>
    <item android:color="@color/textColorSecondary" android:state_checked="false"/>
</selector>

最后是抽屉布局中nav_header.xml : 就是上图中抽屉布局上部的图片区域

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="190dp"
    android:background="?attr/colorPrimaryDark"
    android:gravity="bottom"
    android:orientation="vertical"
    android:padding="16dp"
    android:theme="@style/ThemeOverlay.AppCompat.Dark">

    <ImageView
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center_horizontal"
        tools:text="名字"
        android:textAppearance="@style/TextAppearance.AppCompat.Body1"/>

</FrameLayout>

以及其底部的item菜单,menu_drawer.xml ,也是需要放入menu 目录中的:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:id="@+id/favorites" android:title="@string/favorites"></item>
    <item android:id="@+id/todo" android:title="@string/todo"></item>
    <item android:id="@+id/night_mode" android:title="@string/night_mode"></item>
    <item android:id="@+id/setting" android:title="@string/setting"></item>
    <item android:id="@+id/logout" android:title="@string/logout"></item>
    <item android:id="@+id/about" android:title="@string/about"></item>

</menu>

还有顶部的扫一扫按钮和查询按钮的布局文件,也是一个menu 文件,需放入menu目录下,menu_activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item android:id="@+id/scan"
        android:icon="@drawable/ic_scan"
        android:title="@string/scan"
        app:showAsAction="always|collapseActionView">
    </item>

    <item android:id="@+id/search"
        android:icon="@drawable/ic_search"
        android:title="@string/search"
        app:showAsAction="always|collapseActionView">

    </item>
</menu>

好了,讲完了其layout,接下来讲解其MainActivity.kt 其代码逻辑 , 下述代码由kotlin,所写,java的会日后再补上。MainActivity.kt 代码如下:

package com.cx.icxwanandroid.ui

import android.content.Context
import android.content.Intent
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.view.GravityCompat
import androidx.fragment.app.Fragment
import com.cx.icxwanandroid.R
import com.cx.icxwanandroid.base.BaseActivity
import com.cx.icxwanandroid.ui.fragment.*
import com.cx.icxwanandroid.utils.ToastUtils
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.bottomnavigation.LabelVisibilityMode.LABEL_VISIBILITY_LABELED

import com.google.android.material.navigation.NavigationView

import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.toolbar.*
import java.lang.Exception

class MainActivity : BaseActivity() {
    private var mCurrentFragment : Fragment ?= null
    private var index : Int = 0
    private var fragmentTag : String ?= null

	//每个Fragment 的Tag 
    private val fragmentNames = arrayOf(
        HomeFragment::class.java.name,
        WeChatFragment::class.java.name,
        ProjectFragment::class.java.name,
        NavigationFragment::class.java.name,
        KnowledgeTreeFragment::class.java.name
    )

    //底部的tab的Title
    private val bottomTitles = arrayOf(
        R.string.home , R.string.wechat , R.string.project , R.string.navigation , R.string.konwledge_tree
    )

    override var layoutId: Int = R.layout.activity_main

    override fun initData() {
        //设置toolBar
        setSupportActionBar(toolbar)
        //初始化抽屉布局
        initDrawerLayout()
        //初始化抽屉布局中的item 的点击事件
        initNav()
        //设置底部Tab的点击事件
        initNavBottom()
        //设置底部的切换Tab 导致切换Fragment的逻辑
        bottomNav()

    }

    override fun subscibeUi() {

    }

    //初始化整个抽屉的布局,并且设置抽屉按钮开启和关闭的点击事件
    private fun initDrawerLayout(){
        drawer_layout.run {
            val toggle = ActionBarDrawerToggle(
                this@MainActivity,
                this,
                toolbar,
                R.string.app_name,
                R.string.app_name
            )

            addDrawerListener(toggle)
            toggle.syncState()
        }

    }

	//设置抽屉item 的点击事件
    private fun initNav(){
        val navListener = NavigationView.OnNavigationItemSelectedListener{
            when(it.itemId){
                R.id.favorites -> {

                }
                R.id.todo -> {

                }
                R.id.night_mode -> {

                }
                R.id.setting -> {

                }
                R.id.logout -> {

                }
                R.id.about -> {

                }
            }
            drawer_layout.closeDrawer(GravityCompat.START)
            true
        }

        nav_view.run {
            setNavigationItemSelectedListener(navListener)
        }
    }

	//初始化底部Tab的点击事件
    private fun initNavBottom(){
        val mOnNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener {
            item ->
            when(item.itemId){
                R.id.home -> index = 0
                R.id.wechat -> index = 1
                R.id.project -> index = 2
                R.id.navigation -> index = 3
                R.id.knowledge_tree -> index = 4
            }
            bottomNav()
            true
        }
        navigation_bottom.run {
            labelVisibilityMode = LABEL_VISIBILITY_LABELED
            setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener)
        }
    }

    private fun bottomNav(){
        toolbar.title = getString(
            if(index == 0){
                R.string.app_name
            }else{
                bottomTitles[index]
            }
        )
        //设置toolBar 的Title颜色
        toolbar.setTitleTextColor(Color.parseColor("#ffffff"))
        fragmentTag = fragmentNames[index]
        val fragment = getFragmentByTag(fragmentTag!!)
        showFragment(mCurrentFragment , fragment , fragmentTag!!)
    }


    //根据FragmentTag 得到 Fragment
    private fun getFragmentByTag(name : String) : Fragment{
        var fragment = supportFragmentManager.findFragmentByTag(name)
        if(fragment != null){
            return fragment
        } else {
            try {
                // 得到Fragment的实例
                fragment = Class.forName(name).newInstance() as Fragment
            }catch (e : Exception){
                fragment = HomeFragment()
            }
        }
        return fragment!!
    }

    //显示Fragment
    private fun showFragment(from : Fragment ? , to : Fragment , tag : String){
        val transaction = supportFragmentManager.beginTransaction()
        if(from == null){
            if(to.isAdded){
                transaction.show(to)
            }else{
                transaction.add(R.id.container , to , tag)
            }
        }else{
            if(to.isAdded){
                transaction.hide(from).show(to)
            }else{
                transaction.hide(from).add(R.id.container , to , tag)
            }
        }
        transaction.commit()
        mCurrentFragment = to
    }

//初始化顶部的菜单,有扫一扫和查询按钮
    override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.menu_activity_main , menu)
        return super.onCreateOptionsMenu(menu)
    }

//顶部扫一扫和查询的点击事件
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when(item.itemId){
            R.id.scan -> {
                ToastUtils.show("scan")
                return true
            }
            R.id.search -> {
                ToastUtils.show("search")
                return true
            }

        }
        return super.onOptionsItemSelected(item)
    }
}

BaseActivity.kt 代码如下:

package com.cx.icxwanandroid.base

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

abstract class BaseActivity : AppCompatActivity() {

    protected abstract var layoutId : Int

//    protected var multipleStatusView : MultipleStatusView ?= null

    protected abstract fun initData()

    protected abstract fun subscibeUi()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(layoutId)
        initData()
        subscibeUi()
    }
}

BaseFragment 代码如下:

package com.cx.icxwanandroid.base

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment

abstract class BaseFragment : Fragment(){

    protected abstract var layoutId : Int

    protected abstract fun initData()

    protected abstract fun subscribeUi()

    open fun onRetry(){

    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(layoutId , container , false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        initData()
        subscribeUi()
    }
}

好啦,写到这里就结束了,基础的页面框架,Android 抽屉布局 + 底部多 Tab切换 + 自定义ToolBar 的UI 效果就已经搭好啦,读者可以参考代码去实现哈!

发布了22 篇原创文章 · 获赞 15 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/m0_37094131/article/details/103843551