# DragSort **Repository Path**: smaboy/DragSort ## Basic Information - **Project Name**: DragSort - **Description**: 拖拽排序 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-12-21 - **Last Updated**: 2021-12-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DragSort ### 拖拽排序 **** ##### 撸码原因 --->每当看到那么优秀交互效果,总想着它该怎么实现,所以心动不如行动! ##### 效果图 * 话不多说,先奉上效果图(案例参考的原图,请看参考图例) ##### 撸码准备 * 思路 * 采用RecyclerView,更加灵活。通过布局管理器可以灵活的设置布局 * 通过ItemTouchHelper类来处理触摸事件(如拖拽效果,滑动效果等) * 关键方法,adpter.setLayoutManager()该方法用于设置布局,必须设置不然不会展示出来 ,helper.attachToRecyclerView()该方法用于将触摸处理类关联到RecyclerView上。 ##### 参考图例 参考了东航的效果,自己也想弄个新的。 ##### 编写json 这些内容,我们一般都是来自于服务器的,所以这里我想先写一个json数据,至于数据内容我们可以看着 东航这个来写。 这边有一个在线编辑器: https://qqe2.com/ 大家可以试试。 ##### 开始撸码 ###### 思路流程 - 编写json---->主页面采用的UI(标题+内容;其中recyclerview嵌套recyclerview和gridview实现)----> 模式(编辑、排序采用两个布尔值进行控制)---->主页面的recyclerview的内容设置(我采用多类型来进行动态布局,在绑定view的时候设置数据和监听,通过 编辑和排序来控制布局和监听设置)---->我的服务recyclerview的数据和拖拽排序设置---->外部控制我的服务的拖拽排序的开启---->外部控制编辑模式的 开启---->我的服务拖拽排序的bug修复 - 在上个提交中,我基本已经完成大部分逻辑,包括拖拽,编辑模式,排序UI等。 - 下面我们需要,做到真正的编辑模式,即可以通过行前、行中、行后、智能排序等服务组,往我的服务组中添加服务,也可以在我的服务组中,删除自己不感兴趣的服务,修改我的服务组中的排序等。 思考: * 我们有几种业务情景需要模拟? 1. 我的服务组只是在本地维护(即:当清除用户数据,或者卸载重新安装的时候,是不维护我的服务组里面的内容的。) 该情景下,服务器那边其实只需要维护好行前、行中、行后、智能排序的服务即可,其他由本地维护。 2. 我的服务组内容,是需要和服务器那边同步的。(即:只要我的服务组里面的内容发生了改变,如删除、排序等,都得告知服务器用户的改动) 该情景下,需要增加字段,让服务器告知我,我的服务组中的内容,还有就是其他服务分组的默写服务是否已被添加(这不是必须的,我们也可以通过我的服务组中服务的id号,来确定其他服务组中的服务是否被添加) 综上,每个服务必须配定id唯一标识,还有最好有一个字段标识是否被添加。 ##### 问题记录 1. recyclerview中嵌套recyclerview、gridview的时候如果不给子布局的宽度指定具体的值,他们是不能完整展示的。(目前可行的方法有,在xml布局给子布局 设置具体值,或者在绑定布局的时候设置他们的布局属性) 2. ItemTouchHelper绑定RecyclerView的时候,可在isLongPressDragEnabled回调方法中控制,是否支持长按拖拽。 3. 在编辑模式和模式和非编辑模式切换的时候,首次长按拖拽没问题,后面再回到编辑模式,出现卡顿,不顺畅甚至无法实现拖拽排序。 - 问题所在,当我们切换编辑模式的时候,我们需要刷新适配器,此时我的服务中的recyclerview适配器是新建了,但是我们recycler已经在之前绑定了触摸helper, 导致我们多次绑定,再一个,我们在move方法中进行适配器的刷新的时候,任然用的是首次创建的adapter来刷新,之后我们又新建了adapter导致刷新失败,修改这两处后, 我们就可以正常操作了。 4. 我的服务在编辑状态下,点击右上角的移除图标,第一次有效,后面点击出现数组越界 分析:通过观看日志发现,数组越界 这里我们是直接将,onBindViewHolder中的i回调给onClickItemDelete方法,可是我们在这个方法中只处理了,数据删除的通知 myRecyclerViewAdapter.notifyItemRemoved(position); 该方法并不会重新走onBindViewHolder。 @Override public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, final int i) { myViewHolder.title.setText(mySercices.get(i).getTitle()); if(isEdit){//编辑状态,item不可点,右上角tag表示显示 myViewHolder.iv_edit_tag.setVisibility(View.VISIBLE); myViewHolder.iv_edit_tag.setImageResource(R.drawable.delete); myViewHolder.iv_edit_tag.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //注意,这里不要直接使用pisition,不然会造成排序错乱,应该使用myViewHolder.getAdapterPosition() onClickItemDeleteListener.onClickItemDelete(myViewHolder.getAdapterPosition()); } }); } else {//非编辑状态,item可点,右上角tag不显示 myViewHolder.rootView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent intent =new Intent(); intent.putExtra("title",mySercices.get(i).getTitle()); intent.setClass(mContext,EmptyActivity.class); mContext.startActivity(intent); } }); myViewHolder.iv_edit_tag.setVisibility(View.GONE); } } 5. 设置删除监听 myRecyclerViewAdapter.setOnClickItemDeleteListener(new MyRecyclerViewAdapter.OnClickItemDeleteListener() { @Override public void onClickItemDelete(int position) { //通知其他组,清空添加标识 setServiceFromDefultOtherService(mySercices.get(position).getId(), false); setServiceFromInteligentService(mySercices.get(position).getId(), false); //移除我的服务中选定服务 mySercices.remove(position); myRecyclerViewAdapter.notifyItemRemoved(position); } }); 6. 添加事件的不同意问(当我们从其他服务中添加服务到我的服务组中,添加标识不统一) 解决思路:在点击事件中,设置添加标识,并在回调参数中将要设置的view返回,在一个就是通知其他服务组同一服务的标识要统一 gridViewAdapter.setOnClickItemAddListener(new GridViewAdapter.OnClickItemAddListener() { @Override public void onClickItmeAdd(int position, ImageView iv_edit_tag) { //点击添加按钮后,将服务添加到我的服务列表中,并将当前点击的服务标记为已添加状态(设置背景图片和设置添加字段的标识) mySercices.add(beforeSercices.get(position)); myRecyclerViewAdapter.notifyDataSetChanged(); iv_edit_tag.setImageResource(R.drawable.added); beforeSercices.get(position).setAdded(true); //设置智能排序组的添加标识 setServiceFromInteligentService(beforeSercices.get(position).getId(), true); } }); 7. 删除事件(我的服务组中,在编辑状态下删除服务,其他组不能同步实时更新) 解决思路: 添加标识抹去,获取相应的view或者刷新其他组的适配器 通过id查找在哪个分组,抹去标识,并刷新该分组适配器 7. 取消按钮 - 思路分析 :将各个适配器中的数据还原到编辑之前的状态 - 思考: 1. 由于我们在主适配器中将各服务分组数据设置为成员变量了,所以不能直接用另一个成员变量来保存编辑之前成员变量,因为此时我们保存的只不过 是该成员变量的一个引用,他们的实际数据最终都会指向同一个。(此处须注意)/n 2. 我是将编辑之前的数据保存sp中,如果我们这个模块箱保持和服务器同步,可将我们各服务分组中的数据上传到服务进行保存。这就可以实现,同一账号 在不同设备中,我的服务的统一。 ##### 参考资料 参考博客: https://blog.csdn.net/qq_30379689/article/details/52463139