转载请注明出处:http://blog.csdn.net/binbinqq86/article/details/46010951
项目用到了ListView的侧滑删除的功能,由于当时项目比较赶,就随便在网上找了一个,但是效果不是太好,最近闲了下来,就想自己实现一个,于是就按照QQ的联系人列表的侧滑菜单做了一个,效果基本上是一模一样的。在这个过程中,自己也学习到了不少的东西,下面就把这个过程跟大家分享出来。
废话不多说,首先上效果图。
看完了图如果感觉效果不好,请不要拍砖,好的话请继续往下看~
下面结合代码说说实现的原理:首先自定义一个ViewGroup来实现item的滑动效果。
package com.binbin.slidedelmenu.item;
import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.FrameLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.Scroller;
/**
* Created by -- on 2016/11/3.
* 带侧滑菜单的自定义item
*/
public class MenuItem extends ViewGroup {
private int contentWidth;
private Scroller mScroller;
private int maxWidth,maxHeight;//viewGroup的宽高
private static final int MIN_FLING_VELOCITY = 600; // dips per second
/**最小滑动距离,超过了,才认为开始滑动 */
private int mTouchSlop = 0 ;
/**上次触摸的X坐标*/
private float mLastX = -1;
/**第一次触摸的X坐标*/
private float mFirstX = -1;
private int ratio;
//防止多只手指一起滑动的flag 在每次down里判断, touch事件结束清空
private static boolean isTouching;
private int mRightMenuWidths;//右侧菜单总宽度
private VelocityTracker mVelocityTracker;
private float mMaxVelocity;
private int mPointerId;//多点触摸只算第一根手指的速度
private boolean isQQ=true;//是否是qq效果
private boolean qqInterceptFlag;//qq效果判断标志
private static MenuItem mViewCache;//存储的是当前正在展开的View
private boolean isUserSwiped;// 判断手指起始落点,如果距离属于滑动了,就屏蔽一切点击事件
//仿QQ,侧滑菜单展开时,点击除侧滑菜单之外的区域,关闭侧滑菜单。
//增加一个布尔值变量,dispatch函数里,每次down时,为true,move时判断,如果是滑动动作,设为false。
//在Intercept函数的up时,判断这个变量,如果仍为true 说明是点击事件,则关闭菜单。
private boolean isUnMoved = true;
public MenuItem(Context context) {
this(context,null);
}
public MenuItem(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public MenuItem(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public MenuItem(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init(){
contentWidth=getContext().getResources().getDisplayMetrics().widthPixels;
mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mScroller=new Scroller(getContext());
mMaxVelocity = ViewConfiguration.get(getContext()).getScaledMaximumFlingVelocity();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setClickable(true);//令自己可点击,从而获取触摸事件(很重要,必须写在onMesaure中)
mRightMenuWidths=0;//由于ViewHolder的复用机制,每次这里要手动恢复初始值
/**
* 根据childView计算的出的宽和高,计算容器的宽和高,主要用于容器是warp_content时
*/
for (int i = 0,count = getChildCount(); i < count; i++) {
View childView = getChildAt(i);
//令每一个子View可点击,从而获取触摸事件
childView.setClickable(true);
if(childView.getVisibility()!=View.GONE){
//获取每个子view的自己高度宽度,取最大的就是viewGroup的大小
measureChild(childView, widthMeasureSpec, heightMeasureSpec);
maxWidth = Math.max(maxWidth,childView.getMeasuredWidth());
maxHeight = Math.max(maxHeight,childView.getMeasuredHeight());
if(i>0){//第一个是content,后面的都是菜单
if(childView.getLayoutParams().width== LayoutParams.MATCH_PARENT){
//菜单的宽不能MATCH_PARENT
throw new IllegalArgumentException("======menu'width can't be MATCH_PARENT=====");
}
mRig