之前几篇笔记完成了Room创建数据库,到数据库升级,以及RecyclerView的动态显示,这一次来使用Naviation进行页面切换,可视化的方式定义页面,并且添加部分动画效果和手势;
相较于之前每一个页面就是一个Activity
,每次使用Intent
和finsh
来切换和关闭页面,不仅耗费资源,而且切换生硬,并且不便于扩展维护,现在Jetpack
加入了Navigation
,可以可视化的设计页面的跳转,把一个活动只作为容器,页面内容存放在Fragment
中,也提高了日后的维护和扩展
在Android Studio中创建Fragment,如同之前创建Activiy一样,可以创建若干个Fragment,每一个Fragment都有对应的XML布局文件,其布局方式同Activity一样:
Fragment不需要在清单文件中声明
Fragment
同Activity一样也有生命周期,主要有如下方法
onCreateView()
初始化组件,在这里进行组件的绑定
参数分别是:布局文件、组件组、是否是根节点
return inflater.inflate(R.layout.fragment_peo, container, false);
其最重要的方法的onActivityCreated
在活动上创建操作
因为Fragment是显示在Activity上的,因此操作需要在该方法内完成,其相当于Activity的onCreate
方法;
requireActivity()
可以在Fragment中获取显示的活动,相当于在Activity中使用thisgetViewLifecycleOwner()
可以感知Fragment的生命周期在Fragment中也可以复写
OptionsMenu
、ContextMenu
等方法,其与Activity中一样;
Navigation即导航,用于设置不同页面之间的切换,在RES目录下新建Navigation文件即可;
点击添加按钮添加页面到导航中
可以使用每个页面的圆点的指向来定义动作,并可以给每个动作添加动画或者参数
在Res中新建Animation
文件,在文件中使用标记语言来书写动画:
移动,包含fromXDelta
、toXDelta
等,即从X轴的位置,移动到X轴的位置;duration
持续时间,动画的持续时间;
选中,使用同移动相同,需要设置pivotX\Y
来告知旋转中心;
透明度……在Activiry中添加
或者NavHostFragment
,并选中之前设置的Fragment/Navigation即可
建议使用
NavHostFragment
,可以自动导入Fragment;
获取导航控制器,参数为当前的视图或者活动
//获取导航控制器,参数为当前页面
NavController controller = Navigation.findNavController(v);
使用导航控制器调用动作
每一个动作即前面设置的箭头
controller.navigate(R.id.action_peoFragment_to_addFragment);
实例化一个ItemTouchHelper
对象复写ItemTouchHelper.SimpleCallback
接口,可以实现拖拽或滑动效果,其中有两个参数:
swipeDirs
滑动,可以添加滑动启动,ItemTouchHelper.START | ItemTouchHelper.END
即可以向左或右滑动(不用LEFT和RIGHT是因为不同的手机对左右的定义可能不一样)dragDirs
拖拽,设置拖拽的方向,ItemTouchHelper.UP|ItemTouchHelper.DOWN
表示允许上下拖动.attachToRecyclerView()
添加到RecyclerView上new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP|ItemTouchHelper.DOWN, ItemTouchHelper.START | ItemTouchHelper.END) {}.attachToRecyclerView(recyclerView);
onSwiped
方法是当滑动发生时执行,如下代码是完成之前笔记中的滑动删除功能:
//滑动
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
//Livedata获取内容,从viewholder的位置,viewHolder.getAdapterPosition()获取当前的位置
final People people = peos.get(viewHolder.getAdapterPosition());
peopleViewModel.delPeo(people);
//底部横幅, 三个参数(显示在那个组件上,显示的内容,时间)
Snackbar.make(requireView().findViewById(R.id.peoView), "删除了一个联系人", Snackbar.LENGTH_LONG)
.setAction("撤销", new View.OnClickListener() {
@Override
public void onClick(View v) {
peopleViewModel.insertPeo(people);
}
}).show();
}
}).attachToRecyclerView(recyclerView);
onMove
方法在当拖拽发生时执行,如下代码完成的是之前笔记中的拖动交换位置方法:
注意!此方法有BUG,因为处理数据需要时间,当拖动很快时,数据还来不及修改会导致出现严重的数据混乱
只要你拖动的足够快,数据就追不上你
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
//获取拖动的内容
People people = peos.get(viewHolder.getAdapterPosition());
//获取目的地内容
People peopleend = peos.get(target.getAdapterPosition());
//交换他们的id
int idTemp = people.get_id();
people.set_id(peopleend.get_id());
peopleend.set_id(idTemp);
//更新数据
peopleViewModel.updatePeo(people, peopleend);
//改变位置 (参数 起 终)
adapterCard.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
adapterNorm.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
return false;
}
当数据更改,适配器每一次都会重新刷新,但是其实大部分资源并没有改变,而且动画非常生硬,这时候可以修改之前的适配器,将继承的RecyclerView.Adapter
修改为更加高级的ListAdapter
,这样系统会自动的比较数据的改变,从而不需要每次都全部刷新,使用的资源跟小,并且动画更加流畅(有添加和消失的动画)
ListAdapter
接口需要两个参数
ListAdapter<People,PeoAdapter.peoViewHolder>
在构造方法中继承比较,并传入比较的方法,以之前的比较为利,需要这样定义比较方法,首先判断是不是同一个元素,之后比较同一个元素中的内容;
super(new DiffUtil.ItemCallback<People>() {
//比较元素是否相同
@Override
public boolean areItemsTheSame(@NonNull People oldItem, @NonNull People newItem) {
//直接比较id是否相同,相同就是同一个元素
return oldItem.get_id()==newItem.get_id();
}
//比较内容是否相同
@Override
public boolean areContentsTheSame(@NonNull People oldItem, @NonNull People newItem) {
//比较内容
boolean flag=(oldItem.getCollege().equals(newItem.getCollege())&&oldItem.getName().equals(newItem.getName()));
return flag;
}
});
由于继承接口时候以及声明的管理的资源对象,因此不需要在适配器中再声明对象,故可以删除适配器中原有的和对象相关的对象数组和方法,直接调用本身的方法
其中获取Item可以直接使用
getItem(int)
即可
需要传入参数时候调用submitList(object);
即可
此时系统会自动比较内容的区别,当数据改变时候会自动刷新并添加过度动画
此外由于之前观察者使用Activity
的生命周期,但由于在切换Fragment
时其并没有变化,因此观察者会被重叠,故需要将此前生命周期换为Fragment的getViewLifecycleOwner()
即:
findPeo.observe(getViewLifecycleOwner(), new Observer<List<People>>()