您的位置:首页 > 移动开发 > Android开发

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都是能实现侧滑效果的,大家多多举一反三。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息