使用过今日头条的伙计们对这个效果肯定很熟悉。拖拽可排序,点击标签后可以删除。今天我们采用RecyclerView来实现。
实现思路:
通过ItemTouchHelper来绑定RecyclerView的子控件触摸事件。
当滑动拖拽的时候,通知适配器来交换两个子控件的显示位置。
更改数据源,使数据源与子空间显示内容一致。
这就是实现的基本思路,是不是很简单?当然,首先要了解一下ItemTouchHelper这哥们儿是干啥的,有什么作用。
This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.
Depending on which functionality you support, you should override onMove(RecyclerView, ViewHolder, ViewHolder) and / or onSwiped(ViewHolder, int).
This class is designed to work with any LayoutManager but for certain situations, it can be optimized for your custom LayoutManager by extending methods in the ItemTouchHelper.Callback class or implementing ItemTouchHelper.ViewDropHandler interface in your LayoutManager.
By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. You can customize these behaviors by overriding onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, boolean) or onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean).
Most of the time you only need to override onChildDraw.
通过API文档的介绍,这个哥们儿是为RecyclerView工作的,他需要一个CallBack,可以回调RecyclerView的子控件滑动和拖拽事件,而且也可以通过这个CallBack重绘我们的子view。这就一目了然了嘛。通过使用ItemTouchHelper,可以很轻松的就实现了RecyclerView触摸事件的回调。换句话说,只要我们为RecyclerView 绑定了ItemTouchHelper之后,RecyclerView子控件的拖动和滑动事件已经帮我们实现了。我们所要做的就是在触摸事件之后,如何去改变去更新Adapter和改变我们的数据。
开始撸码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
|
public
class
ChannelActivity
extends
Activity {
public
RecyclerView rv;
public
List<DataBean> list =
new
ArrayList<>();
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_channel);
initData();
initView();
}
private
void
initView() {
rv = (RecyclerView) findViewById(R.id.rl_view);
rv.setLayoutManager(
new
GridLayoutManager(
this
,
4
));
MyAdapter adapter =
new
MyAdapter(
this
, list);
rv.setAdapter(adapter);
//关联ItemTouchHelper
ItemTouchHelper touchHelper =
new
ItemTouchHelper(
new
MyItemTouchCallBack(adapter));
touchHelper.attachToRecyclerView(rv);
}
private
void
initData() {
DataBean bean1 =
new
DataBean(
"体育"
,
0
,
"url"
);
DataBean bean2 =
new
DataBean(
"新闻"
,
1
,
"url"
);
DataBean bean3 =
new
DataBean(
"影视"
,
2
,
"url"
);
DataBean bean4 =
new
DataBean(
"电视剧"
,
3
,
"url"
);
DataBean bean5 =
new
DataBean(
"热点"
,
4
,
"url"
);
DataBean bean6 =
new
DataBean(
"推荐"
,
5
,
"url"
);
DataBean bean7 =
new
DataBean(
"屌丝男士"
,
6
,
"url"
);
DataBean bean8 =
new
DataBean(
"音乐"
,
7
,
"url"
);
DataBean bean9 =
new
DataBean(
"电影"
,
8
,
"url"
);
list.add(bean1);
list.add(bean2);
list.add(bean3);
list.add(bean4);
list.add(bean5);
list.add(bean6);
list.add(bean7);
list.add(bean8);
list.add(bean9);
}
}
|
自定义MyItemTouchCallBack
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
public
class
MyItemTouchCallBack
extends
ItemTouchHelper.Callback {
private
TouchInterface touchInterface;
public
MyItemTouchCallBack(TouchInterface touchInterface) {
this
.touchInterface = touchInterface;
}
@Override
public
int
getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
//拖拽
int
dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT;
//滑出屏幕
int
swipeFlags = ItemTouchHelper.RIGHT | ItemTouchHelper.LEFT | ItemTouchHelper.UP | ItemTouchHelper.DOWN;
return
makeMovementFlags(dragFlags,
0
);
}
@Override
public
boolean
onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
int
position_target = target.getLayoutPosition();
int
position = viewHolder.getLayoutPosition();
//滑动事件回调到了Adapter,用来处理数据
touchInterface.onMove(position, position_target);
return
true
;
}
//标签动画持续时间,默认是250
@Override
public
long
getAnimationDuration(RecyclerView recyclerView,
int
animationType,
float
animateDx,
float
animateDy) {
return
super
.getAnimationDuration(recyclerView, animationType, animateDx, animateDy);
}
/**
* 是否可以长按拖拽,默认是true
*
* @return
*/
@Override
public
boolean
isLongPressDragEnabled() {
return
super
.isLongPressDragEnabled();
}
/**
* 标签划出去的回调,direction是滑动的方向
*
* @return
*/
@Override
public
void
onSwiped(RecyclerView.ViewHolder viewHolder,
int
direction) {
}
}
|
Adapter类中处理数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
public
class
MyAdapter
extends
RecyclerView.Adapter<MyViewHolder>
implements
TouchInterface {
private
Context context;
//是否显示delete
public
boolean
isShow;
public
List<DataBean> getList() {
return
list;
}
public
void
setList(List<DataBean> list) {
this
.list = list;
}
private
List<DataBean> list;
public
MyAdapter(Context context, List<DataBean> list) {
this
.context = context;
this
.list = list;
}
@Override
public
MyViewHolder onCreateViewHolder(ViewGroup parent,
int
viewType) {
MyViewHolder viewHolder =
new
MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_layout, parent,
false
));
return
viewHolder;
}
@Override
public
void
onBindViewHolder(MyViewHolder holder,
final
int
position) {
holder.tv_des.setText(list.get(position).name);
holder.tv_des.setOnClickListener(
new
View.OnClickListener() {
@Override
public
void
onClick(View v) {
DataBean bean = list.remove(position);
notifyDataSetChanged();
Toast.makeText(context,
"删除了"
+bean.name+
"频道"
,Toast.LENGTH_SHORT).show();
}
});
holder.tv_des.setOnLongClickListener(
new
View.OnLongClickListener() {
@Override
public
boolean
onLongClick(View v) {
isShow =
true
;
notifyDataSetChanged();
return
true
;
}
});
if
(isShow) {
holder.iv_icon.setVisibility(View.VISIBLE);
}
else
{
holder.iv_icon.setVisibility(View.GONE);
}
}
@Override
public
int
getItemCount() {
return
list.size();
}
@Override
public
void
onMove(
int
currentPosition,
int
targetPosition) {
Collections.swap(list, currentPosition, targetPosition);
if
(targetPosition < currentPosition) {
List<DataBean> subList = list.subList(targetPosition +
1
, currentPosition +
1
);
//向右移一位
rightStepList(
0
, subList);
}
else
{
List<DataBean> subList = list.subList(currentPosition, targetPosition);
//向左移一位
leftStepList(
0
, subList);
}
notifyItemMoved(currentPosition, targetPosition);
}
}
class
MyViewHolder
extends
RecyclerView.ViewHolder {
public
ImageView iv_icon;
public
TextView tv_des;
public
MyViewHolder(View itemView) {
super
(itemView);
iv_icon = (ImageView) itemView.findViewById(R.id.iv_icon);
tv_des = (TextView) itemView.findViewById(R.id.tv_des);
}
}
|
解释一下onMove方法,例如:我们的数据是[1,2,3,4,5,6],当6移动到3的位置时,那么数据源最后变化为[1,2,6,3,4,5]。但是在显示的时候我们先是将当前position和targetposition对调[1,2,6,4,5,3],然后取出[4,5,3]进行右移一位,这样数据源就对上了。如果是从3移动到6进行左移就可以了,数据排序的算法,采用反转的思想。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
public
class
DataUtils {
/**
* 利用反转的思想对数据进行排序
* 例如:list{0,1,2,3,4,5,6,7} 左移一位
* 第一步:第一位先反转{0,1,2,3,4,5,6,7}
* 第二部:剩下的在反转{0,7,6,5,4,3,2,1}
* 第三步:全部反转{1,2,3,4,5,6,7,0}
*
* 例如:list{0,1,2,3,4,5,6,7} 右移一位
* 第一步:最右边一位先反转{1,2,3,4,5,6,7}
* 第二部:剩下的在反转{6,5,4,3,2,1,0,7}
* 第三步:全部反转{7,6,5,4,3,2,1,0}
*
* 因为list的index是从0开始的,step要相应的-1
* 优点:少创建对象,优化内存
*
* @param start
* @param end
* @param list
*/
public
static
void
reverseList(
int
start,
int
end,List list){
int
count = (end+
1
-start)/
2
;
for
(
int
i =
0
;i< count;i++){
Object temp = list.get(start+i);
list.set(start+i,list.get(end-i));
list.set(end-i,temp);
}
}
public
static
void
leftStepList(
int
step,List list){
int
size = list.size() -
1
;
//左移
reverseList(
0
,step,list);
reverseList(step+
1
,size,list);
reverseList(
0
,size,list);
}
public
static
void
rightStepList(
int
step,List list){
int
size = list.size() -
1
;
//右移
reverseList(size-step,size,list);
reverseList(
0
,size-step-
1
,list);
reverseList(
0
,size,list);
}
}
|
Activity布局文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:id
=
"@+id/activity_channel"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
tools:context
=
"com.iwintrue.channe.ChannelActivity"
>
<
android.support.v7.widget.RecyclerView
android:id
=
"@+id/rl_view"
android:background
=
"@color/white"
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
></
android.support.v7.widget.RecyclerView
>
</
RelativeLayout
>
|
子控件布局文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
android:orientation
=
"vertical"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
>
<
RelativeLayout
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:padding
=
"10dp"
>
<
TextView
android:id
=
"@+id/tv_des"
android:layout_width
=
"80dp"
android:layout_height
=
"30dp"
android:text
=
"屌丝男士"
android:gravity
=
"center"
android:background
=
"@drawable/rl_shape"
android:textColor
=
"@color/textColor"
android:layout_marginTop
=
"5dp"
android:layout_marginRight
=
"5dp"
/>
<
ImageView
android:layout_alignRight
=
"@+id/tv_des"
android:layout_marginRight
=
"-5dp"
android:id
=
"@+id/iv_icon"
android:layout_width
=
"20dp"
android:layout_height
=
"20dp"
android:src
=
"@mipmap/delete"
android:scaleType
=
"fitXY"
android:visibility
=
"gone"
/>
</
RelativeLayout
>
</
LinearLayout
>
|
实现效果: