模仿QQ左滑删除当前会话列表

来源:互联网 发布:顶点网络代理 编辑:程序博客网 时间:2024/05/16 02:41

最近无聊,看了很多关于QQ列表侧滑删除的文章和技术文档,结合许多整理一下内容。
侧滑结构如图:这里写图片描述
所需填充的数据由“文本”和“删除”2部分组成。代码如下
content.xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="60dp"    android:background="#97d8da"    android:gravity="center"    android:orientation="vertical">    <TextView        android:id="@+id/Content"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="Text"        android:gravity="center"        android:textSize="24dp"        /></LinearLayout>

delete.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:id="@+id/Delete"    android:layout_width="130dp"    android:layout_height="60dp"    android:background="#ff0000"    android:gravity="center"    android:orientation="vertical">    <TextView        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="删除"        android:textSize="24dp"        android:textColor="#ffffff"        /></LinearLayout>

ok,既然这2个做好了,那么就到最关键的一步了。就是当我们滑动“文本”和“删除”这一个整体的时候,我们所需要注意的需要删除控件的长度,直接贴代码:
DeleteListView:

import android.content.Context;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.ViewGroup;public class DeleteListView extends ViewGroup{    // 内容部分    private View mContent;     // 删除部分    private View mDelete;      private ViewDragHelper viewDragHelper;    private int mContentWidth;    private int mContentHeight;    private int mDeleteWidth;    private int mDeleteHeight;    public SlideDelete(Context context) {        super(context);    }    public SlideDelete(Context context, AttributeSet attrs) {        super(context, attrs);    }    public SlideDelete(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    private OnSlideDeleteListener onSlideDeleteListener;    public void setOnSlideDeleteListener(OnSlideDeleteListener onSlideDeleteListener){        this.onSlideDeleteListener = onSlideDeleteListener;    }    @Override    protected void onFinishInflate() {        super.onFinishInflate();        mContent = getChildAt(0);        mDelete = getChildAt(1);        viewDragHelper = ViewDragHelper.create(this,new MyDrawHelper());    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // 这跟mContent的父亲的大小有关,父亲是宽填充父窗体,高度是和孩子一样是60dp        // 测量内容部分的大小        mContent.measure(widthMeasureSpec,heightMeasureSpec);         LayoutParams layoutParams = mDelete.getLayoutParams();        int deleteWidth = MeasureSpec.makeMeasureSpec(                layoutParams.width, MeasureSpec.EXACTLY);        int deleteHeight = MeasureSpec.makeMeasureSpec(                layoutParams.height, MeasureSpec.EXACTLY);        // 这个参数就需要指定为精确大小        // 测量删除部分的大小        mDelete.measure(deleteWidth, deleteHeight);         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        mContentWidth = mContent.getMeasuredWidth();        mContentHeight = mContent.getMeasuredHeight();        // 摆放内容部分的位置        mContent.layout(0, 0, mContentWidth, mContentHeight);         mDeleteWidth = mDelete.getMeasuredWidth();        mDeleteHeight = mDelete.getMeasuredHeight();        // 摆放删除部分的位置        mDelete.layout(mContentWidth, 0,mContentWidth +                 mDeleteWidth, mContentHeight);     }    class MyDrawHelper extends ViewDragHelper.Callback {        /**         * Touch的down事件会回调这个方法 tryCaptureView         * @Child:指定要动的孩子  (哪个孩子需要动起来)         * @pointerId: 点的标记         * @return : ViewDragHelper是否继续分析处理 child的相关touch事件         */        @Override        public boolean tryCaptureView(View child, int pointerId) {            System.out.println("调用tryCaptureView");            System.out.println("contentView : " + (mContent == child));            return mContent == child || mDelete == child;        }        /**         * 捕获了水平方向移动的位移数据         * @param child 移动的孩子View         * @param left 父容器的左上角到孩子View的距离         * @param dx 增量值,其实就是移动的孩子View的左上角距离控件(父亲)的距离,包含正负         */        @Override        public int clampViewPositionHorizontal(View child, int left, int dx) {            if(child == mContent){                 // 解决内容部分左右拖动的越界问题                if(left>0){                    return 0;                }else if(-left>mDeleteWidth){                    return -mDeleteWidth;                }            }            if(child == mDelete){                 // 解决删除部分左右拖动的越界问题                if(left<mContentWidth - mDeleteWidth){                    return mContentWidth - mDeleteWidth;                }else if(left > mContentWidth){                    return mContentWidth;                }            }            return left;        }        @Override        public int clampViewPositionVertical(View child, int top, int dy) {            return super.clampViewPositionVertical(child, top, dy);        }        /**         * 当View的位置改变时的回调  这个方法的价值是结合clampViewPositionHorizontal         * 或者clampViewPositionVertical         * @param changedView  哪个View的位置改变了         * @param left  changedView的left         * @param top  changedView的top         * @param dx x方向的上的增量值         * @param dy y方向上的增量值         */        @Override        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {            invalidate();            if(changedView == mContent){                // 如果移动的是mContent                //我们移动mContent的实惠要相应的联动改变mDelete的位置                // 怎么改变mDelete的位置,当然是mDelete的layput方法啦                int tempDeleteLeft = mContentWidth+left;                int tempDeleteRight = mContentWidth+left + mDeleteWidth;                mDelete.layout(tempDeleteLeft,0,tempDeleteRight,mDeleteHeight);            }else{ // touch的是mDelete                int tempContentLeft = left - mContentWidth;                int tempContentRight = left;                mContent.layout(tempContentLeft,0,tempContentRight,mContentHeight);            }        }        /**         * 相当于Touch的up的事件会回调onViewReleased这个方法         * @param releasedChild         * @param xvel  x方向的速率         * @param yvel  y方向的速率         */        @Override        public void onViewReleased(View releasedChild, float xvel, float yvel) {            // 方法的参数里面没有left,那么我们就采用 getLeft()这个方法            int mConLeft = mContent.getLeft();            // 这里没必要分来两个孩子判断            if(-mConLeft>mDeleteWidth/2){                  // mDelete展示起来                isShowDelete(true);                if(onSlideDeleteListener != null){                    // 调用接口打开的方法                    onSlideDeleteListener.onOpen(SlideDelete.this);                 }            }else{                   // mDetele隐藏起来                isShowDelete(false);                if(onSlideDeleteListener != null){                    // 调用接口的关闭的方法                    onSlideDeleteListener.onClose(SlideDelete.this);                 }            }            super.onViewReleased(releasedChild, xvel, yvel);        }    }    /**     * 是否展示delete部分     * @param isShowDelete     */    public void isShowDelete(boolean isShowDelete){        if(isShowDelete){            //采用ViewDragHelper的 smoothSlideViewTo 方法让移动变得顺滑自然,不会太生硬            //smoothSlideViewTo只是模拟了数据,但是不会真正的动起来,动起来需要调用 invalidate            // 而 invalidate 通过调用draw()等方法之后最后还是还是会调用 computeScroll 这个方法            // 所以,使用 smoothSlideViewTo 做过渡动画需要结合  invalidate方法 和 computeScroll方法            // smoothSlideViewTo的动画执行时间没有暴露的参数可以设置,但是这个时间是google给我们经过大量计算给出合理时间            viewDragHelper.smoothSlideViewTo(mContent,-mDeleteWidth,0);            viewDragHelper.smoothSlideViewTo(mDelete,mContentWidth-mDeleteWidth,0);        }else{            //mContent.layout(0,0,mContentWidth,mContentHeight);            //mDelete.layout(mContentWidth, 0, mContentWidth + mDeleteWidth, mDeleteHeight);            viewDragHelper.smoothSlideViewTo(mContent, 0, 0);            viewDragHelper.smoothSlideViewTo(mDelete, mContentWidth, 0);        }        invalidate();    }    @Override    public void computeScroll() {        //super.computeScroll();        // 把捕获的View适当的时间移动,其实也可以理解为 smoothSlideViewTo 的模拟过程还没完成        if(viewDragHelper.continueSettling(true)){            invalidate();        }        // 其实这个动画过渡的过程大概在怎么走呢?        // 1、smoothSlideViewTo方法进行模拟数据,模拟后就就调用invalidate();        // 2、invalidate()最终调用computeScroll,computeScroll做一次细微动画,        // computeScroll判断模拟数据是否彻底完成,还没完成会再次调用invalidate        // 3、递归调用,知道数据noni完成。    }    @Override    public boolean onTouchEvent(MotionEvent event) {        // 使用ViewDragHelper必须复写onTouchEvent并调用这个方法        viewDragHelper.processTouchEvent(event);         //消费这个touch        return true;     }    // SlideDlete的接口    public interface OnSlideDeleteListener {        void onOpen(SlideDelete slideDelete);        void onClose(SlideDelete slideDelete);    }}

最主要的我们既然做完了,那么就可以开始剩下的工作的:实现我们需要的功能!
将之前我们写的2个layou文件include到一个item.xml文件中:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">    <com.example.listview_delete.DeleteListView        android:id="@+id/mSlideDelete"        android:layout_width="match_parent"        android:layout_height="60dp">        <!--文本部分-->        <include layout="@layout/slide_content"/>        <!--删除部分-->        <include layout="@layout/slide_delete"/>    </com.example.listview_delete.DeleteListView></LinearLayout>

然后我们在activity_main.xml中添加代码:

<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"    tools:context="com.example.listview_delete.MainActivity" >    <ListView        android:id="@+id/mLv"        android:layout_width="match_parent"        android:layout_height="match_parent"></ListView></RelativeLayout>

MainActivity.java:

import java.util.ArrayList;import java.util.List;import java.util.ListIterator;import android.app.Activity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.view.ViewGroup;import android.widget.AbsListView;import android.widget.BaseAdapter;import android.widget.LinearLayout;import android.widget.ListView;import android.widget.TextView;public class MainActivity extends Activity {    private ListView mLv;    private ArrayList<String> mData;    // 继续有多少个条目的delete被展示出来的集合    private List<DeleteListView> slideDeleteArrayList = new ArrayList<DeleteListView>();    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mLv = (ListView) findViewById(R.id.mLv);        mData=new ArrayList<String>();        for(int i=0;i<20;i++){            mData.add("Text"+i);        }        mLv.setAdapter(new MyAdapter());        mLv.setOnScrollListener(new AbsListView.OnScrollListener() {            @Override            public void onScrollStateChanged(AbsListView view, int scrollState) {                if(scrollState == SCROLL_STATE_FLING || scrollState == SCROLL_STATE_TOUCH_SCROLL){                    closeOtherItem();                }            }            @Override            public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {            }        });    }    class MyAdapter extends BaseAdapter{        @Override        public int getCount() {            if(mData!=null){                return mData.size();            }           return 0;        }        @Override        public Object getItem(int position) {            if(mData!=null){                return mData.get(position);            }            return null;        }        @Override        public long getItemId(int position) {            return position;        }        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            ViewHolder viewHolder;            if(convertView == null){                viewHolder = new ViewHolder();                convertView = View.inflate(MainActivity.this,R.layout.item,null);                viewHolder.mSlideDelete = (DeleteListView) convertView.findViewById(R.id.mSlideDelete);                viewHolder.mTvContent = (TextView) convertView.findViewById(R.id.Content);                viewHolder.mLlDelete = (LinearLayout) convertView.findViewById(R.id.Delete);                convertView.setTag(viewHolder);            }else{                viewHolder = (ViewHolder) convertView.getTag();            }            viewHolder.mTvContent.setText(mData.get(position));            viewHolder.mSlideDelete.setOnSlideDeleteListener(new DeleteListView.OnSlideDeleteListener() {                @Override                public void onOpen(DeleteListView slideDelete) {                    closeOtherItem();                    slideDeleteArrayList.add(slideDelete);                    Log.d("Slide", "slideDeleteArrayList当前数量:" + slideDeleteArrayList.size());                }                @Override                public void onClose(DeleteListView slideDelete) {                    slideDeleteArrayList.remove(slideDelete);                    Log.d("Slide", "slideDeleteArrayList当前数量:" + slideDeleteArrayList.size());                }            });            viewHolder.mLlDelete.setOnClickListener(new View.OnClickListener() {                @Override                public void onClick(View v) {                    mData.remove(position);                    notifyDataSetChanged();                }            });            return convertView;        }    }    class ViewHolder{        DeleteListView mSlideDelete;        TextView mTvContent;        LinearLayout mLlDelete;    }    private void closeOtherItem(){        // 采用Iterator的原因是for是线程不安全的,迭代器是线程安全的        ListIterator<DeleteListView> slideDeleteListIterator = slideDeleteArrayList.listIterator();        while(slideDeleteListIterator.hasNext()){            DeleteListView slideDelete = slideDeleteListIterator.next();            slideDelete.isShowDelete(false);        }        slideDeleteArrayList.clear();    }}

大致代码就像上面的一样了,此外发现一些bug,不能快速左划删除,另外,当我们左划到一定程度,可以看见删除的时候,这时你点击删除,会发现一个问题:就是你选中的被删除了,但是其下面一条则是被选择像又滑动的状态。
能力有限,只能到这里,希望大家多多指教。
由于刚刚上传源码,所以链接的话还要等,需要的可以私聊我(一般都有空。。。)

0 0
原创粉丝点击