Scroller的用法,VelocityTracker用法,以及滑动冲突的处理

参考:http://blog.csdn.net/yanghuinipurean/article/details/50419455

/**
 * 仿微信刷新
 * @author Nipuream
 */
public class WXLayout extends LinearLayout{

    private static final String TAG = "WXLayout";
    private int mTouchSlop;
    private boolean mIsBeingDragged = false;
    private float mLastMotionY;
    private float  mInitialMotionY;
    private float resistance = 0.6f;
    private Scroller mScroller;
    private ListView mListView;
    private boolean isMove = false;
    private int duration = 300;
    private ScrollRershListener l;
    private boolean isRersh = false;

    public WXLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        init(context);
    }

    private void init(final Context context){
        ViewConfiguration config = ViewConfiguration.get(context);
        mTouchSlop = config.getScaledTouchSlop();
        DecelerateInterpolator interpolator = new DecelerateInterpolator();
        mScroller = new Scroller(context,interpolator);
        post(new Runnable() {
            @Override
            public void run() {
                // ?主线程的最后才获取mListView?
                mListView = (ListView) WXLayout.this.getChildAt(0);
            }
        });
    }


    //处理滑动冲突
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        final  int action = ev.getAction();

        //手势抬起和取消,不阻碍事件传递
        if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP){
            mIsBeingDragged = false;
            return false;
        }

        //如果mIsBeingDragged为true表示正在拖动,那么除了down事件都不发送给子元素
        //down事件应该要发给子元素,因为down触发时,不能判定之后父元素是否会滑动,从而拦截子元素的事件
        if (action != MotionEvent.ACTION_DOWN && mIsBeingDragged) {
            return true;
        }

        switch(action){
        case MotionEvent.ACTION_DOWN:{
            //记录按下的位置,down事件一定会走到此处
            mLastMotionY = mInitialMotionY = ev.getY();
            mIsBeingDragged = false;
            break;
        }
        case MotionEvent.ACTION_MOVE:{
            final float y = ev.getY(), x = ev.getX();
            final float diff, absDiff;
            diff = y - mLastMotionY;
            absDiff = Math.abs(diff);
            if(absDiff > mTouchSlop){   //如果滑动的距离足够大
                if(diff > 1){           //并且是手指向下滑动
                    //并且ListView的第一个元素已经可以被看到了
                    if(mListView.getFirstVisiblePosition()==0){ 
                        View view = mListView.getChildAt(0);
                        Rect rect = new Rect ();
                        view.getLocalVisibleRect(rect);
                        //并且第一个元素已经滑到listView的顶部了
                        if(rect.top == 0){
                            //那么事件将由listView的父元素接管,来模拟微信的效果,
                            //mInitialMotionY应该在此处赋值,当作滑动的起始点
                            mLastMotionY = y;
                            mIsBeingDragged = true;
                        }
                    }
                }
            }
            break;
        }
        }
        return mIsBeingDragged;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        // TODO Auto-generated method stub

        //如果碰触到控件的边缘,就不接受这一系列的action了
        if (event.getAction() == MotionEvent.ACTION_DOWN && event.getEdgeFlags() != 0) {
            return false;
        }

        //如果Scroller正在滑动,就不接受这次事件了
        if(isMove){
            return false;
        }

        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:{
            //因为此Layout元素不会拦截down事件,所以按理说执行不到此处?
            mLastMotionY = mInitialMotionY = event.getY();
            return true;
        }
        case MotionEvent.ACTION_MOVE:{
            if (mIsBeingDragged) {
                if(l!=null && !isRersh){
                    l.startRersh();
                    isRersh = true;
                }
                mLastMotionY = event.getY();
                float moveY = mLastMotionY - mInitialMotionY;   //计算layout的滑动距离
                if(l != null){
                    l.Rersh(moveY);
                }
                if(moveY > 0){  //设置layout的下拉位置
                    int value = (int) Math.abs(moveY);
                    scrollTo(0, - (int)(value*resistance));
                }
                return true;
            }
            break;
        }
        case MotionEvent.ACTION_CANCEL:
        case MotionEvent.ACTION_UP:{
            //将layout用动画mScroller.startScroll来复原
            if(mIsBeingDragged){
                mIsBeingDragged = false;
                startMoveAnim(getScrollY(), Math.abs(getScrollY()), duration);
                if(l!= null && isRersh && (event.getY() - mInitialMotionY) > 0){
                    l.endRersh(event.getY() - mInitialMotionY);
                    isRersh = false;
                }
                return true;
            }
            break;
        }
        }
        return super.onTouchEvent(event);
    }

    public void startMoveAnim(int startY, int dy, int duration) {
        isMove = true;
        mScroller.startScroll(0, startY, 0, dy, duration);
        invalidate();//通知UI线程的更新
    }

    @Override
    public void computeScroll() {
        //判断是否还在滚动,还在滚动为true
        if (mScroller.computeScrollOffset()) {
            scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            //更新界面
            postInvalidate();
            isMove = true;
        } else {
            isMove = false;
        }
        super.computeScroll();
    }

    public interface ScrollRershListener{
        void Rersh(float value);
        void startRersh();
        void endRersh(float value);
    }

    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    public void setOnScrollRershListener(ScrollRershListener l){
        this.l = l;
    }
}


/**
 * 仿QQ侧滑删除
 * @author Nipuream
 *
 */
public class SlideListView extends ListView implements OnTouchListener,OnClickListener{

    private static final String TAG = "SlideListView";
    private Context mContext;
    private Scroller mScroller; 
    /**
     * 初始值
     */
    private float initalXvalue,mLastXvalue;
    private float initalYvalue,mLastYvalue;
    /**
     * 确认滑动的最小速度
     */
    private int MIN_VELOCITY = 800;
    /**
     * 速度跟踪器
     */
    private VelocityTracker velocityTracker;
    /**
     * 正在被拖动的view
     */
    private View dragView;
    /**
     * 正在被拖动的position
     */
    private int touchPos;
    /**
     * 默认的最小滑动距离
     */
    private int mTouchSlop;
    /**
     * 滑动时间
     */
    private int DURATION_TIME = 300;
    /**
     * 出现删除按钮的Item
     */
    private View tempView ;
    /**
     * 出现删除按钮的position
     */
    private int tempPos ;
    /**
     * 删除按钮
     */
    private Button deleteBtn;
    /**
     * 移除接口
     */
    private RemoveItemListener l;
    /**
     * 是否可以滑动
     */
    private boolean isSlide = false;

    public SlideListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // TODO Auto-generated constructor stub
        init(context);
    }

    private void init(Context context){
        mContext = context;
        AccelerateInterpolator interpolator = new AccelerateInterpolator();
        mScroller = new Scroller(context,interpolator);
        velocityTracker = VelocityTracker.obtain();
        ViewConfiguration config = ViewConfiguration.get(context);
        mTouchSlop = config.getScaledTouchSlop();
        setOnTouchListener(this);
    }

    /**
     * 捕捉用户到底拖动了哪个view
     * 拦截事件
     */

    //此方法一定会被调用,所以在此方法内纪录触摸到的Item
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // TODO Auto-generated method stub
        final int action = ev.getAction();

        if (action == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
            return false;
        }

        if(action == MotionEvent.ACTION_DOWN){

            mLastXvalue = initalXvalue = ev.getX();
            mLastYvalue = initalYvalue = ev.getY();

            //正在滑动时,事件继续分发
            if(!mScroller.isFinished()){
                return super.dispatchTouchEvent(ev);
            }

            //通过点击位置,查出所点击的Item的索引
            touchPos  = pointToPosition((int)mLastXvalue, (int)mLastYvalue);

            if(touchPos == AdapterView.INVALID_POSITION){
                return super.dispatchTouchEvent(ev);
            }

            GetTracker(ev);
            //取得当前触摸的Item
            dragView = getChildAt(touchPos - getFirstVisiblePosition());
            isSlide = false;
        }else if(action == MotionEvent.ACTION_MOVE){
            mLastXvalue = ev.getX();
            mLastYvalue = ev.getY();
            if(velocityTracker == null){
                GetTracker(ev);
            }
            velocityTracker.computeCurrentVelocity(1000);
            //当横向滑动速度足够大,或者横向滑动距离足够大时,标记Item为可以滑动
            if(Math.abs(velocityTracker.getXVelocity()) > MIN_VELOCITY 
                    ||Math.abs(mLastXvalue - initalXvalue)> mTouchSlop 
                    && Math.abs(mLastYvalue - initalYvalue)  < mTouchSlop){
                    isSlide = true;
            }
        }else if(action == MotionEvent.ACTION_UP){
            CloseTracker();
        }
        return super.dispatchTouchEvent(ev);
    }


    /**
     * 消费事件
     */
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if(isSlide){
            switch(ev.getAction()){
            case MotionEvent.ACTION_MOVE:
            {
                mLastXvalue = ev.getX();
                final float moveX = mLastXvalue - initalXvalue;
                //横向滑动大于特定值时,dragView跟随手指的位置向左移动
                if(Math.abs(moveX) > dip2px(mContext, 20))
                {
                    if(moveX < 0 && Math.abs(moveX)<dip2px(mContext, 100))
                    {
                        if(dragView != null){
                            dragView.scrollTo(Math.abs((int)moveX), 0);
                        }
                        return true;
                    }
                }
            }
            break;
            case MotionEvent.ACTION_UP:
            {
                mLastXvalue = ev.getX();
                float scrollDistance = mLastXvalue - initalXvalue;
                //手指抬起时,滑到最左或者划回原位
                if(scrollDistance < 0){
                    if(Math.abs(scrollDistance) < dip2px(mContext, 50)){
                        //滑动回去
                        if(dragView != null){
                            mScroller.startScroll(dragView.getScrollX(), 0, -dragView.getScrollX(), 0, DURATION_TIME);
                            invalidate();
                        }
                    }else if(Math.abs(scrollDistance) > dip2px(mContext, 50) ){
                        //滑动到底
                        if(dragView != null){
                            mScroller.startScroll(dragView.getScrollX(), 0, (dip2px(mContext, 100) - Math.abs(dragView.getScrollX())), 0,DURATION_TIME);
                            invalidate();
                            tempView = dragView;
                            tempPos = touchPos;
                            setListener();
                        }
                    }
                }
            }
            break;
            }
        }
        return super.onTouchEvent(ev);
    }

    private void GetTracker(MotionEvent ev){
        if(velocityTracker == null){
            velocityTracker  = VelocityTracker.obtain();
        }
        velocityTracker.addMovement(ev);
    }

    private void CloseTracker() {
        if(velocityTracker != null){
            velocityTracker.recycle();
            velocityTracker.clear();
            velocityTracker = null;
        }
    }

    private void setListener(){
        deleteBtn = (Button) tempView.findViewById(R.id.delete);
        deleteBtn.setOnClickListener(this);
    }

    @Override
    public void computeScroll() {
        // TODO Auto-generated method stub
        if(mScroller.computeScrollOffset()){
            dragView.scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
            postInvalidate();
        }
        super.computeScroll();
    }


    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }


    @Override
    //返回true将使onTouchEvent无法触发,当某个Item左滑后显示出delete按钮,
    //则点击整个layout时,Item复位,且onTouchEvent不能被触发
    public boolean onTouch(View v, MotionEvent event) {
        if(tempView != null){
            dragView = tempView ;
            mScroller.startScroll(dragView.getScrollX(), 0, - dragView.getScrollX(), 0,DURATION_TIME);
            invalidate();
            deleteBtn.setOnClickListener(null);
            deleteBtn = null;
            tempView = null;
            return true;
        }
        return false;
    }

    @Override
    //点击delete按钮的回调
    public void onClick(View v) {
        // TODO Auto-generated method stub
        if(l != null){
            l.remove(tempPos);
            dragView = tempView;
            dragView.scrollTo(0, 0);
            tempView = null;
            invalidate();
        }
    }

    public interface RemoveItemListener{
        void remove(int pos);
    }

    public void setOnRemoveItemListener(RemoveItemListener l){
        this.l = l;
    }

}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值