写点什么

Android App 首页主流框架搭建

作者:yechaoa
  • 2022 年 6 月 08 日
  • 本文字数:4664 字

    阅读完需:约 15 分钟

DrawerLayout+NavigationView+Toolbar+ViewPager+BottomNavigationView

效果

页面结构解析

这是一个比较常见的 APP 首页的结构,侧边栏+主页,侧边栏里是一些菜单,主页由底部菜单控制内容区,内容区是可滑动的子页面。整体比较舒服合理,各自为阵,却又能关联在一起,加上又是大众喜爱的Material Design风格,所以成为了当下 APP 首页的主流结构。



上图是做的一个简单的思维导图,并不复杂,理清了结构就能事半功倍。

页面布局

1.首页

即整个大的容器。



<?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:id="@+id/drawer_layout"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:fitsSystemWindows="true"    tools:context=".module.MainActivity"    tools:openDrawer="start">
<include layout="@layout/app_bar_main" android:layout_width="match_parent" android:layout_height="match_parent" />
<com.google.android.material.navigation.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" />
</androidx.drawerlayout.widget.DrawerLayout>
复制代码


  • DrawerLayout包裹着include的主页和侧边栏内容NavigationView

  • app_bar_main是主页内容,采用 include 的方式引用是为了结构清晰,避免混乱。

  • 侧边栏NavigationView分为头部布局headerLayout和菜单menu,注意一个是 layout 一个是 menu。

  • 其他需要注意的是,NavigationView的位置应与主内容app_bar_main同级,且在主内容之后。

  • 关于 DrawerLayout 更多使用可以查看:DrawerLayout使用详解

2.主页

这里主页说的是首页除侧边栏以外的页面。



<?xml version="1.0" encoding="utf-8"?><androidx.coordinatorlayout.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"    tools:context=".module.MainActivity">
<com.google.android.material.appbar.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<androidx.appcompat.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"/>
</com.google.android.material.appbar.AppBarLayout>
<include layout="@layout/content_main"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_marginRight="@dimen/dp_20" android:layout_marginEnd="@dimen/dp_20" android:layout_marginBottom="@dimen/dp_70" app:srcCompat="@android:drawable/ic_dialog_email"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
复制代码


上面是标题,中间是内容区,FloatingActionButton 可以忽略。

3.主页内容区

效果同 2 一样


<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout 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"    app:layout_behavior="@string/appbar_scrolling_view_behavior"    tools:context=".module.MainActivity"    tools:showIn="@layout/app_bar_main">
<LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<androidx.viewpager.widget.ViewPager android:id="@+id/view_pager" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" />
<com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_navigation" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" android:background="?android:attr/windowBackground" app:layout_constraintBottom_toTopOf="@+id/viewPager" app:menu="@menu/bottom_navigation" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
复制代码


  • 上面是ViewPager,可滑动的内容区,填充一个一个的Fragment子页面。

  • 下面是BottomNavigationView底部菜单,与上面的 ViewPager 关联。


到此页面布局的部分介绍完毕,下面开始说代码部分。

代码部分

1.侧边栏

我们要在toolbar上加一个按钮,把侧边栏关联起来,让其点击可以弹出侧边栏。


    /**     * Drawer关联Toolbar     */    private fun initActionBarDrawer() {        val toggle = ActionBarDrawerToggle(            this,            drawer_layout,            toolbar,            R.string.navigation_drawer_open,            R.string.navigation_drawer_close        )        drawer_layout.addDrawerListener(toggle)        toggle.syncState()    }
复制代码

2.内容区

关联了侧边栏,我们来看内容区,上面说过内容区是 ViewPager 包含的一个个子页面 Fragment,来看代码的实现


    /**     * 初始化Fragment     */    private fun initFragments() {        val viewPagerAdapter = CommonViewPagerAdapter(supportFragmentManager)        viewPagerAdapter.addFragment(HomeFragment())        viewPagerAdapter.addFragment(TreeFragment())        viewPagerAdapter.addFragment(NaviFragment())        viewPagerAdapter.addFragment(ProjectFragment())
view_pager.offscreenPageLimit = 1 view_pager.adapter = viewPagerAdapter }
复制代码


处理事件

1.侧边栏点击事件

        /**         * 侧边栏点击事件         */        nav_view.setNavigationItemSelectedListener {            // Handle navigation view item clicks here.            when (it.itemId) {                R.id.nav_collect -> {                    ToastUtilKt.showToast("收藏")                }                R.id.nav_share -> {                    ToastUtilKt.showToast("分享")                }                R.id.nav_about -> {                    ToastUtilKt.showToast("关于")                }                R.id.nav_logout -> {                     ToastUtilKt.showToast("退出")                 }            }
//关闭侧边栏 drawer_layout.closeDrawer(GravityCompat.START)
true }
复制代码


根据itemId判断触发事件,并关闭侧边栏,这一步可选,也可以不关闭 保持侧边栏打开的状态。

2.view_pager 滑动监听

        /**         * view_pager 滑动监听         */        view_pager.addOnPageChangeListener(object : OnPageChangeListener {            override fun onPageScrollStateChanged(state: Int) {            }
override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int ) { }
override fun onPageSelected(position: Int) { bottom_navigation.menu.getItem(position).isChecked = true //设置checked为true,但是不能触发ItemSelected事件,所以滑动时也要设置一下标题 when (position) { 0 -> { toolbar.title = resources.getString(R.string.app_name) } 1 -> { toolbar.title = resources.getString(R.string.title_tree) } 2 -> { toolbar.title = resources.getString(R.string.title_navi) } else -> { toolbar.title = resources.getString(R.string.title_project) } } } })
复制代码


view_pager 滑动之后底部对应的菜单选中,并重新设置标题。

3.bottom_navigation 底部菜单点击事件

        /**         * bottom_navigation 点击事件         */        bottom_navigation.setOnNavigationItemSelectedListener {            when (it.itemId) {                R.id.navigation_home -> {                    view_pager.currentItem = 0                    return@setOnNavigationItemSelectedListener true                }                R.id.navigation_tree -> {                    view_pager.currentItem = 1                    return@setOnNavigationItemSelectedListener true                }                R.id.navigation_navi -> {                    view_pager.currentItem = 2                    return@setOnNavigationItemSelectedListener true                }                R.id.navigation_project -> {                    view_pager.currentItem = 3                    return@setOnNavigationItemSelectedListener true                }            }            false        }
复制代码


底部菜单点击的时候也让 view_pager 滑动的响应的位置,同第 2 步其实是相互关联的。


到此,整个搭建就完成了,从页面布局到初始化控件,再到处理事件,整体思路要清晰,搭建起来就很快,小的功能细节再调试完善完善就 ok 了。

完整代码

https://github.com/yechaoa/wanandroid_kotlin


写作不易,有用就点个赞呗 ^ _ ^


发布于: 刚刚阅读数: 4
用户头像

yechaoa

关注

还未添加个人签名 2018.10.23 加入

知名互联网大厂技术专家,多平台博客专家、优秀博主、人气作者,博客风格深入浅出,专注于Android领域,同时探索于大前端方向,持续研究并落地前端、小程序、Flutter、Kotlin等相关热门技术

评论

发布
暂无评论
Android App首页主流框架搭建_android_yechaoa_InfoQ写作社区