AndroidNavigation
A library managing navigation, nested Fragment, StatusBar, Toolbar for Android
Install / Use
/learn @listenzz/AndroidNavigationREADME
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.
特性
- 一行代码实现 Fragment 嵌套,一次性构建好嵌套层级
- 一行代码实现 Fragment 跳转,不再需要写一大堆操作 fragment 的代码了,不用担心用错 FragmentManager 了
- 可扩展性强,允许自定义容器和路由
- 自动为你创建 Toolbar,一行代码设置标题、按钮,支持关闭自动创建功能以实现定制
- 一处设置全局样式,到处使用,并且支持不同页面个性化
- 支持侧滑返回
- 支持懒加载
- 支持 font icons
6.0 screenshot:


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 嵌套问题,提供了 StackFragment、TabBarFragment、DrawerFragment 三个容器类。它们可以作为 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 提供了 toggleMenu、openMenu、closeMenu 这几个方法来打开或关闭 Menu。
可以通过 getContentFragment、getMenuFragment 来获取对应的 Fragment。
可以通过 setMinDrawerMargin 或 setMaxDrawerWidth 来设置 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
