Android 原生导航 IV-实现Drawer+ToolBar+Tab

本文详细介绍了如何在Android应用中实现Drawer、ToolBar和Tab的整合,通过使用NavigationVIew、Toolbar和TabLayout,构建了一个功能丰富的导航系统。文章还涉及到如何在主布局中引入Toolbar,以及如何关联Drawer与Toolbar,最后介绍了Tab的实现方式,包括添加TabLayout和ViewPager,以实现实时切换不同的页面。通过阅读本文,开发者可以掌握如何在Android应用中高效地构建导航系统,提升用户体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述:

要实现这个组合, 先要了解结构层次. 合理的组合应该是Drawer封装ToolBar, 然后ToolBar再封装Tab.

Drawer:

首先是Drawer, Drawer的层次结构比较简单, 可以在layout中直接实现.

为Drawer创建一个layout:

Layout的结构是

<android.support.v4.widget.DrawerLayout>

     <!--Your contents ToolBar-->

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

其中NavigationView是Material Design中推荐的用法, 它可以在Drawer中显示一个这样的页面:


其中蓝色的头部区域是可选的, 下面的菜单区域是必选的. 它的完整用法是这样:

    <android.support.design.widget.NavigationView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/drawer_header"
            app:menu="@menu/drawer"/>

其中, 有两个属性, 一个是app:headerLayout, 它是可选的, 指定了一个header. App:menu则指定了一个菜单资源, 代表header下面的菜单(可以在运行时更新). 最简单的drawer菜单是一些checkable菜单项:

<group android:checkableBehavior="single">
    <item
        android:id="@+id/navigation_item_1"
        android:checked="true"
        android:icon="@drawable/ic_android"
        android:title="@string/navigation_item_1"/>
    <item
        android:id="@+id/navigation_item_2"
        android:icon="@drawable/ic_android"
        android:title="@string/navigation_item_2"/>
</group>

选中的菜单项将会在drawer中高亮显示, 以确保让用户知道当前选中的是哪个. 还可以在菜单中使用subheader来指定一个独立组的item:

<item
    android:id="@+id/navigation_subheader"
    android:title="@string/navigation_subheader">
    <menu>
        <item
            android:id="@+id/navigation_sub_item_1"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_1"/>
        <item
            android:id="@+id/navigation_sub_item_2"
            android:icon="@drawable/ic_android"
            android:title="@string/navigation_sub_item_2"/>
    </menu>
</item>

现在假设我们已经拥有了一个这样的Drawer的layout, 它来自Android的Sample:

<?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"
    android:id="@+id/my_drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </FrameLayout>

    <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:fitsSystemWindows="true"
        app:headerLayout="@layout/nav_header_main" app:menu="@menu/activity_main_drawer" />

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

这里的NavigationVIew也可以使用一个LIstView来代替, 以显示一个简单的菜单, 它的headerLayout和menu的实现稍后再研究, 我们先拿它作为一个整体来看待. 在主layout中有了这些之后我们就拥有了一个Drawer的基本框架, 它在隐藏和显示的时候分别长这样:


而在MainActivity中不需要添加额外的代码, 设置Activity的Content即可.

ToolBar:

现在要为APP添加layout, 只需要修改Drawer layout中的主界面内容为一个appbar就可以了.

为ToolBar添加Layout:

添加一个新建的layout, 很简单, 一个Toolbar + 主界面内容:

<?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="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/my_own_toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:elevation="4dp"
        android:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <include layout="@layout/content_main" />
</LinearLayout>

并将Drawer中的FrameLayout改为引入Toolbar的代码:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout >

    <include layout="@layout/app_bar_my_own" android:layout_height="match_parent"
        android:layout_width="match_parent"/>
    …
</android.support.v4.widget.DrawerLayout>

这时候就可以看到已经拥有一个ToolBar了, 但是上面是空的, 什么也没有:


关联ToolBar与Drawer:

现在我们需要在Activity中添加一些代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my_own);
//设置Toolbar
    Toolbar myToolbar = (Toolbar) findViewById(R.id.my_own_toolbar);
    setSupportActionBar(myToolbar);
    //关联Drawer
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.my_drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, myToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();
}

上面的代码包括两部分, 一个是设置Toolbar, 还有就是关联Drawer与Toolbar. setSupportActionBar()之后, Activity的名字就会出现在ToolBar上面. 下面关联Drawer这段可以参考Android 原生导航 III-Drawer导航. 它添加了对Drawer显示/隐藏事件的监听. syncState()则是用来在ToolBar上为Drawer添加一个指示器. 这个指示器可以通过drawerArrowStyle属性修改. 完成这些代码之后, 主界面变成了这样, 并且图标会因为Drawer显示而发生变化:


Tab:

Tab的实现比较复杂, 基础逻辑是TabLayout+ ViewPager.

实现Tab的Layout:

因为Tab是AppBar的子view, 所以要修改AppBar的layout文件, 添加TabLayout和ViewPager元素, 修改之后的AppBar的layout文件如下:

