QQ聊天界面侧滑删除

来源:互联网 发布:java多线程调用方法 编辑:程序博客网 时间:2024/06/11 07:35

侧滑删除

效果图:
这里写图片描述

实现步骤:

1. 创建SwipeLayout

public class SwipeLayout extends FrameLayout {    public SwipeLayout(Context context) {        super(context);    }    public SwipeLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }}

2. 创建SwipeLayout的布局

<com.itcast.jq.swipelayout.view.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="55dp" >    <LinearLayout        android:layout_width="wrap_content"        android:layout_height="match_parent"        android:layout_gravity="right"        android:orientation="horizontal" >        <TextView            android:layout_width="55dp"            android:layout_height="match_parent"            android:background="#44ff0000"            android:gravity="center"            android:text="删除" />        <TextView            android:layout_width="55dp"            android:layout_height="match_parent"            android:background="#4400ff00"            android:gravity="center"            android:text="取消" />    </LinearLayout>    <LinearLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:background="#33ff0000" >        <TextView            android:layout_width="match_parent"            android:layout_height="match_parent"            android:gravity="center_vertical"            android:paddingLeft="10dp"            android:text="帅的人" />    </LinearLayout></com.itcast.jq.swipelayout.view.SwipeLayout>

3. 重写回调方法获取控件尺寸及初始化位置

