SkillAgentSearch skills...

AndroidNavigation

A library managing navigation, nested Fragment, StatusBar, Toolbar for Android

Install / Use

/learn @listenzz/AndroidNavigation
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

AndroidNavigation

A library managing nested Fragment, translucent StatusBar and Toolbar for Android.

You could use it as a single Activity Architecture Component.

This is also the subproject of hybrid-navigation.

Download demo apk

特性

  • 一行代码实现 Fragment 嵌套,一次性构建好嵌套层级
  • 一行代码实现 Fragment 跳转,不再需要写一大堆操作 fragment 的代码了,不用担心用错 FragmentManager 了
  • 可扩展性强,允许自定义容器和路由
  • 自动为你创建 Toolbar,一行代码设置标题、按钮,支持关闭自动创建功能以实现定制
  • 一处设置全局样式,到处使用,并且支持不同页面个性化
  • 支持侧滑返回
  • 支持懒加载
  • 支持 font icons

6.0 screenshot:

android-navigation

android-navigation

Installation

implementation 'io.github.listenzz:AndroidNavigation:13.10.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}

Usage


<a name="building-hierarchy"></a>

构建 UI 层级

你的 Fragment 需要继承 AwesomeFragment。

你的 Activity 需要继承 AwesomeActivity,然后设置 rootFragment。

public class MainActivity extends AwesomeActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            TestFragment testFragment = new TestFragment();
            setActivityRootFragment(testFragment);
        }
    }

}

你可以调用 setActivityRootFragment 多次,根据不同的 App 状态展示不同的根页面。比如一开始你只需要展示个登录页面,登陆成功后将根页面设置成主页面。

AwesomeFragment 同样部署了 setActivityRootFragment 接口,方便你随时随地切换 activity 的根。

你通常还需要另外一个 Activity 来做为闪屏页(Splash),这个页面则不必继承 AwesomeActivity。

为了处理常见的 Fragment 嵌套问题,提供了 StackFragmentTabBarFragmentDrawerFragment 三个容器类。它们可以作为 Activity 的 rootFragment 使用。这三个容器为 Fragment 嵌套提供了非常便利的操作。

StackFragment

StackFragment 以栈的形式管理它的子 Fragment,支持 push、pop 等操作,在初始化时,需要为它指定 rootFragment。

public class MainActivity extends AwesomeActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            TestFragment testFragment = new TestFragment();
            StackFragment stackFragment = new StackFragment();
            // 把 TestFragment 设置为 StackFragment 的根
            stackFragment.setRootFragment(testFragment);
            // 把 StackFragment 设置为 Activity 的根
            setActivityRootFragment(stackFragment);
        }
    }
}

如果 TestFragment 的根布局是 LinearLayout 或 FrameLayout,会自动帮你创建 Toolbar,当由 A 页面跳转到 B 页面时,会为 B 页面的 Toolbar 添加返回按钮。更多关于 Toolbar 的配置,请参考 设置 Toolbar 一章。

在 TestFragment 中,我们可以通过 getStackFragment 来获取套在它外面的 StackFragment,然后通过 StackFragment 提供的 pushFragment 跳转到其它页面,或通过 popFragment 返回到前一个页面。关于导航的更多细节,请参考 导航 一章。

TabBarFragment

这也是一个比较常见的容器,一般 APP 主界面底下都会有几个 tab,点击不同的 tab 就切换到不同的界面。

public class MainActivity extends AwesomeActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {

            // 首页
            HomeFragment homeFragment = new HomeFragment();
            homeFragment.setTabBarItem(new TabBarItem("首页", R.drawable.icon_home));

            // 通讯录
            ContactsFragment contactsFragment = new ContactsFragment();
            contactsFragment.setTabBarItem(new TabBarItem("通讯录", R.drawable.icon_contacts));

            // 添加 tab 到 TabBarFragment
            TabBarFragment tabBarFragment = new TabBarFragment();
            tabBarFragment.setFragments(homeFragment, contactsFragment);

            // 把 TabBarFragment 设置为 Activity 的根
            setActivityRootFragment(tabBarFragment);
        }
    }

}

在 HomeFragment 或 ContactsFragment 中,可以通过 getTabBarFragment 来获取它们所属的 TabBarFragment.

可以通过 TabBarFragment 的 setSelectedIndex 方法来动态切换 tab,通过 getTabBar 可以获取 TabBar, 然后可以调用 TabBar 提供的方法来设置红点,未读消息数等。

如果对提供的默认 TabBar 不满意,可以通过实现 TabBarProvider 来自定义 TabBar , 在设置 TabBarFragment 为其它容器的根前,调用 TabBarFragment#setTabBarProvider 来设置自定义的 TabBar, 参数可以为 null, 表示不需要 TabBar.

如果 HomeFragment 或 ContactsFragment 需要有导航的能力,可以先把它们嵌套到 StackFragment 中。

public class MainActivity extends AwesomeActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {

            // 首页
            HomeFragment homeFragment = new HomeFragment();
            StackFragment homeNavigationFragment = new StackFragment();
            homeNavigationFragment.setRootFragment(homeFragment);
            homeNavigationFragment.setTabBarItem(new TabBarItem("首页", R.drawable.icon_home));

            // 通讯录
            ContactsFragment contactsFragment = new ContactsFragment();
            StackFragment contactsNavigationFragment = new StackFragment();
            contactsNavigationFragment.setRootFragment(contactsFragment);
            contactsNavigationFragment.setTabBarItem(new TabBarItem("通讯录", R.drawable.icon_contacts));

            // 添加 tab 到 TabBarFragment
            TabBarFragment tabBarFragment = new TabBarFragment();
            tabBarFragment.setFragments(homeNavigationFragment, contactsNavigationFragment);

