Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作

前言

之前几篇笔记完成了Room创建数据库,到数据库升级,以及RecyclerView的动态显示,这一次来使用Naviation进行页面切换,可视化的方式定义页面,并且添加部分动画效果和手势;

笔记目录

    • 前言
    • Navigation切换页面
      • 1、Fragment
        • 创建Fragment
        • 配置Fragment
      • 2、Navigation
      • 3、自定义动画
      • 4、在Activy中布局
      • 5、调用Navigation进行切换
    • 设置手势动作
    • 性能优化

Navigation切换页面

相较于之前每一个页面就是一个Activity,每次使用Intentfinsh来切换和关闭页面,不仅耗费资源,而且切换生硬,并且不便于扩展维护,现在Jetpack加入了Navigation,可以可视化的设计页面的跳转,把一个活动只作为容器,页面内容存放在Fragment中,也提高了日后的维护和扩展

1、Fragment

创建Fragment

在Android Studio中创建Fragment,如同之前创建Activiy一样,可以创建若干个Fragment,每一个Fragment都有对应的XML布局文件,其布局方式同Activity一样:

Fragment不需要在清单文件中声明

Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第1张图片

配置Fragment

Fragment同Activity一样也有生命周期,主要有如下方法
onCreateView()初始化组件,在这里进行组件的绑定
参数分别是:布局文件、组件组、是否是根节点

return inflater.inflate(R.layout.fragment_peo, container, false);

其最重要的方法的onActivityCreated在活动上创建操作
因为Fragment是显示在Activity上的,因此操作需要在该方法内完成,其相当于Activity的onCreate方法;

  • requireActivity()可以在Fragment中获取显示的活动,相当于在Activity中使用this
  • getViewLifecycleOwner()可以感知Fragment的生命周期

在Fragment中也可以复写OptionsMenuContextMenu等方法,其与Activity中一样;

2、Navigation

Navigation即导航,用于设置不同页面之间的切换,在RES目录下新建Navigation文件即可;
Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第2张图片
点击添加按钮添加页面到导航中
Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第3张图片
可以使用每个页面的圆点的指向来定义动作,并可以给每个动作添加动画或者参数
Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第4张图片

3、自定义动画

在Res中新建Animation文件,在文件中使用标记语言来书写动画:

  • 移动,包含fromXDeltatoXDelta等,即从X轴的位置,移动到X轴的位置;
  • duration持续时间,动画的持续时间;
  • 选中,使用同移动相同,需要设置pivotX\Y来告知旋转中心;
  • 透明度……

4、在Activy中布局

在Activiry中添加或者NavHostFragment,并选中之前设置的Fragment/Navigation即可

建议使用NavHostFragment,可以自动导入Fragment;

Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第5张图片

5、调用Navigation进行切换

获取导航控制器,参数为当前的视图或者活动

//获取导航控制器,参数为当前页面
                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;
            }

效果展示:
Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第6张图片
Jetpack学习笔记(四):Navigation切换页面、动画效果以及简单的手势操作_第7张图片

性能优化

当数据更改,适配器每一次都会重新刷新,但是其实大部分资源并没有改变,而且动画非常生硬,这时候可以修改之前的适配器,将继承的RecyclerView.Adapter修改为更加高级的ListAdapter,这样系统会自动的比较数据的改变,从而不需要每次都全部刷新,使用的资源跟小,并且动画更加流畅(有添加和消失的动画
ListAdapter接口需要两个参数

  • 管理的资源对象Objec
  • 控件单元组ViewHolder
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>>() 

你可能感兴趣的:(java,android)