Android-实现ListView上下滑动删除和编辑(仿微信电话本)

Android-实现ListView左右滑动删除和编辑(仿微信电话本)

有时候,为了实现项目中的需求,完成设计好的用户交互体验,不的不把这些View重新改造成自己想要的效果。

Android原生的ListView是不支持左右滑动的,但是看到微信电话本上,联系人可以左右滑动进行操作的,就通过自己的设想和思路,并加以实现了。

思路:
1.获取到手指放到屏幕时的x,y位置,并判断点击的处于ListView的那个position。
2.判断滑动的方向,如果是上下方向,touch事件就交给ListView处理;如果是左右方向,就禁止ListView进行滑动。
3.根据手指的移动,来移动选中的View。
4.滑动结束后,把View归位。
5.通过传入的监听器,告诉用户是左滑动还是右滑动。

效果图:
Android-实现ListView上下滑动删除和编辑(仿微信电话本)
Android-实现ListView上下滑动删除和编辑(仿微信电话本)
Android-实现ListView上下滑动删除和编辑(仿微信电话本)
Android-实现ListView上下滑动删除和编辑(仿微信电话本)
重新的ListView的代码:

package com.example.wz.view;

import android.annotation.SuppressLint;
import android.content.Context;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

import com.example.wz.R;
import com.example.wz.util.MyLog;
import com.example.wz.util.OpenLooper;
import com.example.wz.util.OpenLooper.LoopCallback;

public class MyListView extends ListView {

    public MyLog log = new MyLog(this, true);

    public float screenWidth;

    public int mTouchSlop;

    public float density;

    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public float transleteSpeed = 2f;
    public OpenLooper openLooper = null;
    public LoopCallback loopCallback = null;

    @SuppressWarnings("deprecation")
    public MyListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.screenWidth = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getWidth();
        this.mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        this.density = context.getResources().getDisplayMetrics().density;
        this.openLooper = new OpenLooper();
        this.openLooper.createOpenLooper();
        this.loopCallback = new ListLoopCallback(this.openLooper);
        this.openLooper.loopCallback = this.loopCallback;
    }

    public class BodyStatus {
        public int None = 0, Down = 1, Move = 2, Up = 3, Homing = 4;
        int state = None;
    }

    public BodyStatus bodyStatus = new BodyStatus();

    public class RemoveDirection {
        public int None = 0, Left = 1, Right = 2, Homing_Left = 3, Homing_Right = 4;
        public int state = None;
    }

    public RemoveDirection removeDirection = new RemoveDirection();

    public class ListLoopCallback extends LoopCallback {
        public ListLoopCallback(OpenLooper openLooper) {
            openLooper.super();
        }

        @Override
        public void loop(double ellapsedMillis) {
            if (bodyStatus.state == bodyStatus.Homing) {
                goHoming((float) ellapsedMillis);
            }
        }
    }

    public void goHoming(float delta) {
        float distance = (float) delta * transleteSpeed;
        if (removeDirection.state == removeDirection.Left) {
            float currentX = itemView.getScrollX() + distance;
            if (currentX > screenWidth) {
                distance = distance - (currentX - screenWidth);
            }
            itemView.scrollBy((int) (distance), itemView.getScrollY());
            if (itemView.getScrollX() > screenWidth / 2 + 40 * screenWidth / 1080) {
                t2.setTranslationX(itemView.getScrollX() - screenWidth / 2 - 40 * 3f);
            } else {
                t2.setTranslationX(40 * 3f);
            }
        } else if (removeDirection.state == removeDirection.Right) {
            float currentX = itemView.getScrollX() - distance;
            if (currentX < -screenWidth) {
                distance = distance - (Math.abs(currentX) - screenWidth);
            }
            itemView.scrollBy((int) (-distance), itemView.getScrollY());
            if (itemView.getScrollX() < -(screenWidth / 2 + 40 * 3f * 2)) {
                t1.setTranslationX(-(Math.abs(itemView.getScrollX()) - screenWidth / 2 - 40 * 3f));
            } else {
                t1.setTranslationX(-40 * 3f);
            }
        } else if (removeDirection.state == removeDirection.Homing_Left) {
            float currentX = itemView.getScrollX() - distance;
            if (currentX < 0) {
                distance = distance - (Math.abs(currentX));
            }
            itemView.scrollBy((int) (-distance), itemView.getScrollY());
        } else if (removeDirection.state == removeDirection.Homing_Right) {
            float currentX = itemView.getScrollX() + distance;
            if (currentX > 0) {
                distance = distance - currentX;
            }
            itemView.scrollBy((int) (distance), itemView.getScrollY());
        }
        if (itemView.getScrollX() == 0 || itemView.getScrollX() >= screenWidth || itemView.getScrollX() <= -screenWidth) {
            openLooper.stop();
            if (itemView.getScrollX() >= screenWidth) {
                mRemoveListener.removeItem(removeDirection, position);
            } else if (itemView.getScrollX() <= -screenWidth) {
                mRemoveListener.removeItem(removeDirection, position);
            }
            new Handler().postDelayed(new Runnable() {

                @Override
                public void run() {
                    itemView.scrollTo(0, itemView.getScrollY());
                    bodyStatus.state = bodyStatus.None;
                }
            }, 300);
        }
    }

    public int touch_down_x;
    public int touch_down_y;

    public View itemView;
    public TextView t1;
    public TextView t2;

    public int SNAP_VELOCITY = 800;
    public int position;

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        int action = event.getAction();

        if (action == MotionEvent.ACTION_DOWN) {
            // addVelocityTracker(event);
            if (bodyStatus.state != bodyStatus.None) {
                return super.dispatchTouchEvent(event);
            }
            this.touch_down_x = (int) event.getX();
            this.touch_down_y = (int) event.getY();

            position = pointToPosition(touch_down_x, touch_down_y);
            if (position == AdapterView.INVALID_POSITION) {
                return super.dispatchTouchEvent(event);
            }
            itemView = getChildAt(position - getFirstVisiblePosition());
            t2 = (TextView) itemView.findViewById(R.id.t2);
            t1 = (TextView) itemView.findViewById(R.id.t1);
        } else if (action == MotionEvent.ACTION_MOVE) {
            if (Math.abs(getScrollVelocity()) > SNAP_VELOCITY || (Math.abs(event.getX() - touch_down_x) > mTouchSlop && Math.abs(event.getY() - touch_down_y) < mTouchSlop)) {
                isSlide = true;
            }
        } else if (action == MotionEvent.ACTION_UP) {
            int velocityX = getScrollVelocity();
            if (Math.abs(velocityX) > SNAP_VELOCITY) {
                if (velocityX > SNAP_VELOCITY) {
                    bodyStatus.state = bodyStatus.Homing;
                    removeDirection.state = removeDirection.Right;
                    openLooper.start();
                } else {
                    bodyStatus.state = bodyStatus.Homing;
                    removeDirection.state = removeDirection.Left;
                    openLooper.start();
                }
            } else {
                if (itemView.getScrollX() >= screenWidth / 2) {
                    bodyStatus.state = bodyStatus.Homing;
                    removeDirection.state = removeDirection.Left;
                    openLooper.start();
                } else if (itemView.getScrollX() <= -screenWidth / 2) {
                    bodyStatus.state = bodyStatus.Homing;
                    removeDirection.state = removeDirection.Right;
                    openLooper.start();
                } else {
                    if (itemView.getScrollX() < 0) {
                        removeDirection.state = removeDirection.Homing_Right;
                    } else {
                        removeDirection.state = removeDirection.Homing_Left;
                    }
                    bodyStatus.state = bodyStatus.Homing;
                    openLooper.start();
                }
            }
            recycleVelocityTracker();
            isSlide = false;
        }
        return super.dispatchTouchEvent(event);
    }

    public boolean isSlide = false;

    @SuppressLint("Recycle")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (isSlide && position != AdapterView.INVALID_POSITION) {
            requestDisallowInterceptTouchEvent(true);
            addVelocityTracker(event);
            int x = (int) event.getX();
            int action = event.getAction();
            if (action == MotionEvent.ACTION_DOWN) {
            } else if (action == MotionEvent.ACTION_MOVE) {
                MotionEvent cancelEvent = MotionEvent.obtain(event);
                cancelEvent.setAction(MotionEvent.ACTION_CANCEL | (event.getActionIndex() << MotionEvent.ACTION_POINTER_INDEX_SHIFT));
                onTouchEvent(cancelEvent);

                int deltaX = touch_down_x - x;
                touch_down_x = x;
                if (itemView.getScrollX() > screenWidth / 2 + 40 * 3f * 2) {
                    t2.setTranslationX(itemView.getScrollX() - screenWidth / 2 - 40 * 3f);
                } else {
                    t2.setTranslationX(40 * 3f);
                }
                if (itemView.getScrollX() < -(screenWidth / 2 + 40 * 3f * 2)) {
                    t1.setTranslationX(-(Math.abs(itemView.getScrollX()) - screenWidth / 2 - 40 * 3f));
                } else {
                    t1.setTranslationX(-40 * 3f);
                }
                itemView.scrollBy(deltaX, 0);
                return true;
            }
        }
        return super.onTouchEvent(event);
    }

    public RemoveListener mRemoveListener;

    public void setRemoveListener(RemoveListener removeListener) {
        this.mRemoveListener = removeListener;
    }

    public interface RemoveListener {
        public void removeItem(RemoveDirection direction, int position);
    }

    public VelocityTracker velocityTracker;

    public void addVelocityTracker(MotionEvent event) {
        if (velocityTracker == null) {
            velocityTracker = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
    }

    public int getScrollVelocity() {
        if (velocityTracker != null) {
            velocityTracker.computeCurrentVelocity(1000);
            int velocity = (int) velocityTracker.getXVelocity();
            return velocity;
        }
        return 0;
    }

    public void recycleVelocityTracker() {
        if (velocityTracker != null) {
            velocityTracker.recycle();
            velocityTracker = null;
        }
    }
}

测试ListView的Activity代码:

package com.example.wz;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import com.example.wz.view.MyListView;
import com.example.wz.view.MyListView.RemoveDirection;
import com.example.wz.view.MyListView.RemoveListener;

public class TestListActivity extends Activity {

    LayoutInflater mInflater;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        displayMetrics = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_testlist);
        mInflater = getLayoutInflater();
        listView = (MyListView) findViewById(R.id.slideCutListView);
        showAddressDialog();
    }

    public DisplayMetrics displayMetrics;

    MyListView listView;
    public ArrayList<String> items;

    public void showAddressDialog() {
        items = new ArrayList<String>() {
            {
                add("item...1");
                add("item...2");
                add("item...3");
                add("item...4");
                add("item...5");
                add("item...6");
                add("item...7");
                add("item...8");
                add("item...9");
                add("item...10");
                add("item...11");
                add("item...12");
                add("item...13");
                add("item...14");
                add("item...15");
                add("item...16");
                add("item...17");
                add("item...18");
                add("item...19");
                add("item...20");
            }
        };

        NearbyRelationAdapter nearbyRelationAdapter = new NearbyRelationAdapter();
        listView.setAdapter(nearbyRelationAdapter);
        listView.setRemoveListener(new RemoveListener() {

            @Override
            public void removeItem(RemoveDirection direction, int position) {
                if (direction.state == direction.Left) {
                    Log.e("A", "left" + "-" + position);
                } else if (direction.state == direction.Right) {
                    Log.e("A", "right" + "-" + position);
                }
            }
        });
    }

    public class NearbyRelationAdapter extends BaseAdapter {

        @Override
        public int getCount() {
            return items.size();
        }

        @Override
        public Object getItem(int posotion) {
            return items.get(posotion);
        }

        @Override
        public long getItemId(int posotion) {
            return posotion;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            Holder holder = null;
            if (convertView == null) {
                holder = new Holder();
                convertView = mInflater.inflate(R.layout.view_menu_item1, null);
                holder.title = (TextView) convertView.findViewById(R.id.title);
                holder.o1 = convertView.findViewById(R.id.o1);
                holder.o1.setTranslationX(-displayMetrics.widthPixels);
                holder.o2 = convertView.findViewById(R.id.o2);
                holder.o2.setTranslationX(displayMetrics.widthPixels);
                convertView.setTag(holder);
            } else {
                holder = (Holder) convertView.getTag();
            }
            holder.title.setText(items.get(position));
            convertView.scrollTo(0, convertView.getScrollY());
            return convertView;
        }

        class Holder {
            TextView title;
            View o1, o2;
        }
    }
}

ListView布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/darker_gray" >

    <com.example.wz.view.MyListView
        android:id="@+id/slideCutListView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:cacheColorHint="@android:color/transparent"
        android:listSelector="@android:color/transparent" >
    </com.example.wz.view.MyListView>

    <TextView
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:layout_centerHorizontal="true"
        android:background="#0099cd" />

</RelativeLayout>

ListView 的Item布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="#fff" >

    <RelativeLayout
        android:id="@+id/o1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#ff0000" >

        <TextView
            android:id="@+id/t1"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:gravity="center"
            android:padding="10dp"
            android:text="删除"
            android:textColor="#99000000"
            android:textSize="18sp" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/o2"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#660099cd" >

        <TextView
            android:id="@+id/t2"
            android:layout_width="80dp"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:gravity="center"
            android:padding="10dp"
            android:text="编辑"
            android:textColor="#99000000"
            android:textSize="18sp" />
    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/r1"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="#fff" >

        <TextView
            android:id="@+id/title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:gravity="center_vertical"
            android:textColor="#99000000"
            android:textSize="18sp" />
    </RelativeLayout>

</RelativeLayout>

代码中用到的其他的类,在上一篇文章中有:
http://blog.csdn.net/qxs965266509/article/details/45200289

如有转载请著名来自http://blog.csdn.net/qxs965266509

3楼u010785685昨天 21:48
学习了,感谢楼主的分享
2楼qq_27836885昨天 19:20
支持
1楼qxs965266509昨天 17:50
谢谢。