            // 把 TabBarFragment 设置为 Activity 的根
            setActivityRootFragment(tabBarFragment);
        }
    }

}

DrawerFragment

这个容器内部封装了 DrawerLayout。使用时需要为它设置两个子 Fragment。

public class MainActivity extends AwesomeActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {

            DrawerFragment drawerFragment = new DrawerFragment();
            drawerFragment.setContentFragment(new ContentFragment());
            drawerFragment.setMenuFragment(new MenuFragment());

            // 把 drawerFragment 设置为 Activity 的根
            setActivityRootFragment(drawerFragment);
        }
    }

}

在 ContentFragment 或 MenuFragment 中,我们可以通过 getDrawerFragment 来获取它们所属的 DrawerFragment。

DrawerFragment 提供了 toggleMenuopenMenucloseMenu 这几个方法来打开或关闭 Menu。

可以通过 getContentFragmentgetMenuFragment 来获取对应的 Fragment。

可以通过 setMinDrawerMarginsetMaxDrawerWidth 来设置 menu 的宽度

contentFragment 可以是一个像 TabBarFragment 这样的容器。可以参考 demo 中 MainActivity 中的设置。

自定义容器

如果以上容器都不能满足你的需求,你可以自定义容器。

容器在添加子 fragment 时一定要注意判断 savedInstanceState 是否为 null, 会不会在生命周期重启时,重复添加 fragment。

可以参考 demo 中 ViewPagerFragment 这个类,它就是个自定义容器。

自定义容器,继承 AwesomeFragment 并重写下面这个方法。

@Override
public boolean isLeafAwesomeFragment() {
    return false;
}

因为 AwesomeFragment 会为非容器类 Fragment 的 root view 添加背景。如果容器不表明它是容器,也会为容器添加背景,这样就会导致不必要的 overdraw。

可能需要有选择地重写以下方法

@Override
protected AwesomeFragment childFragmentForAppearance() {
    // 这个方法用来控制当前的 status bar 的样式是由哪个子 fragment 决定的
    // 如果不重写,则由容器自身决定
    // 可以参考 StackFragment、TabBarFragment
    // 是如何决定让哪个子 fragment 来决定 status bar 样式的
    return 一个恰当的子 fragment;
}

如何使不同 fragment 拥有不同的 status bar 样式,请参考 设置状态栏 一章

<a name="WYSIWYG-dialog"></a>

@Override
protected boolean onBackPressed() {
    // 这个方法用来控制当用户点击返回键时,到底要退出哪个子 fragment
    // 返回 true 表示当前容器消费了此事件,否则转发给上一层容器处理
    // 可以参考 DrawerFragment,StackFragment 是如何处理返回键的
    return super.onBackPressed();
}

非容器页面也可以重写 onBackPressed 来处理用户点击返回按钮事件。

<a name="navigation"></a>

所见即所得 Dialog

Fragment 可以作为 Dialog 显示,本库做了特殊处理,使得显示出来的 Dialog 布局和在 xml 预览中所见一模一样。实现细节请看这篇文章

导航

导航是指页面间的跳转和传值,实际上和容器如何管理它的子 Fragment 有很大关系。

present & dismiss

AwesomeActivity 和 AwesomeFragment 提供了两个基础的导航功能 present 和 dismiss

  • present

    present 是一种模态交互方式,只有关闭被 present 的页面,才可以回到上一个页面,通常要求 presented 的页面给 presenting 的页面返回结果,类似于 startActivityForResult

    比如 A 页面 present 出 B 页面

    // A.java
    presentFragment(testFragment, REQUEST_CODE);
    

    B 页面返回结果给 A 页面

    // B.java
    Bundle result = new Bundle();
    result.putString("text", resultEditText.getText().toString());
    setResult(Activity.RESULT_OK, result);
    dismissFragment();
    

    A 页面实现 onFragmentResult 来接收这个结果

    // A.java
    @Override
    public void onFragmentResult(int requestCode, int resultCode, Bundle data) {
        super.onFragmentResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE) {
            if (resultCode != 0) {
                String text = data.getString("text", "");
                resultText.setText("present result:" + text);
            } else {
                resultText.setText("ACTION CANCEL");
            }
        }
    }
    

    有些时候,比如选择一张照片,我们先要跳到相册列表页面,然后进入某个相册选择相片返回。这也是没有问题的。 A 页面 present 出相册列表页面

    //AFragment.java
    StackFragment stackFragment = new StackFragment();
    AlbumListFragment albumListFragment = new AlbumListFragment();
    stackFragment.setRootFragment(albumListFragment);
    presentFragment(stackFragment, 1)
    

    相册列表页面 push 到某个相册

    push 是 StackFragment 的能力,要使用这个功能,你的 fragment 外层必须有一个 StackFragment 做为容器。

    // AlbumListFragment.java
    AlbumFragment albumFragment = new AlbumFragment();
    getStackFragment.pushFragment(albumFragment);
    

    在相册页面选好相片后返回结果给 A 页面

    // AlbumFragment.java
    Bundle result = new Bundle();
    result.putString("uri", "file://...");
    setResult(Activity.RESULT_OK, result);
    dismissFragment();
    

    在 A 页面接收返回的结果(略)。

  • dismiss

    关闭 present 出来的 Fragment,可以在该 Fragment 的任意子 Fragment 中调用,请参看上面相册的例子。

    present 所使用的 Fragm

View on GitHub
GitHub Stars716
CategoryDevelopment
Updated13d ago
Forks95

Languages

Java

Security Score

100/100

Audited on Mar 16, 2026

No findings