@Overrideprotected void onFinishInflate() {    super.onFinishInflate();    // System.out.println("---------onFinishInflate");    if (getChildCount() < 2) {        throw new IllegalStateException("SwipeLayout must has at least 2 children");    }    mBackView = getChildAt(0);    mFrontView = getChildAt(1);}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    super.onMeasure(widthMeasureSpec, heightMeasureSpec);    // System.out.println("---------onMeasure");    // 注意不能在onFinishInflate方法中获取宽高    mWidth = getMeasuredWidth();    mHeight = getMeasuredHeight();    mBackWidth = mBackView.getMeasuredWidth();}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {    super.onSizeChanged(w, h, oldw, oldh);    // System.out.println("---------onMeasure");}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {    super.onLayout(changed, left, top, right, bottom);    // System.out.println("---------onMeasure");    // 设置两个子控件的初始化位置    mFrontView.layout(0, 0, mWidth, mHeight);    mBackView.layout(mWidth, 0, mWidth + mBackWidth, mHeight);}

4. 使用ViewDragHelper实现拖动效果

    public SwipeLayout(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    private void init() {        // 1. 创建ViewDragHelper对象        mDragHelper = ViewDragHelper.create(this, mCallback);    }    // 4. 处理回调方法    Callback mCallback = new Callback() {        // 根据返回决定是否可以拖动        public boolean tryCaptureView(View child, int pointerId) {            return true;        };        // 根据返回值决定被拖动的视图将要显示的位置        public int clampViewPositionHorizontal(View child, int left, int dx) {            return left;        };      };    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        // 2. 让ViewDragHelper决定是否拦截事件        return mDragHelper.shouldInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // 3. 传触摸事件传给ViewDragHelper处理        mDragHelper.processTouchEvent(event);        // 按下时需要返回true以便能持续地接收到触摸事件        if (event.getAction() == MotionEvent.ACTION_DOWN) {            return true;        }               return super.onTouchEvent(event);    }

5. 限制滑动范围

在clampViewPositionHorizontal方法中进行两个View的滑动范围限制

// 根据返回值决定被拖动的视图将要显示的位置public int clampViewPositionHorizontal(View child, int left, int dx) {      // 限制frontView拖动的范围, [-mBackWidth, 0]    if (child == mFrontView) {        if (left < -mBackWidth) {            left = -mBackWidth;        } else if (left > 0) {            left = 0;        }    }     if (child == mBackView) {         // 限制backView拖动的范围: [mWidth-mBackWidth, mWidth]        if (left < mWidth-mBackWidth) {            left = mWidth-mBackWidth;        } else if (left > mWidth) {            left = mWidth;        }    }       return left;}

6. 关联滑动效果

在onViewPositionChanged方法通过offsetLeftAndRight方法关联滑动

// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView,                                      int left, int top, int dx, int dy) {    // 关联两个子控件的滑动    if (changedView == mFrontView) { // 滑动mFrontView时,同时滑动mBackView        mBackView.offsetLeftAndRight(dx);    } else if (changedView == mBackView) {// 滑动mBackView时,同时滑动mFrontView        mFrontView.offsetLeftAndRight(dx);    }    // 解决往左滑时mBackView空白的问题,同时兼容低版本2.3    invalidate();};

7. 打开或关闭SwipeLayout

// 松开手释放子控件时回调// xvel: 往右甩速度大于0public void onViewReleased(View releasedChild, float xvel, float yvel) {        int left = mFrontView.getLeft();    if (xvel == 0 && left < -mBackWidth /2) {        open();    } else if (xvel < 0) { // 往左甩        open();    } else {        close();    }};/** 关闭SwipeLayout*/public void close() {    // 平滑滚动到某一位置: 第一步    mDragHelper.smoothSlideViewTo(mFrontView, 0, 0);    ViewCompat.postInvalidateOnAnimation(this);}/** 打开SwipeLayout */public void open() {    mDragHelper.smoothSlideViewTo(mFrontView, -mBackWidth, 0);    ViewCompat.postInvalidateOnAnimation(this);}// 平滑滚动到某一位置: 第二步@Overridepublic void computeScroll() {    super.computeScroll();    if (mDragHelper.continueSettling(true)) {        ViewCompat.postInvalidateOnAnimation(this);    }}

8. 监听滑动状态(打开、关闭、准备打开、准备关闭)

// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView,                                      int left, int top, int dx, int dy) {    ...     // 监听滑动状态(打开,关闭,准备打开,准备关闭)    listenDragStatus(); };// ===========监听拖动状态(begin)============public enum DragStatus {    OPEN, CLOSE, DRAGGING}private DragStatus mCurrentStatus = DragStatus.CLOSE;public DragStatus getDragStatus() {    return mCurrentStatus;}public interface OnDragListener {    public void open(SwipeLayout swipeLayout);    public void close(SwipeLayout swipeLayout);    /** 准备打开 */    public void onStartOpen(SwipeLayout swipeLayout);    /** 准备关闭 */    public void onStartClose(SwipeLayout swipeLayout);}private OnDragListener mOnDragListener;public void setOnDragListener(OnDragListener onDragListener) {    this.mOnDragListener = onDragListener;}/** 监听拖动状态  */protected void listenDragStatus() {    int left = mFrontView.getLeft();        DragStatus status;    if (left == 0) {        status = DragStatus.CLOSE;    } else if (left == -mBackWidth) {        status = DragStatus.OPEN;    } else {        status = DragStatus.DRAGGING;    }    if (mOnDragListener != null) {        if (status == DragStatus.OPEN) {            mOnDragListener.open(this);        } else if (status == DragStatus.CLOSE) {            mOnDragListener.close(this);        } else { // 当前状态为拖动状态                       // 上一次状态为关闭,当前状态为拖动,则表示准备打开            if (mCurrentStatus == DragStatus.CLOSE) {                mOnDragListener.onStartOpen(this);            } else if (mCurrentStatus == DragStatus.OPEN) {                // 上一次状态为打开,当前状态为拖动,则表示准备关闭                mOnDragListener.onStartClose(this);            }        }    }    mCurrentStatus = status;}// ===========监听拖动状态(end)============

9. 集成到ListView(显示列表)

protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_main);    mListView = (ListView) findViewById(R.id.list_view);    mAdapter = new ContactAdapter(this,             Arrays.asList(Constant.LIST_DATAS2));    mListView.setAdapter(mAdapter);}public class ContactAdapter extends MyBaseAdapter<String> {    public ContactAdapter(Context context, List<String> listDatas) {        super(context, listDatas);    }    @Override    public BaseHolder<String> createViewHolder(Context context,             ViewGroup parent, String bean, int position) {        return new ContactHolder(context, parent, this, position, bean);    }}public class ContactHolder extends BaseHolder<String> {    private TextView tvName;    public ContactHolder(Context context, ViewGroup parent,             MyBaseAdapter<String> adapter, int position, String bean) {        super(context, parent, adapter, position, bean);    }    @Override    public View createView(Context context, ViewGroup parent, int position, String bean) {        return super.inflater.inflate(R.layout.item_list, parent, false);    }    @Override    protected void initView() {        tvName = (TextView) super.itemView.findViewById(R.id.tv_name);    }    @Override    protected void onRefreshViews(String bean, int position) {        tvName.setText(bean);    }}

10. 只打开一个SwipeLayout

public class SwipeLayoutManager {    // 单例    private static SwipeLayoutManager instance = new SwipeLayoutManager();    private SwipeLayoutManager() {    }    public static SwipeLayoutManager getInstance() {        return instance;    }    /** 用来记录打开的SwipeLayout */    private SwipeLayout mOpenSwipeLayout;    public SwipeLayout getOpenSwipeLayout() {        return mOpenSwipeLayout;    }    /** 记录列表中打开的SwipeLayout */    public void setOpenSwipeLayout(SwipeLayout swipeLayout) {        this.mOpenSwipeLayout = swipeLayout;    }    /** 关闭之前打开的SwipeLayout */    public void closeSwipeLayout() {        if (mOpenSwipeLayout != null) {            mOpenSwipeLayout.close();            mOpenSwipeLayout = null; // 关闭后需要置为空        }    }}// 列表的Holder类的查找item子控件的方法protected void initView() {    ...     SwipeLayout layout =  (SwipeLayout) super.itemView;    layout.setOnDragListener(new OnDragListener() {             @Override        public void onOpen(SwipeLayout swipeLayout) {        }        // 准备打开某一个列表项的SwipeLayout时,先关闭之前打开的,再保存当前打开的        @Override        public void onStartOpen(SwipeLayout swipeLayout) {            if (SwipeLayoutManager.getInstance().getOpenSwipeLayout()                 != swipeLayout) {                SwipeLayoutManager.getInstance().closeSwipeLayout();            }            SwipeLayoutManager.getInstance().setOpenSwipeLayout(swipeLayout);        }        @Override        public void onStartClose(SwipeLayout swipeLayout) {        }               @Override        public void onClose(SwipeLayout swipeLayout) {        }    });}

11. 优化体验:如果左右滑动请求父控件不要拦截事件

当左右滑动时,请求列表不要拦截事件// 当子控件位置发生改变后调用public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {    ...    // 如果是左右滑动SwipeLayout,请求父控件ListView不要拦截事件,则    // ListView不会上下滚动,并且会把触摸事件传递下来,让SwipeLayout处理    if (Math.abs(dx) > Math.abs(dy)) {        requestDisallowInterceptTouchEvent(true);    } }

12. 滚动列表时关闭打开的SwipeLayout

// 滚动列表时,关闭打开的SwipeLayoutmListView.setOnScrollListener(new OnScrollListener() {    @Override    public void onScrollStateChanged(AbsListView view, int scrollState) {        if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {            SwipeLayoutManager.getInstance().closeSwipeLayout();        }    }       @Override    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {    }});

13. 列表项中的操作菜单点击事件

// 在列表的Holder类中添加protected void initView() {    tvName = (TextView) super.itemView.findViewById(R.id.tv_name);    tv_delete = (TextView) super.itemView.findViewById(R.id.tv_delete);    tv_cancel = (TextView) super.itemView.findViewById(R.id.tv_cancel);    OnClickListener onClickListener = new OnClickListener() {               @Override        public void onClick(View v) {            SwipeLayoutManager.getInstance().closeSwipeLayout();            switch (v.getId()) {            case R.id.tv_delete:                showToast("取消:" + bean);                break;            case R.id.tv_cancel:                showToast("删除:" + bean);                break;            }        }    };      tv_delete.setOnClickListener(onClickListener);    tv_cancel.setOnClickListener(onClickListener);    ...}

完整源码:点击下载

0 0
原创粉丝点击