listview 侧滑删除 仿qq
2015-09-21 19:06
363 查看
侧滑原理:
侧滑是通过手势判断,将中间的view滑出去,将右边的view滑进来,当滑动的距离不够完全显示右边的view的时候执行平滑函数,自动将右边的view滑进去,并改变滑动状态为open。所以需要通过xml定义两个view,一个作为默认显示的view,一个作为隐藏(需要滑入)的view。先从简单练起:
开始我做了一个简单的demo,没有涉及listview,只是写了简单的一个滑入滑出的效果。public class ScrollerLinearLayout extends LinearLayout { private View centerView = null; //不滑动显示的view private View rightView = null; //左滑显示的view //用这两个可以实现滑动效果 private ScrollerCompat mOpenScroller; private ScrollerCompat mCloseScroller; private int downX; //开始按下的位置 //记录状态 private int state = STATE_CLOSE; private static final int STATE_CLOSE = 0; private static final int STATE_OPEN = 1; private int mBaseX; public ScrollerLinearLayout(Context context) { super(context); } public ScrollerLinearLayout(Context context, AttributeSet attrs) { super(context, attrs); } public ScrollerLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } public void setContentView(View centerView, View rightView){ this.centerView = centerView; this.rightView = rightView; //初始化mColoseScroller和mOpenScroller mCloseScroller = ScrollerCompat.create(getContext()); mOpenScroller = ScrollerCompat.create(getContext()); initView(); } //child view的布局参数设定好后 添加到parent view里面 private void initView() { // setLayoutParams(new AbsListView.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT)); //这也是设置宽和高 LinearLayout.LayoutParams contentParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT); centerView.setLayoutParams(contentParams); rightView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT)); //将这两个布局都add到这个view中 addView(centerView); addView(rightView); } // 判断是否滑出的状态 public boolean isOpen() { return state == STATE_OPEN; } /** * 这里的函数是由 listview来控制的。 * listview来监听左滑,并判断是否需要将rightview 显示出来。 * (因为不是所有左滑都要画出rightview,还需要判断listview的其他item的状态不是STATE_OPEN 状态 * ,所以由listview来控制自己item的rightview是否左滑最合理,这个函数目的就是让listview来调用) * 反之右滑亦然 * * @param event * @return */ public boolean onSwipeLeft(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downX = (int) event.getX(); break; case MotionEvent.ACTION_MOVE: int dis = (int) (downX - event.getX()); if (state == STATE_OPEN) { dis += rightView.getWidth(); } //手指移动多少距离,rightview也移动多少距离 swipe(dis); break; case MotionEvent.ACTION_UP: if ((downX - event.getX()) > (rightView.getWidth() / 2)) { smoothOpenMenu(); //自动滑出来 } else { smoothCloseMenu(); //自动滑进去 return false; } break; } //消费掉事件 return true; } @Override public void computeScroll() { if (state == STATE_OPEN) { if (mOpenScroller.computeScrollOffset()) { swipe(mOpenScroller.getCurrX()); postInvalidate(); } } else { if (mCloseScroller.computeScrollOffset()) { swipe(mBaseX - mCloseScroller.getCurrX()); postInvalidate(); } } } private void swipe(int dis) { if (dis > rightView.getWidth()) { dis = rightView.getWidth(); } if (dis < 0) { dis = 0; } centerView.layout(-dis, centerView.getTop(), centerView.getWidth() - dis, getMeasuredHeight()); rightView.layout(centerView.getWidth() - dis, rightView.getTop(), centerView.getWidth() + rightView.getWidth() - dis, rightView.getBottom()); } public void smoothCloseMenu() { state = STATE_CLOSE; mBaseX = -centerView.getLeft(); mCloseScroller.startScroll(0, 0, mBaseX, 0, 350); postInvalidate(); } public void smoothOpenMenu() { state = STATE_OPEN; mOpenScroller.startScroll(-centerView.getLeft(), 0, rightView.getWidth(), 0, 350); postInvalidate(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if(rightView != null) rightView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if(centerView != null) centerView.layout(0, 0, getMeasuredWidth(), centerView.getMeasuredHeight()); if(rightView != null) rightView.layout(getMeasuredWidth(), 0, getMeasuredWidth() + rightView.getMeasuredWidth(), centerView.getMeasuredHeight()); } @Override public boolean onTouchEvent(MotionEvent event) { onSwipeLeft(event); return true; } }
MainActivity部分:
private void initScrollerLayout() { View centerView1 = LayoutInflater.from(this).inflate(R.layout.view_framelayout_01, null); View rightView1 = LayoutInflater.from(this).inflate(R.layout.view_framelayout_02, null); ScrollerLinearLayout scrollerLinearLayout1 = (ScrollerLinearLayout)super.findViewById(R.id.activity_listview_scroller_layout01); scrollerLinearLayout1.setContentView(centerView1, rightView1); View centerView2 = LayoutInflater.from(this).inflate(R.layout.view_framelayout_01, null); View rightView2 = LayoutInflater.from(this).inflate(R.layout.view_framelayout_02, null); ScrollerLinearLayout scrollerLinearLayout2 = (ScrollerLinearLayout)super.findViewById(R.id.activity_listview_scroller_layout02); scrollerLinearLayout2.setContentView(centerView2, rightView2); }
XML部分:
view_framelayout_01
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:gravity="center_vertical" android:layout_height="match_parent"> <com.best.android.listviewactivity.CircleImageView android:layout_width="50dp" android:layout_height="50dp" /> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="wrap_content"> <RelativeLayout android:layout_margin="4dp" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:text="android交流群" android:textSize="14dp" android:layout_alignParentLeft="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="15:34" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> <RelativeLayout android:layout_margin="4dp" android:layout_width="fill_parent" android:layout_height="wrap_content"> <TextView android:text="Emma加入群" android:textColor="#666666" android:textSize="14dp" android:layout_alignParentLeft="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:text="99+" android:gravity="center" android:textColor="#fff" android:background="@mipmap/green_bg" android:layout_alignParentRight="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </RelativeLayout> </LinearLayout> </LinearLayout>view_framelayout_02:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:background="#444444" android:gravity="center" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:text="置顶" android:textColor="#fff" android:textSize="16sp" android:layout_margin="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> <LinearLayout android:id="@+id/view_framelayout_02_ll_delete" android:background="#ee2222" android:gravity="center" android:layout_width="fill_parent" android:layout_height="fill_parent"> <TextView android:text="删除" android:textColor="#fff" android:textSize="16sp" android:layout_margin="20dp" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout> </LinearLayout>
基本侧滑已经实现,但是不是我们最终想要的效果,所以接下来我们要加入listview
与listview关联:
首先更改上面代码的这个部分(将第一句取消注释,第二句注释掉)// setLayoutParams(new AbsListView.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT)); setLayoutParams(new LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));并将ScrollerLinearLayout里面的ontouchevent函数去掉,因为现在事件监听将在listview里面做判断。
主要难点在自定义listview里面的ontouchEvent方法 这里详细讲里面的逻辑。。。。。
首先梳理下思路: 在action.down的时候我们都需要做哪些呢?
1.记录下按下的位置。2.记录下是listview里面的哪个item
3.状态要改变
4.获取当前的item的view对象。
5.判断item是否和上次点击的item相同,如果不相同那么就直接让view关闭,如果相同那么记录下x坐标
action.move的时候我们做的事情比较简单,记录下移动的距离,判断是否是x方向的移动,是的话就改变滑动状态,并直接调用onswipeleft()执行滑动。否则就是让listview上下滚动了。
action.up,我们就需要判断滑动是否执行到位,不到位就调用smoothOpen方法,将没有滑出的部分滑出,没有滑入的部分滑入。
自定义Listview ontouch部分代码@Override public boolean onTouchEvent(MotionEvent ev) { if (ev.getAction() != MotionEvent.ACTION_DOWN && mTouchView == null) return super.onTouchEvent(ev); int action = MotionEventCompat.getActionMasked(ev); // int disY; action = ev.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: //按住的item的position int oldPos = mTouchPosition; //记录位置 mDownX = ev.getX(); mDownY = ev.getY(); mTouchState = TOUCH_STATE_NONE; //获取点击的item的position mTouchPosition = pointToPosition((int) ev.getX(), (int) ev.getY()); //判断当前点击的是否和上次点击的item是同一个,如果是同一个,并且状态是打开了的就记录状态和坐标 //mTouchView.onSwipeLeft(ev);在down方法里就是记录X坐标的 if (mTouchPosition == oldPos && mTouchView != null && mTouchView.isOpen()) { mTouchState = TOUCH_STATE_X; mTouchView.onSwipeLeft(ev); return true; } //获取当前的item的View View view = getChildAt(mTouchPosition - getFirstVisiblePosition()); //如果不是同一个item 那么点击的话就关闭掉之前打开的item if (mTouchView != null && mTouchView.isOpen()) { mTouchView.smoothCloseMenu(); mTouchView = null; return super.onTouchEvent(ev); } //判断该view的类型 if (view instanceof ScrollerLinearLayout) { mTouchView = (ScrollerLinearLayout) view; } if (mTouchView != null) { mTouchView.onSwipeLeft(ev); } break; case MotionEvent.ACTION_MOVE: float dy = Math.abs((ev.getY() - mDownY)); float dx = Math.abs((ev.getX() - mDownX)); if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { //执行滑动 mTouchView.onSwipeLeft(ev); } return true; } else if (mTouchState == TOUCH_STATE_NONE) { //判断滑动方向,x方向执行滑动,Y方向执行滚动 if (Math.abs(dy) > MAX_Y) { mTouchState = TOUCH_STATE_Y; } else if (dx > MAX_X) { mTouchState = TOUCH_STATE_X; } } break; case MotionEvent.ACTION_UP: if (mTouchState == TOUCH_STATE_X) { if (mTouchView != null) { mTouchView.onSwipeLeft(ev); //如过最后状态是打开 那么就重新初始化 if (!mTouchView.isOpen()) { mTouchPosition = -1; mTouchView = null; } } ev.setAction(MotionEvent.ACTION_CANCEL); super.onTouchEvent(ev); return true; } break; } return super.onTouchEvent(ev); }
listview adapter部分代码
@Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView==null){ View view01 = LayoutInflater.from(context).inflate(R.layout.view_framelayout_01, null); View view02 = LayoutInflater.from(context).inflate(R.layout.view_framelayout_02, null); LinearLayout layoutDelete = (LinearLayout)view02.findViewById(R.id.view_framelayout_02_ll_delete); layoutDelete.setOnClickListener(new OnDeleteClickListener(position)); // convertView = new MyFrameLayout(view01, view02); ScrollerLinearLayout scrollerLinearLayout = new ScrollerLinearLayout(context); scrollerLinearLayout.setContentView(view01, view02); convertView = scrollerLinearLayout; } return convertView;
最后效果图:
等下拉刷新功能完成后,会将源码一起上传上去,不过上面都已经将的很详细了,应该都能实现开发功能了。。。。
最后总结下:其实这个思路不仅仅是实现listview的侧滑,按照这个思路来gridview,recycleview都是能实现侧滑效果的,大家多多举一反三。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories