推荐阅读:
先给大家分享一下,侧滑删除,布局也就是前面一个item,然后有两个隐藏的按钮(TextView也可以),然后我们可以向左侧滑动,然后显示出来,然后对delete(删除键)实现监听,就可以了哈。好了那就来看看代码怎么实现的吧。
首先和之前一样
自定义View,初始化ViewDragHelper:
package com.example.removesidepull;import android.content.Context;import android.support.v4.widget.ViewDragHelper;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.widget.FrameLayout;/*** Created by 若兰 on 2016/2/2.* 一个懂得了编程乐趣的小白,希望自己* 能够在这个道路上走的很远,也希望自己学习到的* 知识可以帮助更多的人,分享就是学习的一种乐趣* QQ:1069584784* csdn:http://blog.csdn.net/wuyinlei*/public class SwipeLayout extends FrameLayout {private ViewDragHelper mDragHelper;public SwipeLayout(Context context) {this(context, null);}public SwipeLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SwipeLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);//第一步 初始化ViewDragHelpermDragHelper = ViewDragHelper.create(this, mCallback);}ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {@Overridepublic boolean tryCaptureView(View child, int pointerId) {//返回true return true;}};}
然后我们就要去处理拦截事件也就是重写一些onInterceptTouchEvent和onTouchEvent方法,默认是不拦截的:
/*** 传递触摸事件** @param ev* @return*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {//交给ViewDragHelper判断是否去拦截事件return mDragHelper.shouldInterceptTouchEvent(ev);}@Overridepublic boolean onTouchEvent(MotionEvent event) {try {mDragHelper.processTouchEvent(event);} catch (Exception e) {e.printStackTrace();}//返回true,这里表示去拦截事件return true;}
然后我们去重写一下ViewDragHelper里面的clampViewPositionHorizontal方法:
@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {return left;}
好了这个时候,就已经可以实现滑动了,我们先来看下结果:
这里我们可以看到,已经可以滑动了,好了接下来的就是要处理滑动事件,去放置到正确的地方(call me 和删除刚开始不能见,还有只能左滑显示,右滑隐藏)。
好了,我们先获取两个View吧:
/*** 当xml填充完毕的时候*/@Overrideprotected void onFinishInflate() {super.onFinishInflate();/*** 后view*/mBackView = getChildAt(0);/*** 前view*/mFrontView = getChildAt(1);}
获取想要的宽和高:
/*** 在这里获取宽和高** @param w* @param h* @param oldw* @param oldh*/@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);/*** 高度*/mHeight = mFrontView.getMeasuredHeight();/*** 宽度*/mWidth = mFrontView.getMeasuredWidth();/*** 移动距离*/mRange = mBackView.getMeasuredWidth();}
摆放这两个view的位置:
/*** 摆放位置* @param changed* @param left* @param top* @param right* @param bottom*/@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);layoutContent(false);}private void layoutContent(boolean isOpen) {//摆放前viewRect frontRect = computeFrontViewRect(isOpen);mFrontView.layout(frontRect.left, frontRect.top, frontRect.right, frontRect.bottom);//摆放后viewRect backRect = computeBackViewRect(frontRect);mBackView.layout(backRect.left,backRect.top,backRect.right,backRect.bottom);//前置前viewbringChildToFront(mFrontView);}/*** 我们可以把前view相当于一个矩形** @param frontRect* @return*/private Rect computeBackViewRect(Rect frontRect) {int left = frontRect.right;return new Rect(left, 0, left + mRange, 0 + mHeight);}private Rect computeFrontViewRect(boolean isOpen) {int left = 0;if (isOpen) {left = -mRange;}return new Rect(left, 0, left + mWidth, 0 + mHeight);}
当然这个实现,只是可以拖拽了前view,因为我们没有把改变的dx传递下去,好了来实现拖拽前view的时候,后view也跟着出来(ViewDragHelper里面的方法):
/*** 当view位置改变的时候* @param changedView 改变的view* @param left* @param top* @param dx x轴偏移量* @param dy*/@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {super.onViewPositionChanged(changedView, left, top, dx, dy);//传递事件,如果是拖拽的前view,if (changedView == mFrontView){//Offset this view's horizontal location by the specified amount of pixels.//也就是说我的我的前view左滑了dx,那么我的后view也是左滑dx,右滑同理mBackView.offsetLeftAndRight(dx);} else if (changedView == mBackView){//拖拽的是后view的话,前View的处理方式一样mFrontView.offsetLeftAndRight(dx);}//兼容老版本invalidate();}
好了这个时候我们来看下效果:
是不是发现了问题,就是我的前view想要的结果是不能右滑的(只允许左滑和返回),那么接下来就实现这个想要的结果吧。以下的代码是在clampViewPositionHorizontal()方法里面:
//在这里处理放置的逻辑拖拽的前viewif (child == mFrontView) {if (left > 0) {return 0;} else if (left < -mRange) {return -mRange;}}//拖拽的后viewelse if (child == mBackView) {if (left > mWidth) {return mWidth;} else if (left < mWidth - mRange) {return mWidth - mRange;}}
看下效果图:
好了,这个时候已经基本实现了,接下来实现以下滑动的距离和速度【判断是否打开和关闭:
/*** 拖拽的view释放的时候** @param releasedChild* @param xvel* @param yvel*/@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {if (xvel == 0 && mFrontView.getLeft() < -mRange / 2.0f) {open();} else if (xvel < 0) {open();} else {close();}}/*** 关闭*/public void close() {Utils.showToast(getContext(), "close");layoutContent(false);}//打开public void open() {//Utils.showToast(getContext(), "open");layoutContent(true);}
好了,接下来实现以下平滑的关闭和打开:
public void close() {close(true);}/*** 关闭** @param isSmooth*/public void close(boolean isSmooth) {int finalLeft = 0;if (isSmooth) {//开始动画 如果返回true表示没有完成动画if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {ViewCompat.postInvalidateOnAnimation(this);}} else {layoutContent(false);}}public void open() {open(true);}/*** 打开** @param isSmooth*/public void open(boolean isSmooth) {int finalLeft = -mRange;if (isSmooth) {//开始动画if (mDragHelper.smoothSlideViewTo(mFrontView, finalLeft, 0)) {ViewCompat.postInvalidateOnAnimation(this);}} else {layoutContent(true);}}/*** 持续动画 */@Overridepublic void computeScroll() {super.computeScroll();//这个是固定的if (mDragHelper.continueSettling(true)) {ViewCompat.postInvalidateOnAnimation(this);}}
我们看下最终的效果吧:
好了,在这里我们加上一些回调,以方便外部使用的时候可以回调:
/*** 默认状态是关闭*/private Status status = Status.Close;private OnSwipeLayoutListener swipeLayoutListener;public Status getStatus() {return status;}public void setStatus(Status status) {this.status = status;}public OnSwipeLayoutListener getSwipeLayoutListener() {return swipeLayoutListener;}public void setSwipeLayoutListener(OnSwipeLayoutListener swipeLayoutListener) {this.swipeLayoutListener = swipeLayoutListener;}/*** 定义三种状态*/public enum Status {Close, Open, Draging}/*** 定义回调接口 这个在我们*/public interface OnSwipeLayoutListener {/*** 关闭** @param mSwipeLayout*/void onClose(SwipeLayout mSwipeLayout);/*** 打开** @param mSwipeLayout*/void onOpen(SwipeLayout mSwipeLayout);/*** 绘制** @param mSwipeLayout*/void onDraging(SwipeLayout mSwipeLayout);/*** 要去关闭*/void onStartClose(SwipeLayout mSwipeLayout);/*** 要去开启*/void onStartOpen(SwipeLayout mSwipeLayout);}
dispatchSwipeEvent()方法(在onViewPositionChanged()方法中调用)
protected void dispatchSwipeEvent() {//判断是否为空if (swipeLayoutListener != null) {swipeLayoutListener.onDraging(this);}// 记录上一次的状态Status preStatus = status;// 更新当前状态status = updateStatus();if (preStatus != status && swipeLayoutListener != null) {if (status == Status.Close) {swipeLayoutListener.onClose(this);} else if (status == Status.Open) {swipeLayoutListener.onOpen(this);} else if (status == Status.Draging) {if (preStatus == Status.Close) {swipeLayoutListener.onStartOpen(this);} else if (preStatus == Status.Open) {swipeLayoutListener.onStartClose(this);}}}}
updateStatus()方法:
/*** 更新状态** @return*/private Status updateStatus() {//得到前view的左边位置int left = mFrontView.getLeft();if (left == 0) {//如果位置是0,就是关闭状态return Status.Close;} else if (left == -mRange) {//如果左侧边距是后view的宽度的负值,状态为开return Status.Open;}//其他状态就是拖拽return Status.Draging;}
好了,事件基本上已经实现完毕了,这个侧拉删除的我会更新至我的项目中,同时希望Android高仿QQ6.0侧滑删除实例代码对大家有所帮助。
新闻热点
疑难解答
图片精选