Android应用中实现拖拽排序及添加阴影的方式
来源:互联网 发布:常见最流行的网络用语 编辑:程序博客网 时间:2024/05/16 00:58
--- by Anwei.shi
背景介绍:
Android 应用开发中避免不了对容器中的各个Item的进行拖拽排序等操作,比如锤子手机中著名的OneStep功能(拖拽),以及Android 系统中语言设置菜单的快速排序功能。前段时间由于项目需求,需要对悬浮球中的菜单项实现如上的快速拖动排序删除等功能。具体实现:
1.拖拽排序
参看AndroidSetting语言设置项源码,主要为LocaleDragAndDropAdapter.java和LocaleRecyclerView.java。在这两个类中主要是利用了RecycleView的辅助类ItemTouchHelper.class进行相关的拖拽排序等功能。该类的Google官方描述:
This is a utility class to add swipe to dismiss and drag& drop support to RecyclerView. It works with a RecyclerView and a Callbackclass, which configures what type of interaction are enabledand also receives events when user performs these actions. Depending on which functionality you support, you should override{@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or{@linkCallback#onSwiped(ViewHolder,int)}.
从以上可以看出实现拖拽必须重写onMove()和onSwiped()这两个方法。Android的布局方式为上下左右,该如何控制拖拽的方向呢?继续阅读Android源码,有getMovementFlags(RecyclerViewrecyclerView, RecyclerView.ViewHolder viewHolder)的抽象方法,其描述为
Should return a composite flag which defines the enabledmove directions in each state(idle, swiping, dragging).
大意是返回的是一个具有移动方向的值flag。根据以上内容,实现如下代码:
private ItemTouchHelper helper= new ItemTouchHelper(new ItemTouchHelper.Callback(){
@Override
public int getMovementFlags(RecyclerViewrecyclerView, RecyclerView.ViewHolder viewHolder){
//首先回调的方法返回int表示是否监听该方向
int dragFlags = ItemTouchHelper.UP| ItemTouchHelper.DOWN;//拖拽
int swipeFlags = ItemTouchHelper.LEFT|ItemTouchHelper.RIGHT;//侧滑删除
return makeMovementFlags(dragFlags, swipeFlags);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target){
//滑动事件
Collections.swap(mMenuDataSugarList, viewHolder.getAdapterPosition(), target.getAdapterPosition()); mRecyAdapter.notifyItemMoved(viewHolder.getAdapterPosition(), target.getAdapterPosition());
isDraySwapPosition =true;
return false;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,int direction){
//侧滑事件
mMenuDataSugarList.remove(viewHolder.getAdapterPosition()); mRecyAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
@Override
public boolean isLongPressDragEnabled() {
//是否可拖拽
return true;
}
@Override
public boolean isItemViewSwipeEnabled() {
return true;
}
}
将helper与相应的recycleview,采用如下代码进行关联,
helper.attachToRecyclerView(mMenuSettingRecyclerview);
则很容易实现recycleview的item上下拖动排序与左右滑动删除。可是实际测试却发现,每次需要拖动排序时,总需要按压一段时间才会有效果,体验不是很好。可不可以设置一个快速拖动按钮呢?继续查看ItemTouchHelper.class源码,发现startDrag(ViewHolderviewHolder)这个方法,google注释是这么说的:TheViewHolder to start dragging. It must be a direct child of RecyclerView.前半句说明这个方法就是用来立即执行拖动的,可是后半句说明这个holder必须由recycleview的子Item进行调用。如何将recycleview子Item的事件,传递给整个helper辅助类呢?带着疑问首先就想到了接口,所以定义一个接口:
public interface OnStartDragListener {
void onStartDrag(RecyclerView.ViewHolder viewHolder);
}
在Item快速拖动menu被触摸时,回调该接口
holder.mItemMenuHomeIcon.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event){
if (MotionEventCompat.getActionMasked(event)== MotionEvent.ACTION_DOWN)
{
mDragStartListener.onStartDrag(holder);
}
return false;
}
});
然后在recycleView所在类实现该接口中的方法,
@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder){
helper.startDrag(viewHolder);
}
以上分别基本实现了recycleview的正常拖动与快速拖动排序。补充一点,由于本人所负责项目中的数据需要实时的保存到数据库,及时更新到悬浮球item上中,而且大多数的项目也都离不开数据库操作。原本我是在onMove以及onSwiped中进行list数据的保存。慢速拖动时,数据库还能较及时的写入新的数据,可是在快速拖动时,一秒钟可能来回拖动数十次,onMove方法被回调了数百次甚至更多,频繁的写入数据库对系统性能有着较明显的影响。后来我将数据库的保存操作放在FragmentonStop周期方法内,这样避免了数据库的频繁写入,可是只有该fragment销毁之后,悬浮球的菜单更改项才能生效,这个会对实时性有所影响。有没有什么折中的办法呢?后来在同事的提醒下,可以将数据保存操作放在拖动松开的时刻。重写RecycleViewonTouch 方法:
mMenuSettingRecyclerview.setOnTouchListener(new View.OnTouchListener(){
@Override
public boolean onTouch(View v, MotionEvent event){
if (event.getAction()== MotionEvent.ACTION_UP&& isDraySwapPosition){
saveMenuDataSugar();//保存拖动之后的数据到数据库
isDraySwapPosition =false;//用来标示recycleview是否被拖动排序
}
return false;
}
});
按以上方法,着实较好的解决了性能与时效性的均衡性。
2.添加阴影
最后,来实现一下,如何在拖动某个Item时,添加阴影效果。开始的时候,我按照传统的添加阴影效果再布局之上使用selector选择器:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/button_bg_press"/>
<item android:state_focused="true" android:drawable="@drawable/button_bg_press"/>
<item android:state_pressed="true" android:drawable="@drawable/button_bg_press" />
<item android:drawable="@drawable/button_bg_normol" />
</selector>
实际的效果的为在刚刚touch到recycleView的Item项时就会有相应的阴影,可是此时并没有拖动啊?显然,这与实际需求效果不一致。必须在拖动开始时添加阴影,结束之后,移除相应的效果。在ItemTouchHelper.class类中拖动与删除有onMove和onSwiped方法,选中有onSelectedChanged方法,我们可以将其阴影效果的添加放在该方法。但是在该类中却没有发现onUnSelectedChanged方法,继续查看源码,有这么一个方法clearView,其描述为:Thisis a good place to clear all changes on the View that was done in,大意为:如果我们需要清除一个view的改变,则需要在该处进行实现。综上,可以在这两个方法的内部进行阴影的添加与删除。
首先定义一个接口:(该接口的主要作用实现抽象类,并不是真正意义的回调)
public interface ItemTouchHelperViewHolder{
void onItemSelected();
void onItemClear();
}
重写ItemTouchHelper.class中以下两个方法:
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder,int actionState){
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE){
if (viewHolder instanceof ItemTouchHelperViewHolder){
ItemTouchHelperViewHolderitemViewHolder =(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemSelected();
}
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){
super.clearView(recyclerView, viewHolder);
if (viewHolder instanceof ItemTouchHelperViewHolder){
ItemTouchHelperViewHolderitemViewHolder =(ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
最后在ItemViewHolder中实现该方法。
@Override
public void onItemSelected() {
itemView.setElevation(30);
itemView.setBackgroundResource(R.drawable.bg_item_selected);
}
@Override
public void onItemClear() {
itemView.setElevation(0);
itemView.setBackgroundColor(0);
}
最终效果如下动图
总结:
本文主要是针对项目中使用到RecycleView拖动排序功能,基于Android原生行为的较为完整实现。在网上搜索RecycleView拖动排序,或许能够搜出很多,但大多都是只是简单的讲解一个普通排序,而快速排序和添加阴影等却很少有人提过。通过该此次项目也算是对Android源码的阅读和学习有着进一步的认识,当我们遇到问题没有思路时,通过网络等很容易了解到大致内容,但是,开发中遇到特别深入的内容时,我们还是需要阅读源码,不然即使知其然却不知其所以然。路漫漫其修远兮,吾将上下而求索,阅读源码之路还是很漫长的!- Android应用中实现拖拽排序及添加阴影的方式
- Android图片添加阴影效果的两种方式
- Android图片添加阴影效果的两种方式
- Android图片添加阴影效果的两种方式
- ios、html、Android中阴影的实现,及注意的问题
- Android中按钮单击事件及监听器的实现方式
- Android中实现推送方式的基础知识及相关解决方案
- Android中实现IPC的方式及使用场景
- Android--添加阴影效果--CardView的使用
- 在Android中实现阴影效果 背景阴影
- Android中从一个应用中启动另外的应用的实现方式
- android textView添加阴影
- android 添加阴影引导
- Android Laucnher3 拖拽应用图标阴影投放位置
- android中给TextView或者Button的文字添加阴影效果
- android中给TextView或许Button的文字添加阴影效果
- Android xml中为文字添加阴影(shadow方法)
- TextView中的文字添加阴影效果及Style的使用
- 站外SEO应该怎么做?
- Leetcode22.+Leetcode216. 回溯法之应用(二):圆括号+组合问题
- 时间选择器
- [BZOJ4597][SHOI2016]随机序列(线段树)
- android第三方QQ登录授权
- Android应用中实现拖拽排序及添加阴影的方式
- sqlserver视图使用示例
- JAVA课程4 类 描述计算机CPU速度与硬盘容量
- java反射实现对象复制
- 献给CSS3初学者:常用易混的样式用法总结
- JSP中meta标签之详解
- Android WebView使用笔记(一)
- ListView快速添加头部
- 假装是一个成功的布局——2、布局