——-不知道说些啥..

嗯,前面我们仿着做了QQ的侧拉菜单,现在来做做侧拉删除,复习复习下我们ViewDragHelper的使用,先来看下效果吧

右边的是整合上次的侧滑菜单效果,没看过想了解的朋友可以去看看侧滑菜单的实现http://blog.csdn.net/z8z87878/article/details/51731221
额,控件缓慢拖动判断距离是否打开或关闭也做了,后面再录着看效果吧,好了,现在开始我们的自定义侧拉删除吧.其实这个和上次那个侧拉滑动还是挺像的是不是,其实是这样的,像这种自定义拖动容器,就是那几个类那几个方法,所以我们在熟悉这几个类和方法的情况下,其实自定义这种拖动容器是并不难的.开始自定义前,我们的第一个问题就是继承那个容器控件,如果自己继承ViewGroup的话,是挺不好的,它要我们写更多的代码,所以我们应该去继承那些常用的容器控件,其实也没几个是吧.我一开始根据这个控件的特性想的是继承LineLayout,单个条目是没问题的,但是把它整合到ListView的时候出问题了,不能显示,从来没碰过这种情况,我当时就懵了….后来试着继承FrameLayout.可以.所以我们这个控件继承FrameLayout,这为什么继承lineLayout然后ListView不显示,其中有多少不为人知的秘密……请你们探索着然后告诉我….

