分类: C/C++
2016-12-23 17:33:00
布局是由内容区域和删除区域两部分组成,所以这里自定义view采用继承帧布局的形式,定义两个view为contentView和deleteVIew
重写onFinishInflate(),这个方法是在布局加载完成之后调用的,在这里按照顺序将其子view分别定义为contentView和deleteView。
@Override protected void onFinishInflate() { super.onFinishInflate(); contentView = getChildAt(0); deleteView = getChildAt(1); }
在onSizeChanged中计算出来两个view的宽高
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); contentWidth = contentView.getMeasuredWidth(); contentHeight = contentView.getMeasuredHeight(); deleteWidth = deleteView.getMeasuredWidth(); deleteHeight = deleteView.getMeasuredHeight(); }
ok,到这里的时候我们的布局中的内容区域和删除区域是重叠的,如下图所示:
我们需要的就是重新摆放的两个区域的位置,那么就需要重写onLayout方法。代码比较简单,不在细说
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); contentView.layout(0,0,contentWidth,contentHeight); deleteView.layout(contentWidth,0,contentWidth+deleteWidth,deleteHeight); }
重新build一下我们的xml,然后就能看到他们各司其职,都回到了我们想要的位置上了。
到这里,我们的布局基本上是完成了,下边就是滑动了。
ViewDraghelper通常定义在ViewGroup的内部,并通过静态工厂的方法进行初始化//第一个参数是要监听的view,第二个参数是ViewDragHelper的一个回调 ViewDragHelper viewDraghelper = ViewDragHelper.create(this,callback)
//当前触摸的子控件,返回值代表当前控件是否可以滑动。 //这里只有两个子控件,并且两个都可以被拖动,所以返回如下 @Override public boolean tryCaptureView(View child, int pointerId) //我们这里只有内容区域和删除区域,两个都是可滑动的,所以可以直接返回true。 return child == contentView || child == deleteView; }2. 上边的方法能够让子view跟着手势滑动,但是是随意滑动的,而我们还需要给他们定义滑动规则和范围 首先上边实现的滑动仅仅是单独的某个view的滑动,那么我们需要解决的第一个问题就是让两个一起滑动 那么就需要重写位置改变时候的监听,当某一个子view的位置改变的时候手动的改变另一个view的位置
@Override public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { super.onViewPositionChanged(changedView, left, top, dx, dy); if(contentView == changedView){ deleteView.layout(deleteView.getLeft()+dx,deleteView.getTop(),deleteView.getRight()+dx,deleteView.getBottom()); }else if(deleteView == changedView){ contentView.layout(contentView.getLeft()+dx,contentView.getTop(),contentView.getRight()+dx,contentView.getBottom()); } }3. 到这里,两个view绑定在了一起可以一起滑动了,但是滑动的范围却没有限制,那么我们就来限制一下范围
//这个是当view的位置发生变化时候的回调,对应的还有一个垂直方向上的回调,这里左滑是水平方向的,不需要另一个 @Override public int clampViewPositionHorizontal(View child, int left, int dx) { //限制两个区域能够移动的范围 if(contentView == child){ if(left > 0)left = 0; if(left
用户体验的优化从下面几个方面进行
当我们滑动出来的宽度是deleteview的宽度的一半的时候那就自动打开,否则就自动关闭
那么我们需要再介绍一个方法:onViewReleased()
//这个方法是手指抬起,或者是事件拦截释放掉的回调 @Override public void onViewReleased(View releasedChild, float xvel, float yvel) { super.onViewReleased(releasedChild, xvel, yvel); //我们事先的是左滑,所以contentview的getLeft()是个负数 if(contentView.getLeft()
实现思路:我在这里采用的是,定义一个集合来存放打开状态的item,理论上这个集合永远只有一条数据或者没有,所以在open()中将这一项add进集合,在close()中将这一项remove当有item处于打开状态的时候,触摸滑动是要被拦截的,只能先关闭处于打开状态的,才能再次进行触摸滑动,那么相应的就要在tryCaptureView()中来判断,具体看代码
要解决这个问题我们首先需要清除知道是左右滑动还是上下滑动,我们需要拦截出来上下滑动的事件,如果没有打开项就把这个事件交给item的父布局listview来执行,反之就让item消费。那么相应的就是在item的onTouchEvent(MotionEvent ev)中判断了
//上下滑动的距离大于左右滑动的距离 else if(Math.abs(x-lastx)
说到点击事件,我们都会想,listview的item的click事件就可以的,是的,listview有这样的事件,我们来看看。在对应的activity中,我们再onItemclick()中打出toast,点击点击点击!没反应没反应没反应!傻眼了吧,你重写了item的onTouchEvent,事件在这里执行了,那里肯定没反应了。来解决一下吧!
想想点击事件,那么就是按下和抬起的时候是一样的,那么久作为点击事件,我们在item的onTouchEvent()中判断出来点击行为,代码如下:
case MotionEvent.ACTION_UP: int upx = (int) event.getX(); int upy = (int) event.getY(); //按下和抬起时候是一样的,那么就是点击事件 if(upx == clickx && upy == clicky){ AdapterView.OnItemClickListener itemClickListener = listView.getOnItemClickListener(); if(itemClickListener != null){ int position = (int) getTag(getId()); itemClickListener.onItemClick(listView,this,position,position); } }
判断出点击行为之后,我们要做的就是点击事件,既然listview有onItemClickListener,那么我们就能相应的get到,如上代码,get到这个点击事件,然后再得到当前点击的position,然后执行onItemClick即可。
有了上边的分析,下面就是具体的操作了,我们要get到这个点击事件,那么就需要拿到listview,可以通过set方法传递到这个类,需要position,我们也需要传递过来。这样的操作肯定要在adapter中了。我们通过setTag的方式将position传递过来即可。
代码实现:
@Override public View getView(int i, View convertView, ViewGroup viewGroup) { ViewHolder holder; if(convertView == null){ holder = new ViewHolder(); convertView = LayoutInflater.from(context).inflate(R.layout.custom_item,null); holder.tv = (TextView) convertView.findViewById(R.id.tv_content); holder.delete_tv = (TextView) convertView.findViewById(R.id.tv_delete); holder.customItem = (CustomItem) convertView; convertView.setTag(holder); }else { holder = (ViewHolder) convertView.getTag(); } //listview传递到item中,避免空指针,在item中可以做一下判空,上边我没有做哈 holder.customItem.setListView(listView); //setTag的形式将pisition存起来,在item中取出来 holder.customItem.setTag(holder.customItem.getId(),i);//settag,在customItem中gettag拿出position holder.tv.setText(list.get(i)); holder.delete_tv.setOnClickListener(this); holder.delete_tv.setTag(i); return convertView; }
主要代码就是上述代码块中的粗斜体。再来试试,你的toast已经出来了。还没回截取动态图,将就着看看,感兴趣了动手试试呗
以上只是实现了左滑和点击事件,还有删除事件等等,感兴趣的自己试试呗。如果上述描述有什么不严谨的地方,或者是有更好的方案的话欢迎指正。
后期会进一步解决一下侧滑和左滑结合在一起的冲突问题,然后再来更新。需要代码的回头我会上传,来源。