<?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="match_parent"
   
android:orientation="vertical">

    <android.support.v7.widget.Toolbar
       
android:id="@+id/my_own_toolbar"
       
android:layout_width="match_parent"
       
android:layout_height="?attr/actionBarSize"
       
android:background="?attr/colorPrimary"
       
android:elevation="4dp"
        
android:theme="@style/ThemeOverlay.AppCompat.ActionBar"/>

   <android.support.design.widget.TabLayout
       
android:id="@+id/tab_layout"
       
android:layout_width="match_parent"
       
android:layout_height="wrap_content">
    </android.support.design.widget.TabLayout>

    <android.support.v4.view.ViewPager

       android:id="@+id/pager"
       
android:layout_width="match_parent"
       
android:layout_height="match_parent">
    </android.support.v4.view.ViewPager>

</LinearLayout>

这时界面没有变化, 因为想让它们起作用还得设置一下.

关联TabLayout和VIewPager:

首先是TabLayout, 它比较简单, 添加如下代码到onCreate()中即可:

TabLayout tabLayout= (TabLayout)findViewById(R.id.tab_layout);
tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));

这时候就可以看到Tab了.


然后添加ViewPager, ViewPager需要用到FragmentPagerAdapter或者FragmentStatePagerAdapter. 这里使用FragmentPagerAdapter:

public class SectionsPagerAdapter extends FragmentPagerAdapter {
    public SectionsPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = new DummySectionFragment();
        Bundle args = new Bundle();
        args.putInt(DummySectionFragment.ARG_SECTION_NUMBER, position + 1);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public int getCount() {
        return 3;
    }
}

public static class DummySectionFragment extends Fragment {
    public static final String ARG_SECTION_NUMBER = "section_number";
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.content_main, container, false);
        TextView dummyTextView = (TextView) rootView.findViewById(R.id.section_label);
        dummyTextView.setText(Integer.toString(getArguments().getInt(ARG_SECTION_NUMBER)));
        return rootView;
    }
}

这里添加一个Fragment作为页面内容, 显示Tab的号码. 有了FragmentPagerAdapter就可以将它设置给ViewPager了, 在onCreate()中添加如下代码:

mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));

tabLayout.setOnTabSelectedListener(this);

现在完整的onCreate()代码是这样的:

SectionsPagerAdapter mSectionsPagerAdapter;
ViewPager mViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_my_own);
    Toolbar myToolbar = (Toolbar) findViewById(R.id.my_own_toolbar);
    setSupportActionBar(myToolbar);

    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.my_drawer_layout);
    ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
            this, drawer, myToolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
    drawer.setDrawerListener(toggle);
    toggle.syncState();

    TabLayout tabLayout = (TabLayout)findViewById(R.id.tab_layout);
    tabLayout.addTab(tabLayout.newTab().setText("Tab 1"));
    tabLayout.addTab(tabLayout.newTab().setText("Tab 2"));
    tabLayout.addTab(tabLayout.newTab().setText("Tab 3"));

    mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
    mViewPager = (ViewPager) findViewById(R.id.pager);
    mViewPager.setAdapter(mSectionsPagerAdapter);
    mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));

    tabLayout.setOnTabSelectedListener(this);
}

由于要监听Tab的点击事件, 还要添加implements TabLayout.OnTabSelectedListener到Activity. 并且实现它的回调方法:

@Override
public void onTabSelected(TabLayout.Tab tab) {
    mViewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(TabLayout.Tab tab) {

}

@Override
public void onTabReselected(TabLayout.Tab tab) {

}

至此这个Android最常用的导航的基本样子就已经有了, 它是这样的, 并且可以响应点击和滑动改变Tab的事件:


在栗子中, NavigationView和TabLayout这两种很重要的类都来自于android.support.design, 它提供了Material Design的支持, 很好用.

 

参考: https://github.com/googlesamples/android-HorizontalPaging/

https://github.com/googlesamples/android-NavigationDrawer/

http://stackoverflow.com/questions/26540078/use-tab-with-new-toolbar-appcompat-v7-21

<think>好的,用户的问题是关于在Flutter 3中使用GetX实现包含DrawerTabBar和搜索功能的示例教程。我需要先理清用户的具体需求,然后结合现有的知识和引用内容来组织回答。 首先,用户提到的三个组件:DrawerTabBar和搜索功能。GetX是状态管理和依赖注入的库,所以需要说明如何用GetX来管理这些组件的状态和绑定。根据引用内容,GetX中的GetView、GetxController和Binding是关键部分,特别是引用[1]提到了GetView和Bindings的使用,以及控制器的生命周期。这可能需要在回答中详细说明如何创建控制器并使用Bindings来初始化。 接下来,关于TabBar的实现,引用[2]和[3]提到了使用DefaultTabController和如何处理TabBar与页面的同步问题。但用户希望使用GetX而不是DefaultTabController,所以需要将TabController转换为由GetX管理的状态。可能需要创建一个TabController,并在GetxController中管理当前选中的标签页索引,然后通过Obx或GetBuilder来更新视图。 Drawer实现相对直接,但需要集成到GetX的结构中。可能需要说明如何在AppBar中设置leading图标来打开Drawer,并在Drawer添加菜单项,通过Get.toNamed或其他导航方法切换路由。 搜索功能通常涉及一个搜索框,可能需要一个TextController来监听输入变化,并触发搜索操作。这里可以使用GetxController来管理搜索状态,比如存储搜索关键字,并在输入变化时更新结果。 然后,需要将这些部分整合到一个主页面中,使用GetMaterialApp作为入口,并配置路由。引用[3]提到可能出现的标题栏重复的问题,需要注意Scaffold和AppBar的层级结构,避免重复渲染。 步骤分解可能需要: 1. 添加依赖:确认pubspec.yaml中包含get和flutter的最新版本。 2. 创建控制器:分别处理TabBar状态和搜索逻辑。 3. 使用Bindings:在路由绑定中初始化控制器,引用[1]中的Binding用法。 4. 构建主界面:使用Scaffold包含Drawer、AppBar(带TabBar)、TabBarView,并在AppBar中添加搜索图标。 5. 实现搜索功能:通过GetxController管理搜索状态,可能使用Debounce来优化输入处理。 6. 处理导航Drawer中的菜单项跳转到其他页面,使用GetX的路由管理。 需要注意各部分之间的协调,比如TabBarView的子页面可能需要访问对应的控制器,或者通过Get.find获取。同时,确保Drawer的触发按钮正确设置,可能需要在AppBar的leading属性使用IconButton,并结合GetX的导航方法。 可能遇到的问题包括状态未正确绑定、控制器未初始化、TabBar与TabBarView的协调问题,以及搜索功能的实时更新。需要逐一检查这些点,确保每个部分都正确使用GetX的状态管理。 最后,示例代码需要结构清晰,展示如何将各部分组合在一起,并附上相关说明,帮助用户理解每个组件的作用和GetX的应用方式。</think>### 使用 Flutter 3 和 GetX 实现 DrawerTabBar 和搜索功能的步骤说明 #### 1. 依赖配置 在 `pubspec.yaml` 中添加依赖: ```yaml dependencies: flutter: sdk: flutter get: ^4.6.5 # GetX 核心库 ``` #### 2. 创建控制器 - **TabBar 控制器**:管理 Tab 切换状态 ```dart class TabController extends GetxController { final RxInt currentIndex = 0.obs; void changeTab(int index) { currentIndex.value = index; } } ``` - **搜索控制器**:管理搜索逻辑 ```dart class SearchController extends GetxController { final RxString searchKeyword = ''.obs; void onSearch(String value) { searchKeyword.value = value; // 可添加搜索逻辑(如 API 请求) } } ``` #### 3. 绑定依赖 使用 `Bindings` 初始化控制器: ```dart class MainBinding implements Bindings { @override void dependencies() { Get.lazyPut(() => TabController()); Get.lazyPut(() => SearchController()); } } ``` #### 4. 主页面结构 ```dart class MainPage extends GetView<TabController> { @override Widget build(BuildContext context) { return Scaffold( drawer: _buildDrawer(), appBar: AppBar( title: Obx(() => Text('当前 Tab: ${controller.currentIndex + 1}')), bottom: _buildTabBar(), actions: [ IconButton( icon: Icon(Icons.search), onPressed: () => _showSearchDialog(), ) ], ), body: _buildTabViews(), ); } Widget _buildDrawer() { return Drawer( child: ListView( children: [ ListTile( title: Text("设置"), onTap: () => Get.toNamed('/settings'), ) ], ), ); } PreferredSizeWidget _buildTabBar() { return TabBar( onTap: controller.changeTab, tabs: [ Tab(text: '首页'), Tab(text: '消息'), Tab(text: '我的'), ], ); } Widget _buildTabViews() { return TabBarView( children: [ HomeTab(), MessageTab(), ProfileTab(), ], ); } void _showSearchDialog() { Get.dialog( AlertDialog( title: Text('搜索'), content: TextField( onChanged: Get.find<SearchController>().onSearch, ), ), ); } } ``` #### 5. 初始化应用 ```dart void main() { runApp(GetMaterialApp( initialBinding: MainBinding(), home: MainPage(), getPages: [ GetPage(name: '/settings', page: () => SettingsPage()), ], )); } ``` #### 关键特性说明 1. **状态管理**: - 通过 `GetView` 自动绑定控制器[^1] - 使用 `Obx` 监听 Tab 切换状态变化 2. **路由集成**: - Drawer 使用 `Get.toNamed` 实现导航 - 搜索弹窗通过 `Get.dialog` 快速创建 3. **性能优化**: - `Get.lazyPut` 延迟加载控制器 - 搜索功能可添加防抖(使用 `debounce`)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值