这是我参与「第四届青训营 」笔记创作活动的第一天
本文用于记录初入安卓的我对于整个应用在设计上从全部使用Activity到少Activity与多Fragment结合的转变过程
首先我是通过阅读网上流传的“神书”《第一行代码》入门安卓开发,由于书籍对fragment的介绍相对较少,以至于我在阅读完整本书之后不记得有fragment这个东西了。
在把书看完了之后,我就面临一个需求:实现一个底部导航栏(当时并不知道有这么一个叫法,毕竟刚入门嘛)。再结合当时脑子全是四大组件,fragment啥的早已不在脑中。所以最后一上来就是先建四个Activity,再设计一个公用的底部控件,每个Activity都引入一个底部控件,在切换的时候销毁当前Activity,创建一个新的目标Activity,并初始化底部控件状态。(由于当时的源码找不着了,所以在这里说一下当时的一个思路)
有过这种做法的同学应该能够想象我最后的崩溃程度,接下来说说当时设计面临的问题。一是:四个Activity中每个Activity都有一个底部控件,而底部控件有四个点击事件,这样就需要写十六个onClickListener,虽然可以通过复用减少次数,但是总归相当麻烦。二是:每点击一下就会创建一个新的Activity并且销毁一个新的Activity,这开销不可谓不大。
对于全部都是Activity的设计,不仅导航及其困难,其性能也相当糟糕。所以现在一般采用少量Activity控制多数Fragment,甚至单Activity多Fragment的方法。
为什么会大量使用Fragment来实现页面,这就不得不说一下Fragment了。
什么是Fragment?
Fragment表示应用界面中可重复使用的一部分。Fragment 定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。Fragment 不能独立存在,而是必须由 Activity 或另一个 Fragment 托管。Fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。
Fragment用来做什么?
我认为Fragment就是用来将同一个页面里面有着不同功能的部分解藕的。Android官方提供了这么一种最佳实践,避免让你自己定义一套框架来分离这些功能。
Fragment有什么优点?
首先,Fragment拥有自己的生命周期,让你能够更好地根据不同的生命周期来控制其中的具体逻辑;
其次,Fragment是官方支持,这一方面说明它会随着Android的版本更新增加功能,同时越来越稳定;另一方面则是说明它是一个全局通用的“组件”,你可以将不同的Fragment拼装成一个页面,也可以随时将他们搬走,很方便地让它们在另一个页面当中生效;
还有,Fragment很“轻”,你可以使用它来实现“单Activity架构”;
最后,正如Fragment诞生的目的,它能够完美地为你的App中的不同功能进行解耦,提供良好的灵活性和可复用性。
转载自:(如何优雅地使用Fragment)juejin.cn/post/700770…
对于如何使用Fragment,可以看看我小组成员的另一篇笔记:juejin.cn/post/712766…
说完Fragment的一些优点之后,对于改造我最初的设计也并不困难,接下来说说如何改造。相信对jetpack熟悉的同学,必然知道bottom navigation,我最开始的那个需求就是一个底部导航栏,可惜当时并不知道这个叫底部导航栏,不然可能靠搜索引擎都可以找到解决方案。
但是在最近的青训营的项目中,我并没有采用bottom navigation来实现我们的导航栏,因为利用官方提供的组件来做自定义,还是相对较为困难(可能是我比较菜)。所以最后我选择了一个Activity通过navigation控制三个Fragment的方式,对于底部的导航栏由Activity定义并管理。
底部导航栏的UI
Activity的实现代码
public class MainActivity extends AppCompatActivity {
private final HashMap<Integer, MotionLayout> map = new HashMap<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// 主页面底部导航控件与id的映射
// 如果需要添加底部导航控件,只需要在这里添加一个映射
map.put(R.id.userFragment, binding.bnUser.mlUser);
map.put(R.id.addFragment, binding.bnAdd.mlAdd);
map.put(R.id.listFragment, binding.bnList.mlList);
NavHostFragment navHostFragment =
(NavHostFragment) getSupportFragmentManager()
.findFragmentById(R.id.fragmentContainerView);
if (navHostFragment == null) {
return;
}
NavController navController = navHostFragment.getNavController();
for (Map.Entry<Integer, MotionLayout> entry :
map.entrySet()) {
entry.getValue().setOnClickListener(v -> {
//弹出当前fragment,确保每次只有一个Fragment
navController.popBackStack();
//压入点击的fragment
navController.navigate(entry.getKey());
});
}
navController.addOnDestinationChangedListener((controller, destination, arguments) -> {
int currDestinationId = destination.getId();
for (Map.Entry<Integer, MotionLayout> entry :
map.entrySet()) {
entry.getValue().setProgress(0f);
//当前fragment的导航不能再次点击,其他的导航可以点击
if (entry.getKey() == currDestinationId) {
entry.getValue().setClickable(false);
//点击的导航开始动画
entry.getValue().transitionToEnd();
} else {
entry.getValue().setClickable(true);
}
}
});
}
}
为什么我没有选择使用bottom navigation来实现,主要是我想加点动画效果。关于加了什么动画,我在之后的笔记中再做分享,这篇笔记先记录到在这里。
ps:这个底部导航栏的实现是学习这位Up主的,有兴趣的同学可以看看,不过他是使用kt实现的,我这个可以说是一个java版本的实现。www.bilibili.com/video/BV1nV…