仿QQ侧滑删除

今天要总结的是listview的左滑显示删除按钮,点击删除。这个方法是我上个项目所做的时候应用的。主要的思路就是写两个xml文件,然后动态地添加到父布局中,当然需要在重写listview,判断是左右滑动还是上下滑动,在onTouch方法中判断是否消费本次传递事件,并在合并的View中写了Swipe方法,判断滑出的部分是否超过多余部分的二分之一进行滑入和滑出的操作,用于接收listview传递过来的事件,进行多余部分的显示与否。总体思路比较清晰,但是由于本人刚刚入门。。。还是写地略难过。。。

这里写图片描述

这里写图片描述

主要核心代码SwipeItemLayout.java

package com.example.testdemo;

import android.support.v4.widget.ScrollerCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.FrameLayout;

public class SwipeItemLayout extends FrameLayout {
    // listview的item
    private View contentView = null;
    // 这个是左滑后多出的部分
    private View menuView = null;
    // 这个是动画的速度控制器
    private Interpolator closeInterpolator = null;
    private Interpolator openInterpolator = null;
    // 控制控件滑动的,会平滑滑动,一个开一个关
    private ScrollerCompat mOpenScroller;
    private ScrollerCompat mCloseScroller;
    // 手指点击的初始位置
    private int mDownX;
    private int state = STATE_CLOSE;
    // 当前item的状态,open和close两种
    private static final int STATE_CLOSE = 0;
    private static final int STATE_OPEN = 1;

    // 构造函数
    public SwipeItemLayout(View contentView, View menuView,
            Interpolator closeInterpolator, Interpolator openInterpolator) {
        super(contentView.getContext());
        this.contentView = contentView;
        this.menuView = menuView;
        this.closeInterpolator = closeInterpolator;
        this.openInterpolator = openInterpolator;
        init();// 初始化视图
    }

    private void init() {
        setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT));
        // 构造动画加速器
        if (closeInterpolator != null) {
            mCloseScroller = ScrollerCompat.create(getContext(),
                    closeInterpolator);
        } else {
            mCloseScroller = ScrollerCompat.create(getContext());
        }
        if (openInterpolator != null) {
            mOpenScroller = ScrollerCompat.create(getContext(),
                    openInterpolator);
        } else {
            mOpenScroller = ScrollerCompat.create(getContext());
        }
        // 这里是设置各自的宽和高
        contentView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT));
        menuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));
        // 将这两个布局都add到这个view中
        addView(contentView);
        addView(menuView);
    }

    // 预留接口,在listview中的ontouch事件传递到这里来处理
    public boolean onSwipe(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 获取手指点击的初始位置
            mDownX = (int) event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            // 手指移动的距离
            int dis = (int) (mDownX - event.getX());
            if (state == STATE_OPEN) {
                dis += menuView.getWidth();// 手指移动距离+多出来部分的宽度
            }
            swipe(dis);
            break;
        // 这里其实是一个判断,当用户滑了menuView的一半的时候,自动滑出来,否则滑进去
        case MotionEvent.ACTION_UP:
            if ((mDownX - event.getX()) > (menuView.getWidth() / 2)) {
                // 平滑的滑出
                smoothOpenMenu();
            } else {
                // 平滑的滑进
                smoothCloseMenu();
                return true;// 把这次的touch事件给lisview继续处理
            }
            break;
        }
        return true;// 默认是消费这次操作
    }

    // 判断是否滑出的状态
    public boolean isOpen() {
        return state == STATE_OPEN;
    }

    // 执行滑动操作,dis参数是滑动的距离
    private void swipe(int dis) {
        if (dis > menuView.getWidth()) {
            dis = menuView.getWidth();
        } else if (dis < 0) {
            dis = 0;
        }
        // layout的四个参数分别是(l,t,r,b),类似于绝对布局。相对于绝对布局子view的位置
        contentView.layout(-dis, contentView.getTop(), contentView.getWidth()
                - dis, getMeasuredHeight());
        menuView.layout(contentView.getWidth() - dis, menuView.getTop(),
                contentView.getWidth() + menuView.getWidth() - dis,
                menuView.getBottom());
    }

    // computeScroll在父控件执行drawChild时,会调用这个方法。做刷新操作
    @Override
    public void computeScroll() {
        if (state == STATE_OPEN) {
            if (mOpenScroller.computeScrollOffset()) {
                swipe(mOpenScroller.getCurrX());
                postInvalidate();
            }
        } else {
            if (mCloseScroller.computeScrollOffset()) {
                swipe(- mCloseScroller.getCurrX());
                postInvalidate();
            }
        }
    }

    // 关闭隐藏图标
    public void smoothCloseMenu() {
        state = STATE_CLOSE;
        //contentView的左边相对于父控件的距离contentView.getLeft()
        mCloseScroller.startScroll(0, 0, -contentView.getLeft(), 0, 350);
        postInvalidate();
    }

    // 打开隐藏的图标
    public void smoothOpenMenu() {
        //这里的-contentView.getLeft()与menuView.getWidth()相等
        state = STATE_OPEN;
        mOpenScroller.startScroll(-contentView.getLeft(), 0,
                menuView.getWidth(), 0, 350);
        Log.e("1111111111", contentView.getLeft()+"uuuuuu"+menuView.getWidth());
        postInvalidate();
    }

    public void closeMenu() {
        if (mCloseScroller.computeScrollOffset()) {
            mCloseScroller.abortAnimation();
        }
        if (state == STATE_OPEN) {
            state = STATE_CLOSE;
            swipe(0);
        }
    }

    public void openMenu() {
        if (state == STATE_CLOSE) {
            state = STATE_OPEN;
            swipe(menuView.getWidth());
        }
    }

    public View getContentView() {
        return contentView;
    }

    public View getMenuView() {
        return menuView;
    }

    // 这个方法 其实就是获取menuView的宽和高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        menuView.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) {
        contentView.layout(0, 0, getMeasuredWidth(),
                contentView.getMeasuredHeight());
        menuView.layout(getMeasuredWidth(), 0,
                getMeasuredWidth() + menuView.getMeasuredWidth(),
                contentView.getMeasuredHeight());
    }
}