我们继承frameLayout要实现上面的效果,所以我们要重写onLayout布局方法

  @Overrideprotected void onFinishInflate() {  //视图解析完成后调用,这时候就能拿到我们的孩子对象了super.onFinishInflate();mRightView = getChildAt(0);       //右边选项界面mMainView = getChildAt(1);         //主面板}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {  //测量完成后调用,这时我们可以拿到view的大小super.onSizeChanged(w, h, oldw, oldh);mMainWidth = mMainView.getMeasuredWidth();mMainHeight = mMainView.getMeasuredHeight();mRightWidth = mRightView.getMeasuredWidth();mRange = mRightWidth;                 //拖动范围}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);mRightView.layout(mMainWidth,0,mMainWidth + mRightWidth,mMainHeight);}

这样就实现我们图片中的效果了,静态初始位置布置妥当.我们就可以用我们的ViewDragHelper了来帮助我们处理拖动事件了,这前面侧滑菜单已经讲过了,这里提下大致过程,首选我们得到它的对象ViewDragHelper create(ViewGroup forParent, Callback cb)要写一个类继承它的内部类ViewDragHelper.CallBack, 其中tryCaptureView(View child, int pointerId)是抽象方法,我们必须实现,想处理拖动事件返回true,因为这是水平拖动,所以我们还要重写clampViewPositionHorizontal(View child, int left, int dx)它默认返回0,所以我们要判断left它给我们的建议值,我们要判断它的范围,我们再返回自己计算的left,然后,相应的还要竖直的,我们用不到不重写

 /*** @param child 拖动那个孩子* @param left  拖动建议值,view根据返回的建议值拖动,相对于左位负,相对于右为正,是view相对物理窗口左边的位置* @param dx    变化值* @return 默认返回0, 即不拖动*/@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {if (child == mMainView) {if (left > 0) {left = 0;} else if (left < -mRange) {left = -mRange;               //拖动范围}} else if (child == mRightView) {if (left < mMainWidth - mRightWidth) {left = mMainWidth - mRightWidth;} else if (left > mMainWidth) {left = mRange;}}return left;}

还要个方法叫onViewPositionChanged(View changedView, int left, int top, int dx, int dy) 重写这个方法可以帮助我们在一个view拖动的时候,我们动态layout另一个view达到跟着动的效果,也可以在这设置回调之类的东西

@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if (changedView == mMainView) {mRightView.layout(mMainWidth + left, mRightView.getTop(), mMainWidth + left + mRightWidth, mRightView.getBottom());} else if (changedView == mRightView) {mMainView.layout(mRightView.getLeft() - mMainWidth, 0, mRightView.getLeft(),mMainHeight);}if (mOnDraeListener != null) {int mManLeft = mMainView.getLeft();if (!isOpen && mManLeft == - mRightWidth){     //打开状态,原来是打开的就不用重新回调了mOnDraeListener.onOpen(mDragLayout);isOpen = true;}else if (isOpen && mManLeft == 0){mOnDraeListener.onclose(mDragLayout);isOpen = false;isStartOpen = false;}else{mOnDraeListener.onDrag(changedView, left);  //接口回调if (!isOpen && !isStartOpen){                //只有在关闭的情况下才是要开始打开了mOnDraeListener.onStartOpen(mDragLayout);isStartOpen = true;}}}invalidate();}

接口回调,用来处理比如滑动条目的时候关闭其它已经打开的条目之类的

public interface onDraeListener {void onDrag(View view, int left);void onOpen(DragLayout dragLayout);  //打开了void onclose(DragLayout dragLayout);  //关闭了void onStartOpen(DragLayout dragLayout); //开始打开,不一定打开}private onDraeListener mOnDraeListener;   //引用public void setOnDraeListener(onDraeListener onDraeListener) {  //由外部传个名字mOnDraeListener = onDraeListener;}

还有一个就是onViewReleased(View releasedChild, float xvel, float yvel)我们在这个方法里根据画出的距离或者滑的速度方向,判断我们是要打开,还是关闭.

 // xvel           //水平速度@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {if (xvel < 0) {      //认为松手后还有左的,可以叫惯性速度么...就打开open();} else if (xvel == 0) {                                           //不确定是关闭还是打开状态if (isOpen) {if (mMainView.getLeft() > -mRange / mRightChildeCount * (mRightChildeCount - 1)) {close();                                         //打开状态,即向右滑超过三分之一关闭} else {open();}} else {                                                 //关闭状态跟上面一样向左滑if (mMainView.getLeft() > -mRange / mRightChildeCount) {close();} else {open();}}} else if (xvel > 0) {                                        //打开状态即向右滑close();}}}public void open() {mViewDragHelper.smoothSlideViewTo(mMainView, -mRange, 0);ViewCompat.postInvalidateOnAnimation(this);}public void close() {mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);ViewCompat.postInvalidateOnAnimation(this);}@Overridepublic void computeScroll() {super.computeScroll();if (mViewDragHelper.continueSettling(true)) {ViewCompat.postInvalidateOnAnimation(this);}}

这里讲下自定义属性mRightChildCount

public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DragLayout, defStyleAttr, 0);mRightChildeCount = typedArray.getInt(R.styleable.DragLayout_rightChildCount, -1);if (mRightChildeCount < 2) {throw new IllegalArgumentException("DragLayout右面板孩子数最少为两个");}mViewDragHelper = ViewDragHelper.create(this, new MyCallBack());mDragLayout = this;}

其实我这里是没有必要自定义属性的,但我代码已经上传了,有兴趣的自己把自定义属性去了吧,既然我当初做了,就教不会的朋友一下怎么自定义属性的,其实我一开始也是不会的,但我们可以想想,那些控件的属性是怎么定义的呢,所以我到LineLayout的构造函数中去看了下,它是这样的

没见过猪跑,你吃过猪肉吧.属性是通过attrs来的,所以我们去找找SDK的attrs文件看看,路径是SDK\platforms\android-23\data\res\values,打开,我们找到LineLayout,是这样的

<declare-styleable name="LinearLayout"><!-- Should the layout be a column or a row?  Use "horizontal"for a row, "vertical" for a column.  The default ishorizontal. --><attr name="orientation" /><attr name="gravity" /><!-- When set to false, prevents the layout from aligning its children'sbaselines. This attribute is particularly useful when the childrenuse different values for gravity. The default value is true. --><attr name="baselineAligned" format="boolean" /><!-- When a linear layout is part of another layout that is baselinealigned, it can specify which of its children to baseline align to(that is, which child TextView).--><attr name="baselineAlignedChildIndex" format="integer" min="0"/><!-- Defines the maximum weight sum. If unspecified, the sum is computedby adding the layout_weight of all of the children. This can beused for instance to give a single child 50% of the total availablespace by giving it a layout_weight of 0.5 and setting the weightSumto 1.0. --><attr name="weightSum" format="float" /><!-- When set to true, all children with a weight will be considered havingthe minimum size of the largest child. If false, all children aremeasured normally. --><attr name="measureWithLargestChild" format="boolean" /><!-- Drawable to use as a vertical divider between buttons. --><attr name="divider" /><!-- Setting for which dividers to show. --><attr name="showDividers"><flag name="none" value="0" /><flag name="beginning" value="1" /><flag name="middle" value="2" /><flag name="end" value="4" /></attr><!-- Size of padding on either end of a divider. --><attr name="dividerPadding" format="dimension" /></declare-styleable>

所以我们写自己的attrs的一般格式为

<declare-styleable name="xxxxxx"><attr name="xxx" format="xxx"/>   </declare-styleable>

即我是这样写的

<declare-styleable name="DragLayout"><attr name="rightChildCount" format="integer"></attr></declare-styleable>

写好了这个类,我们还要把touch事件传给我们的ViewDragOpenHelper

@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return mViewDragHelper.shouldInterceptTouchEvent(ev);  //交给我们的mViewDragHelper去判断该不该拦截}@Overridepublic boolean onTouchEvent(MotionEvent event) {try {mViewDragHelper.processTouchEvent(event);               //交给我们的mViewDragHelper去操作触摸事件,事件被拦截有可能会出错,try一下} catch (Exception e) {e.printStackTrace();}return true;}

好了,来看看不整合到ListView的效果

好吧,鼠标不好控制,刚开始的鼠标抬起带了点向左的速度所以打开了/没问题现在就可以整合到listView了,你会发现,打开后,滑右面板,向右滑动不了,为什么呢O(∩_∩)O~,因为和上一次侧拉一样,我们没有重写ViewDragHelper.CallBack中的这个方法

 @Overridepublic int getViewHorizontalDragRange(View child) {  //水平移动范围,我们最好重写这个方法!不然碰到listView拖不动return mRange;}

这个方法一定要重写!!!!!!!!如果是竖直方向的就重写竖直方向的,不然碰到listView你就等着哭吧,其实我一开始是写了的,昨天晚上我写好了还没整到ListView上,说不信邪看看能不能滑动,然后今天上午因为一开始继承的是LineLayout的,listView不显示就把我整懵了,然后就忘了,将近一个小时候看代码才发现…我真是作死小能手….重写后就没问题了,这里再说明下滑动关闭条目的处理,我们用一个ArrayList在回调onOpen的时候存储我们的打开的条目,然后close的时候移除它,在onStartOpen的时候遍历的List关闭里面的条目,并清空,在ListView的滑动也是遍历这个list关闭所有条目,并清空,详情可以下载下来看看,下载地址http://download.csdn.net/detail/z8z87878/9557318
这里我贴下这个控件的完整代码

import android.content.Context;
import android.content.res.TypedArray;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;import com.it.draglayout.R;/*** Created by Root on 2016/6/22.*/
public class DragLayout extends FrameLayout {private View mMainView;private View mRightView;private int mMainWidth;private int mRightWidth;private ViewDragHelper mViewDragHelper;private int mRange;private int mMainHeight;private boolean isOpen = false;private int mRightChildeCount;private boolean isStartOpen = false;DragLayout mDragLayout;public DragLayout(Context context) {this(context, null);}public DragLayout(Context context, AttributeSet attrs) {this(context, attrs, 0);}public DragLayout(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DragLayout, defStyleAttr, 0);mRightChildeCount = typedArray.getInt(R.styleable.DragLayout_rightChildCount, -1);if (mRightChildeCount < 2) {throw new IllegalArgumentException("DragLayout右面板孩子数最少为两个");}mViewDragHelper = ViewDragHelper.create(this, new MyCallBack());mDragLayout = this;}@Overrideprotected void onFinishInflate() {  //视图解析完成后调用,这时候就能拿到我们的孩子对象了super.onFinishInflate();mRightView = getChildAt(0);       //右边选项界面mMainView = getChildAt(1);         //主面板}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {  //测量完成后调用,这时我们可以拿到view的大小super.onSizeChanged(w, h, oldw, oldh);mMainWidth = mMainView.getMeasuredWidth();mMainHeight = mMainView.getMeasuredHeight();mRightWidth = mRightView.getMeasuredWidth();mRange = mRightWidth;                 //拖动范围}@Overrideprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {super.onLayout(changed, left, top, right, bottom);mRightView.layout(mMainWidth,0,mMainWidth + mRightWidth,mMainHeight);}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {return mViewDragHelper.shouldInterceptTouchEvent(ev);  //交给我们的mViewDragHelper去判断该不该拦截}@Overridepublic boolean onTouchEvent(MotionEvent event) {try {mViewDragHelper.processTouchEvent(event);               //交给我们的mViewDragHelper去操作触摸事件,事件被拦截有可能会出错,try一下} catch (Exception e) {e.printStackTrace();}return true;}class MyCallBack extends ViewDragHelper.Callback {@Overridepublic boolean tryCaptureView(View child, int pointerId) {  //要求我们重写,返回true执行拖到事件,false不执行return true;}@Overridepublic int getViewHorizontalDragRange(View child) {  //水平移动范围,我们最好重写这个方法!不然碰到listView拖不动return mRange;}/*** @param child 拖动那个孩子* @param left  拖动建议值,view根据返回的建议值拖动,相对于左位负,相对于右为正,是view相对物理窗口左边的位置* @param dx    变化值* @return 默认返回0, 即不拖动*/@Overridepublic int clampViewPositionHorizontal(View child, int left, int dx) {if (child == mMainView) {if (left > 0) {left = 0;} else if (left < -mRange) {left = -mRange;               //拖动范围}} else if (child == mRightView) {if (left < mMainWidth - mRightWidth) {left = mMainWidth - mRightWidth;} else if (left > mMainWidth) {left = mRange;}}return left;}@Overridepublic void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {if (changedView == mMainView) {mRightView.layout(mMainWidth + left, mRightView.getTop(), mMainWidth + left + mRightWidth, mRightView.getBottom());} else if (changedView == mRightView) {mMainView.layout(mRightView.getLeft() - mMainWidth, 0, mRightView.getLeft(),mMainHeight);}if (mOnDraeListener != null) {int mManLeft = mMainView.getLeft();if (!isOpen && mManLeft == - mRightWidth){     //打开状态,原来是打开的就不用重新回调了mOnDraeListener.onOpen(mDragLayout);isOpen = true;}else if (isOpen && mManLeft == 0){mOnDraeListener.onclose(mDragLayout);isOpen = false;isStartOpen = false;}else{mOnDraeListener.onDrag(changedView, left);  //接口回调if (!isOpen && !isStartOpen){                //只有在关闭的情况下才是要开始打开了mOnDraeListener.onStartOpen(mDragLayout);isStartOpen = true;}}}invalidate();}// xvel           //水平速度@Overridepublic void onViewReleased(View releasedChild, float xvel, float yvel) {if (xvel < 0) {      //认为松手后还有左的,可以叫惯性速度么...就打开open();} else if (xvel == 0) {                                           //不确定是关闭还是打开状态if (isOpen) {if (mMainView.getLeft() > -mRange / mRightChildeCount * (mRightChildeCount - 1)) {close();                                         //打开状态,即向右滑超过三分之一关闭} else {open();}} else {                                                 //关闭状态跟上面一样向左滑if (mMainView.getLeft() > -mRange / mRightChildeCount) {close();} else {open();}}} else if (xvel > 0) {                                        //打开状态即向右滑close();}}}public void open() {mViewDragHelper.smoothSlideViewTo(mMainView, -mRange, 0);ViewCompat.postInvalidateOnAnimation(this);}public void close() {mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);ViewCompat.postInvalidateOnAnimation(this);}@Overridepublic void computeScroll() {super.computeScroll();if (mViewDragHelper.continueSettling(true)) {ViewCompat.postInvalidateOnAnimation(this);}}public boolean isOpen() {return isOpen;}public interface onDraeListener {void onDrag(View view, int left);void onOpen(DragLayout dragLayout);  //打开了void onclose(DragLayout dragLayout);  //关闭了void onStartOpen(DragLayout dragLayout); //开始打开,不一定打开}private onDraeListener mOnDraeListener;   //引用public void setOnDraeListener(onDraeListener onDraeListener) {  //由外部传个名字mOnDraeListener = onDraeListener;}
}

至于整合到我们上次写的QQ侧滑菜单,我是这样做的,先把那个ViewPager的onInterceptTouchEvent 和 onTouchEvent都返回false了,即它已经不能滑动了,然后把我们这个作为一个Fragment放到MyViewPage上去了,然后这还是会和侧滑产生滑动事件冲突的,向右滑的时候会滑菜单,因为SlideLayout是父容器嘛,事件被它拦截了,所以我在它的onInterceptTouchEvent做了一下修改

 private float mStartX = 0;@Overridepublic boolean onInterceptTouchEvent(MotionEvent event) {if (celaIsOpen){                      //如果侧拉打开了,不拦截向右滑动事件switch (event.getAction()){case MotionEvent.ACTION_DOWN:mStartX = event.getRawX();break;case MotionEvent.ACTION_MOVE:float movX = event.getRawX();if (movX > mStartX){return  false;}break;case MotionEvent.ACTION_UP:mStartX = 0;break;}}return mViewDragHelper.shouldInterceptTouchEvent(event);   //交给它去判断该不该拦截}

有兴趣的可以去这下载整合的代码http://download.csdn.net/detail/z8z87878/9557522

鹅厂系列二 : 仿QQ侧拉删除相关推荐

  1. 鹅厂系列三 : 仿QQ消息拖动小球

    未来会怎样,我不知道,我只是想为了比今天好 老规矩,看看效果 嗯,前面自定义了两个视图容器,今天这个是自定义View,开始自定义前,我们应该理清自己的思路,怎么来做这个东西.用我们的QQ,我们会发现, ...

  2. 鹅厂系列一 : 仿QQ侧滑菜单

    --不会的东西你不尝试的去做,你永远都不会做 好了,跟随潮流,还是先看下效果,不然可能都没人想看下去了(不会看到效果后不想看了吧O(∩_∩)O~) 额,图片资源来自QQ_374.APK,里面四五千个图 ...

  3. 仿QQ拖动删除未读消息个数气泡之二

    在仿QQ拖动删除未读消息个数气泡这篇文章中,模仿了QQ的删除未读消息气泡,不过也遗留了一个问题,当时为了让气泡能够在全屏范围内拖动,不能将其放在布局文件xml中,而是采用了在主布局加载完成后用addV ...

  4. 仿QQ侧滑删除,Listview上下滑动,Listview的iteam的点击事件等bug的解决

         网上ListView横向滑动删除Item这样的介绍也很多,但实用性不强,没有解决横向滑动和item的点击事件的冲突,废话少说,有图有真相,下面直接上代码 1:侧滑 2:侧滑的点击事件 3:i ...

  5. 高仿 QQ 侧滑删除 Item 的效果

    <span style="font-size:24px;"><span style="white-space:pre"> </sp ...

  6. 仿QQ侧滑删除ListView——2015第一博

    一直感觉QQ最近联系人那个侧滑删除功能挺高大上的,经过几经波折,终于在新的一年里实现了该功能.实现这个功能真是费了老劲了,好几次有了想法,兴奋的去写代码实现,结果让代码打了自己一个耳光,最终还是用ma ...

  7. Android动画效果(二) 仿QQ点赞动画

    近日有看到QQ点赞的动画效果,于是模仿写了一个 要实现图中效果,需要用到属性动画,属性动画利用ValueAnimator来跟踪记录对象属性已经变化了多长时间及当前这个时间点的值. 如果不设置的话,动画 ...

  8. 自定义View 篇四《低仿QQ测拉删除》

    都知道QQ有一个比较牛逼的效果就是测拉删除效果,目前这个功能,网上自定义控件也有很多实现方式了,本篇也自己实现一个测拉删除效果的自定义控件.虽然功能一样,实现方式不同罢了,也希望提供一些思路,对自己和 ...

  9. Android学习之仿QQ側滑功能的实现

    如今项目越来越多的应用了滑动删除的功能,Android本来遵循的是长按删除,IOS定制的是滑动删除,不可否认滑动删除确实在客户体验上要好一点,所以看了非常多关于仿QQ滑动删除的样例,还是感觉代码家的A ...

最新文章

  1. tensorflow 代码调试工具tfdbg的用法
  2. 交叉熵损失函数在多分类深度学习中的应用 转
  3. 腾讯多任务模型MFH
  4. Linux设置Oracle开机自启动
  5. “豆瓣酱”之用户,场景,功能
  6. 点击windows live writer无反应 / 使用windows live writer的前提
  7. 机器学习-朴素贝叶斯分类
  8. nginx upstream 模块详解
  9. HLSL编译工具—FXC
  10. OpenGL ES 送显 YUV NV12
  11. 开源免费的pdf文档编辑器LibreOffice
  12. C# object 转 Intptr, Intptr 转 object
  13. 中文分词 及发展现状(总结的不错)
  14. 程序员技术入股的那些坑!保护好你的核心技术,想走?没那么容易!
  15. C语言实现乘方运算---m的n次方(附完整源码)
  16. 细数红帽linux系统下的各个文件夹作用
  17. Unity改变应用游戏的分辨率 resoution
  18. Linux内核中喂狗,m3352linux内核中看门狗喂狗和应用层喂狗方法
  19. 如何实现通过回车键提交表单
  20. 使用Sportsvu数据创建NBA动作视频

热门文章

  1. Java课程设计——蜘蛛纸牌
  2. 分治算法:根号n段合并排序算法
  3. 为什么我觉得Python烂的要死?原因有八
  4. 保龄球计分系统c语言代码,保龄球计分系统计分方法
  5. 不撞南墙不回头----------深度优先搜索
  6. 海云数据首发图易 AI 能力服务平台新品,图易 6 塑造 AI 能力新物种
  7. NetCore之下载文件
  8. 关于JAVA接入Filecoin钱包流程
  9. 实力这个东西就是这么硬!
  10. ​国际物流APP开发软件帮助物流服务行业事项信息化发展​