不知道有没有人有疑问,按照之前的做法的却可以做到竖向拖动,横向删除的ListView了。才发现RecyclerView本身并不提供Item的点击事件,那该怎么办呢?先不急,可以先看看之前提过的类ItemTouchHlper。看看这个类是怎么让这名复杂的操作变得这样简单的。
可以看到,里面有一个属性实现了接口OnItemTouchListener,接口定义了三个方法:
OnInterceptTouchEvent,OnTouchEvent,OnRequestDisallowInterceptTouchEvent
看到这就很明显了,RecyclerView向外暴露了他的这三个方法的入口。分别是:
触摸事件拦截,触摸事件,和申请取消时间拦截,一般重写ViewGroup的时候主要的事件处理方法都提供了,难怪可以在不继承RecyclerView的情况下处理复杂的拖弋事件了。
在后面的绑定事件中,RecyclerView.addItemTouchListener()添加了ItemTouchListener对象。同理可知,也可以通过添加ItemTouchListener对象进行点击事件的监听了。
看了源码才发现,平凡底下别有洞天。在这里就不再展开论述。
在方便的同时也引起了我的沉思,在没有对原控件RecyclerView进行修改封装的情况下,RecyclerView发生了巨大的变化。这不能不感叹程序架构起到了巨大的作用。在开发程序的过程中,开发设计人员一直都希望程序的耦合性尽可能的低。就好像人干了坏事后都想把自己外面带,俗称“甩锅”。其实这跟干坏事没关系,又比如很多人都会买外置USB插排。有时候鼠标键盘或者U盘要经常插拔,USB插口就很容易发生松动。如果是USB插排的USB插口松动了,换一个就可以,便宜。要是电脑本机的USB松了,要换就得换主板了,代价可想而知。所以在程序设计的时候就要尽可能的减低模块间的依赖关系。上述是一个很好的例子。我也模仿写了一个例子:
public class ListItemTouchHelper implements RecyclerView.OnItemTouchListener{
private CallBack callBack;
private RecyclerView recyclerView;
private GestureDetector gestureDetector;
private int touchSlop;
private RecyclerView.ViewHolder Selected;
private RecyclerView.ViewHolder PreSelected;
private float origPointX;
private float origPointY;
private float xyProportion;
private int ActivityPointerID = -1;
public ListItemTouchHelper(@NonNull CallBack callBack){
this.callBack = callBack;
}
public void attRecyclerView(@NonNull RecyclerView recyclerView){//绑定RecylerView
if (this.recyclerView != null && recyclerView.equals(this.recyclerView)) {
return;
}else if(this.recyclerView != null && !recyclerView.equals(this.recyclerView)){
destory();
}
this.recyclerView = recyclerView;
this.recyclerView.addOnItemTouchListener(this);
gestureDetector = new GestureDetector(this.recyclerView.getContext(),listener);
touchSlop = ViewConfiguration.get(recyclerView.getContext()).getScaledTouchSlop();
DisplayMetrics d = recyclerView.getContext().getResources().getDisplayMetrics();
xyProportion = d.widthPixels * 1.0f/d.heightPixels;
}
private void destory(){//销毁旧的事件监听
this.recyclerView.removeOnItemTouchListener(this);
this.recyclerView = null;
}
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);;
int action = e.getAction();
if(action == MotionEvent.ACTION_DOWN){
ActivityPointerID = MotionEventCompat.getPointerId(e,0);
origPointX = MotionEventCompat.getX(e,0);
origPointY = MotionEventCompat.getY(e,0);
}else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
selected(null);
ActivityPointerID = -1;
}else if(action == MotionEvent.ACTION_MOVE){
canSelect(e);
}
return Selected != null;
}
@Override
public void onTouchEvent(RecyclerView rv, MotionEvent e) {
gestureDetector.onTouchEvent(e);
if(e.getAction() == MotionEvent.ACTION_UP){
selected(null);
}
}
@Override
public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {}
//判断Item的选中事件
private boolean canSelect(MotionEvent event){
int action = MotionEventCompat.getActionMasked(event);
if(action != MotionEvent.ACTION_MOVE) return false;
if(recyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) return false;
RecyclerView.ViewHolder holder = findViewHolder(event);
if(holder == null) return false;
int index = MotionEventCompat.findPointerIndex(event,ActivityPointerID);
float x = MotionEventCompat.getX(event,index);
float y = MotionEventCompat.getY(event,index);
float dx = x - origPointX;
float dy = y - origPointX;
float absdx = Math.abs(dx);
float absdy = Math.abs(dy);
if(absdx < touchSlop && absdy < touchSlop) return false;
if(absdx < absdy * xyProportion) return false;
ActivityPointerID = MotionEventCompat.getPointerId(event,0);
selected(holder);
return true;
}
private void selected(RecyclerView.ViewHolder holder){
callBack.selectChange(recyclerView,Selected,holder);
Selected = holder;
}
private GestureDetector.SimpleOnGestureListener listener = new GestureDetector.SimpleOnGestureListener(){
@Override
public boolean onSingleTapUp(MotionEvent e) {
int x = (int)MotionEventCompat.getX(e,0);
int y = (int)MotionEventCompat.getY(e,0);
RecyclerView.ViewHolder holder = findViewHolder(e);
callBack.singleTap(holder,x,y);
return super.onSingleTapUp(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
if(Selected != null)
callBack.scroll(Selected,distanceX);
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
if(Selected != null)
callBack.fing(Selected,velocityX);
return super.onFling(e1, e2, velocityX, velocityY);
}
};
//根据事件获取事件下的ViewHolder对象
private RecyclerView.ViewHolder findViewHolder(MotionEvent event){
float x = MotionEventCompat.getX(event,0);
float y = MotionEventCompat.getY(event,0);
View view = recyclerView.findChildViewUnder(x,y);
if(view == null) return null;
return recyclerView.getChildViewHolder(view);
}
//事件回调接口
public interface CallBack{
public void selectChange(RecyclerView parent,RecyclerView.ViewHolder old_holder,RecyclerView.ViewHolder holder);
public void scroll(RecyclerView.ViewHolder holder,float distanceX);
public void fing(RecyclerView.ViewHolder holder,float VelocityX);
public void singleTap(RecyclerView.ViewHolder holder,float x,float y);
}
}
这个例子是实现上篇那个滑动显示取消和删除按钮的ListView的案例
在实现Fragment的OnCreateView方法的时候,就几句短短的代码完结了。简洁明了,一目了然。
机器用的是机器语言,但一般人看不懂,所以就有开发语言的诞生了,通过编译器编译成机器能看懂的机器语言。所以编写代码的过程中非常强调代码的可读性,所以代码的简洁就显得非常重要了。对于可读性,我大学的导师曾经跟我们做过一次讨论,讨论的议题就是关于代码注释的问题。其中有观点说,写注释会使得代码变得不简洁,而且很多用注释的框架,就会使得代码会变得相当混乱,如果不用注释也能让别人看得懂你的代码,那么写代码注释就不重要了。这个观点就见仁见智了。
附上我的代码吧:https://github.com/rj11304/CardPager
后续:http://blog.csdn.net/rj113/article/details/66510609