自定义的SwipeListView

package com.example.testdemo;

import android.support.v4.widget.ScrollerCompat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.FrameLayout;

public class SwipeItemLayout extends FrameLayout {
    // listview的item
    private View contentView = null;
    // 这个是左滑后多出的部分
    private View menuView = null;
    // 这个是动画的速度控制器
    private Interpolator closeInterpolator = null;
    private Interpolator openInterpolator = null;
    // 控制控件滑动的,会平滑滑动,一个开一个关
    private ScrollerCompat mOpenScroller;
    private ScrollerCompat mCloseScroller;
    // 手指点击的初始位置
    private int mDownX;
    private int state = STATE_CLOSE;
    // 当前item的状态,open和close两种
    private static final int STATE_CLOSE = 0;
    private static final int STATE_OPEN = 1;

    // 构造函数
    public SwipeItemLayout(View contentView, View menuView,
            Interpolator closeInterpolator, Interpolator openInterpolator) {
        super(contentView.getContext());
        this.contentView = contentView;
        this.menuView = menuView;
        this.closeInterpolator = closeInterpolator;
        this.openInterpolator = openInterpolator;
        init();// 初始化视图
    }

    private void init() {
        setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT));
        // 构造动画加速器
        if (closeInterpolator != null) {
            mCloseScroller = ScrollerCompat.create(getContext(),
                    closeInterpolator);
        } else {
            mCloseScroller = ScrollerCompat.create(getContext());
        }
        if (openInterpolator != null) {
            mOpenScroller = ScrollerCompat.create(getContext(),
                    openInterpolator);
        } else {
            mOpenScroller = ScrollerCompat.create(getContext());
        }
        // 这里是设置各自的宽和高
        contentView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT));
        menuView.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
                LayoutParams.WRAP_CONTENT));
        // 将这两个布局都add到这个view中
        addView(contentView);
        addView(menuView);
    }

    // 预留接口,在listview中的ontouch事件传递到这里来处理
    public boolean onSwipe(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            // 获取手指点击的初始位置
            mDownX = (int) event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            // 手指移动的距离
            int dis = (int) (mDownX - event.getX());
            if (state == STATE_OPEN) {
                dis += menuView.getWidth();// 手指移动距离+多出来部分的宽度
            }
            swipe(dis);
            break;
        // 这里其实是一个判断,当用户滑了menuView的一半的时候,自动滑出来,否则滑进去
        case MotionEvent.ACTION_UP:
            if ((mDownX - event.getX()) > (menuView.getWidth() / 2)) {
                // 平滑的滑出
                smoothOpenMenu();
            } else {
                // 平滑的滑进
                smoothCloseMenu();
                return true;// 把这次的touch事件给lisview继续处理
            }
            break;
        }
        return true;// 默认是消费这次操作
    }

    // 判断是否滑出的状态
    public boolean isOpen() {
        return state == STATE_OPEN;
    }

    // 执行滑动操作,dis参数是滑动的距离
    private void swipe(int dis) {
        if (dis > menuView.getWidth()) {
            dis = menuView.getWidth();
        } else if (dis < 0) {
            dis = 0;
        }
        // layout的四个参数分别是(l,t,r,b),类似于绝对布局。相对于绝对布局子view的位置
        contentView.layout(-dis, contentView.getTop(), contentView.getWidth()
                - dis, getMeasuredHeight());
        menuView.layout(contentView.getWidth() - dis, menuView.getTop(),
                contentView.getWidth() + menuView.getWidth() - dis,
                menuView.getBottom());
    }

    // computeScroll在父控件执行drawChild时,会调用这个方法。做刷新操作
    @Override
    public void computeScroll() {
        if (state == STATE_OPEN) {
            if (mOpenScroller.computeScrollOffset()) {
                swipe(mOpenScroller.getCurrX());
                postInvalidate();
            }
        } else {
            if (mCloseScroller.computeScrollOffset()) {
                swipe(- mCloseScroller.getCurrX());
                postInvalidate();
            }
        }
    }

    // 关闭隐藏图标
    public void smoothCloseMenu() {
        state = STATE_CLOSE;
        //contentView的左边相对于父控件的距离contentView.getLeft()
        mCloseScroller.startScroll(0, 0, -contentView.getLeft(), 0, 350);
        postInvalidate();
    }

    // 打开隐藏的图标
    public void smoothOpenMenu() {
        //这里的-contentView.getLeft()与menuView.getWidth()相等
        state = STATE_OPEN;
        mOpenScroller.startScroll(-contentView.getLeft(), 0,
                menuView.getWidth(), 0, 350);
        Log.e("1111111111", contentView.getLeft()+"uuuuuu"+menuView.getWidth());
        postInvalidate();
    }

    public void closeMenu() {
        if (mCloseScroller.computeScrollOffset()) {
            mCloseScroller.abortAnimation();
        }
        if (state == STATE_OPEN) {
            state = STATE_CLOSE;
            swipe(0);
        }
    }

    public void openMenu() {
        if (state == STATE_CLOSE) {
            state = STATE_OPEN;
            swipe(menuView.getWidth());
        }
    }

    public View getContentView() {
        return contentView;
    }

    public View getMenuView() {
        return menuView;
    }

    // 这个方法 其实就是获取menuView的宽和高
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        menuView.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) {
        contentView.layout(0, 0, getMeasuredWidth(),
                contentView.getMeasuredHeight());
        menuView.layout(getMeasuredWidth(), 0,
                getMeasuredWidth() + menuView.getMeasuredWidth(),
                contentView.getMeasuredHeight());
    }
}
SwipeAdapterMessage中的代码

package com.example.testdemo;

import java.util.List;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.animation.Interpolator;
import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;

public class SwipeAdapterMessage extends BaseAdapter {

    private Context mContext = null;
    private List<UserMessage> mUserMessage = null;
    private int mResource;
    private int mMerge;

    public SwipeAdapterMessage(Context context, List<UserMessage> data,
            int layoutId, int mergeId) {
        this.mContext = context;
        this.mUserMessage = data;
        this.mResource = layoutId;
        this.mMerge = mergeId;
    }

    public void setData(List<UserMessage> mUserMessageItems) {
        mUserMessage = mUserMessageItems;
    }

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

    @Override
    public UserMessage getItem(int position) {
        return mUserMessage.get(position);
    }

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        ViewHolder viewholder = null;
        if (convertView == null) {
            viewholder = new ViewHolder();
            // 主视图item
            View viewMain = LayoutInflater.from(mContext).inflate(mResource,
                    null);
            // 滑动出来的删除视图
            View viewDel = LayoutInflater.from(mContext).inflate(mMerge, null);
            viewholder.btnDel = (TextView) viewDel.findViewById(R.id.delete);
            viewholder.childId = (TextView) viewMain
                    .findViewById(R.id.messageid);
            viewholder.txtContent = (TextView) viewMain
                    .findViewById(R.id.message_content);
            viewholder.txtDate = (TextView) viewMain
                    .findViewById(R.id.message_date);
            convertView = new SwipeItemLayout(viewMain, viewDel,null , null);
            convertView.setTag(viewholder);
        } else {
            viewholder = (ViewHolder) convertView.getTag();
        }
        UserMessage Item = getItem(position);
        viewholder.childId.setText(String.valueOf(Item.getId()));
        viewholder.txtContent.setText(Item.getMessageContent());
        viewholder.txtDate.setText(Item.getMyDate());
        viewholder.btnDel.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(mContext, "删除成功!", Toast.LENGTH_SHORT).show();
                mUserMessage.remove(position); // 删除视图上对应的位置item
                notifyDataSetChanged(); // 通知数据改变了,会重新刷新适配器中的数据
            }
        });
        return convertView;
    }

    // view容器
    public class ViewHolder {
        TextView btnDel = null;
        TextView txtContent = null;
        TextView txtDate = null;
        TextView childId = null;
    }

}

最后附上本项目的下载地址:http://download.csdn.net/detail/z_zt_t/9556266

总结:我觉得首先要完成这个功能需要对事件的分发机制有着长足的了解,并不断地去理解实验然后跳坑出坑。。。我觉得写博客不能因为已经有大牛写了精美绝伦的博客就直接转载,哪怕写的支零破碎也要留下自己的一知半解,不为大家能看到,哪怕只作为自己的一个笔记片段,也是足够的了。这也是写博客的尴尬,也是转载的不深刻不理解留下的诟病。。。不将就

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值