手把手实现腾讯qq拖拽删去效果(二)

  这节,就一个任务如何把上节自定义的翻页动画控件整进下拉列表中去。

  由于是自定义的下拉列表控件,我们需要自定义能够上啦下滑的listview,这势必会造成这个问题,上拉刷新要响应相应touch事件,拖拽也似乎也要相应触摸事件,这势必会造成了一种事件的冲突了,怎么解决了。我这里用一个变量来区分一下,伪代码如下:

手把手实现腾讯qq拖拽删去效果(二)

  前面说了,这么多了,我们直接点评代码把:

package com.routemap_infomation.utils;



import java.util.Date;



import android.content.Context;

import android.util.AttributeSet;

import android.util.Log;

import android.view.GestureDetector;

import android.view.LayoutInflater;

import android.view.MotionEvent;

import android.view.View;

import android.view.ViewDebug.IntToString;

import android.view.ViewGroup;

import android.view.animation.LinearInterpolator;

import android.view.animation.RotateAnimation;

import android.widget.AbsListView;

import android.widget.AbsListView.OnScrollListener;

import android.widget.HorizontalScrollView;

import android.widget.ImageView;

import android.widget.LinearLayout;

import android.widget.ListView;

import android.widget.ProgressBar;

import android.widget.TextView;



import com.routemap_infomation.myfocus.R;

import com.routemap_infomation.myfocus.LineFocusActivity.RouteItem;

import com.routemap_infomation.utils.GestureJudgeUtils.OnGestureResult;



/**

 * 该类主要是完成 头部部分的功能封装

 * 

 * 一个可以监听ListView是否滚动到最顶部或最底部的自定义控件

 * 只能监听由触摸产生的,如果是ListView本身Flying导致的,则不能监听</br> 如果加以改进,可以实现监听scroll滚动的具体位置等

 * 

 * @author*/



public class ScrollOverListView extends ListView implements OnScrollListener {

    public static boolean canRefleash = true;



    private static final String TAG = "listview";

    

    /**松开更新**/

    private static final  int RELEASE_TO_REFRESH = 0;

    /**下拉更新**/

    private static final  int PULL_TO_REFRESH = 1;

    /**更新中**/

    private static final  int REFRESHING = 2;

    /**无**/

    private static final  int DONE = 3;

    /**加载中**/

    private static final  int LOADING = 4;

    /**实际的padding的距离与界面上偏移距离的比例**/

    private static final int RATIO = 3;

    

    /**是否要使用下拉刷新功能**/

    public boolean showRefresh = true;

    public boolean isDelete;

    public void setIsDelete(boolean isDelete){

        this.isDelete = isDelete;

    }

    

    private int mLastY;

    

    private int mBottomPosition;



    private SlideView mFocusedItemView;

    private LayoutInflater inflater;

    /**头部刷新的布局**/

    private LinearLayout headView;

    /**头部显示下拉刷新等的控件**/

    private TextView tipsTextview;

    /**刷新控件**/

    private TextView lastUpdatedTextView;

    /**箭头图标**/

    private ImageView arrowImageView;

    /**头部滚动条**/

    private ProgressBar progressBar;

    /**显示动画**/

    private RotateAnimation animation;

    /**头部回退显示动画**/

    private RotateAnimation reverseAnimation;

    /** 用于保证startY的值在一个完整的touch事件中只被记录一次**/

    private boolean isRecored;

    /**头部高度**/

    private int headContentHeight;

    /**开始的Y坐标**/

    private int startY;

    /**第一个item**/

    private int firstItemIndex;

    /**状态**/

    private int state;

    

    private boolean isBack;

    private GestureDetector gestureDetector;

    /** 空的 */

    private OnScrollOverListener mOnScrollOverListener = new OnScrollOverListener() {



        @Override

        public boolean onListViewTopAndPullDown(int delta) {

            return false;

        }



        @Override

        public boolean onListViewBottomAndPullUp(int delta) {

            return false;

        }



        @Override

        public boolean onMotionDown(MotionEvent ev) {

            return false;

        }



        @Override

        public boolean onMotionMove(MotionEvent ev, int delta) {

            return false;

        }



        @Override

        public boolean onMotionUp(MotionEvent ev) {

            return false;

        }

    };





    public ScrollOverListView(Context context, AttributeSet attrs, int defStyle) {

        super(context, attrs, defStyle);

        init(context);

    }



    public ScrollOverListView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(context);

    }



    public ScrollOverListView(Context context, PullDownView pullDownView) {

        super(context);

        isDelete = pullDownView.getIsDelete();

        init(context);

    }



    /**出事化控件**/

    private void init(Context context) {

        mBottomPosition = 0;

        setCacheColorHint(0);

        inflater = LayoutInflater.from(context);



        headView = (LinearLayout) inflater.inflate(R.layout.pull_down_head,

                null);

        arrowImageView = (ImageView) headView

                .findViewById(R.id.head_arrowImageView);

        arrowImageView.setMinimumWidth(70);

        arrowImageView.setMinimumHeight(50);

        progressBar = (ProgressBar) headView

                .findViewById(R.id.head_progressBar);

        tipsTextview = (TextView) headView.findViewById(R.id.head_tipsTextView);

        lastUpdatedTextView = (TextView) headView

                .findViewById(R.id.head_lastUpdatedTextView);



        measureView(headView);



        headContentHeight = headView.getMeasuredHeight();



        headView.setPadding(0, -1 * headContentHeight, 0, 0);

        headView.invalidate();



        /**列表添加头部**/

        addHeaderView(headView, null, false);



        setOnScrollListener(this);



        animation = new RotateAnimation(0, -180,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f);

        animation.setInterpolator(new LinearInterpolator());

        animation.setDuration(250);

        animation.setFillAfter(true);

        



        reverseAnimation = new RotateAnimation(-180, 0,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f,

                RotateAnimation.RELATIVE_TO_SELF, 0.5f);

        

        reverseAnimation.setInterpolator(new LinearInterpolator());

        reverseAnimation.setDuration(200);

        reverseAnimation.setFillAfter(true);

        

        state = DONE;

        gestureDetector=new GestureJudgeUtils(PublicDefine.context,new GestureJudgeUtils.OnGestureResult() {

            

            @Override

            public void onGestureResult(int direction) {

                // TODO Auto-generated method stub

                switch (direction) {

                case GestureJudgeUtils.GESTURE_LEFT:

                case GestureJudgeUtils.GESTURE_RIGHT:

                    PublicDefine.isDeleteTag=true;

                    

                    break;

                case GestureJudgeUtils.GESTURE_DOWN:

                case GestureJudgeUtils.GESTURE_UP:

                        PublicDefine.isDeleteTag=false;

                default:

                    break;

                }

            }

        }).Buile();

                

    }

    int currentx=0;

    int currenty=0;

    /**触摸事件的处理**/

    @Override

    public boolean onTouchEvent(MotionEvent ev) {

        final int action = ev.getAction();



        final int y = (int) ev.getRawY();

        final int deltax=(int)ev.getRawX();

        if (action==MotionEvent.ACTION_DOWN&&PublicDefine.isDeleteTag) {

            mFocusedItemView.move();

            PublicDefine.isDeleteTag=false;

            

            return super.onTouchEvent(ev);

        }

        if (action==MotionEvent.ACTION_DOWN) {

            currentx=(int) ev.getRawX();

            currenty=(int) ev.getRawY();

        }

        

        PublicDefine.isDeleteTag=Horizontal(currentx,currenty,deltax,y);

        

            

        cancelLongPress();

        if(PublicDefine.isDeleteTag){

            switch (action) {

            case MotionEvent.ACTION_DOWN: {

                int x = (int) ev.getX();

                int z = (int) ev.getY();

                int position = pointToPosition(x, z);

                Log.e(TAG, "postion=" + position);

                if (position != INVALID_POSITION) {

                    RouteItem data = (RouteItem) getItemAtPosition(position);

                    mFocusedItemView = data.slideView;

                    Log.e(TAG, "FocusedItemView=" + mFocusedItemView);

                }

            }

            default:

                break;

            }



            if (mFocusedItemView != null) {

                  return mFocusedItemView.onRequireTouchEvent(ev);

            }



            return super.onTouchEvent(ev);

        }else{

            switch (action) {

            case MotionEvent.ACTION_DOWN:   //按下的时候

                if (firstItemIndex == 0 && !isRecored) {

                    isRecored = true;

                    startY = (int) ev.getY();

                    Log.v(TAG, "在down时候记录当前位置‘");

                }

                // ===========================

                mLastY = y;

                final boolean isHandled = mOnScrollOverListener.onMotionDown(ev);

                if (isHandled) {

                    mLastY = y;

                    return isHandled;

                }

                break;

            

    

            case MotionEvent.ACTION_MOVE:    //手指正在移动的时候

                int tempY = (int) ev.getY();

                if (showRefresh) {

                    

                    if (!isRecored && firstItemIndex == 0) {

                        Log.v(TAG, "在move时候记录下位置");

                        isRecored = true;

                        startY = tempY;

                    }

                    if (state != REFRESHING && isRecored && state != LOADING) {

    

                        // 保证在设置padding的过程中,当前的位置一直是在head,否则如果当列表超出屏幕的话,当在上推的时候,列表会同时进行滚动

    

                        // 可以松手去刷新了

                        if (state == RELEASE_TO_REFRESH) {

    

                            setSelection(0);

    

                            // 往上推了,推到了屏幕足够掩盖head的程度,但是还没有推到全部掩盖的地步

                            if (((tempY - startY) / RATIO < headContentHeight)

                                    && (tempY - startY) > 0) {

                                state = PULL_TO_REFRESH;

                                changeHeaderViewByState();

    

                            }else if (tempY - startY <= 0) {// 一下子推到顶了

                                state = DONE;

                                changeHeaderViewByState();

    

                                Log.v(TAG, "由松开刷新状态转变到done状态");

                            }

    //                        else {// 往下拉了,或者还没有上推到屏幕顶部掩盖head的地步

    //                            // 不用进行特别的操作,只用更新paddingTop的值就行了

    //                        }

                        }

                        // 还没有到达显示松开刷新的时候,DONE或者是PULL_To_REFRESH状态

                        if (state == PULL_TO_REFRESH) {

    

                            setSelection(0);

    

                            // 下拉到可以进入RELEASE_TO_REFRESH的状态

                            if ((tempY - startY) / RATIO >= headContentHeight) {

                                state = RELEASE_TO_REFRESH;

                                isBack = true;

                                changeHeaderViewByState();

    

                                Log.v(TAG, "由done或者下拉刷新状态转变到松开刷新");

                            }else if (tempY - startY <= 0) {

                                // 上推到顶了

                                state = DONE;

                                changeHeaderViewByState();

    

                                Log.v(TAG, "由DOne或者下拉刷新状态转变到done状态");

                            }

                        }

    

                        // done状态下

                        if (state == DONE) {

                            if (tempY - startY > 0) {

                                state = PULL_TO_REFRESH;

                                changeHeaderViewByState();

                            }

                        }

    

                        // 更新headView的size

                        if (state == PULL_TO_REFRESH) {

                            headView.setPadding(0, -1 * headContentHeight

                                    + (tempY - startY) / RATIO, 0, 0);

                        }

    

                        // 更新headView的paddingTop

                        if (state == RELEASE_TO_REFRESH) {

                            headView.setPadding(0, (tempY - startY) / RATIO

                                    - headContentHeight, 0, 0);

                        }

    

                    }

                }

                // ==============================================

                final int childCount = getChildCount();

                if (childCount == 0){

                    return super.onTouchEvent(ev);

                }

                final int itemCount = getAdapter().getCount() - mBottomPosition;

                final int deltaY = y - mLastY;

                final int lastBottom = getChildAt(childCount - 1).getBottom();

                final int end = getHeight() - getPaddingBottom();

    

                final int firstVisiblePosition = getFirstVisiblePosition();

    

                final boolean isHandleMotionMove = mOnScrollOverListener

                        .onMotionMove(ev, deltaY);

    

                if (isHandleMotionMove) {

                    mLastY = y;

                    return true;

                }

    

                /** 到达底部 *  到达底部的事件在另外一个类执行**/

                if (firstVisiblePosition + childCount >= itemCount

                        && lastBottom <= end && deltaY < 0) {

                    final boolean isHandleOnListViewBottomAndPullDown;

                    isHandleOnListViewBottomAndPullDown = mOnScrollOverListener

                            .onListViewBottomAndPullUp(deltaY);

                    if (isHandleOnListViewBottomAndPullDown) {

                        mLastY = y;

                        return true;

                    }

                }

                break;

            

    

            case MotionEvent.ACTION_UP:    //手指抬起来的时候

                if (state != REFRESHING && state != LOADING) {

    //                if (state == DONE) {

    //                    // 什么都不做

    //                }

                    if (state == PULL_TO_REFRESH) {

                        state = DONE;

                        changeHeaderViewByState();

                        Log.v(TAG, "由下拉刷新状态,到done状态");

                    }

    

                    if (state == RELEASE_TO_REFRESH) {

                        state = REFRESHING;

                        changeHeaderViewByState();

    

                        canRefleash = true;

    

                        Log.v(TAG, "由松开刷新状态,到done状态");

                    }

                }

    

                isRecored = false;

                isBack = false;

    

                // /======================

                final boolean isHandlerMotionUp = mOnScrollOverListener

                        .onMotionUp(ev);

                if (isHandlerMotionUp) {

                    mLastY = y;

                    return true;

                }

                break;

            

            default:

                break;

            }

            mLastY = y;

            

            return super.onTouchEvent(ev);

        }

//        }catch(Exception e){

//            Log.i("test", e.getMessage());

//            return true;

//            

//        }

    }

    

    private boolean Horizontal(int dx, int dy,int dx1,int dy1) {

        // TODO Auto-generated method stub

          int deltax=dx-dx1;

          int deltay=dy-dy1;

           if (Math.abs(deltax)>=Math.abs(deltay)) {

            return true;

                    

        }else {

        return false;    

        }



    }



    // =============================== public method

    /**

     * 可以自定义其中一个条目为头部,头部触发的事件将以这个为准,默认为第一个

     * 

     * @param index  正数第几个,必须在条目数范围之内

     */

    public void setTopPosition(int index) {

        if (getAdapter() == null){

            throw new NullPointerException(

                    "You must set adapter before setTopPosition!");

        }

        if (index < 0){

            throw new IllegalArgumentException("Top position must > 0");

        }

    }



    /**

     * 可以自定义其中一个条目为尾部,尾部触发的事件将以这个为准,默认为最后一个

     * 

     * @param index  倒数第几个,必须在条目数范围之内

     */

    public void setBottomPosition(int index) {

        if (getAdapter() == null){

            throw new NullPointerException(

                    "You must set adapter before setBottonPosition!");

        }

        if (index < 0){

            throw new IllegalArgumentException("Bottom position must > 0");

        }



        mBottomPosition = index;

    }



    /**

     * 设置这个Listener可以监听是否到达顶端,或者是否到达低端等事件</br>

     * 

     * @see OnScrollOverListener

     */

    public void setOnScrollOverListener(

            OnScrollOverListener onScrollOverListener) {

        mOnScrollOverListener = onScrollOverListener;

    }

    

    /**

     * 滚动监听接口

     * 

     * @see ScrollOverListView#setOnScrollOverListener(OnScrollOverListener)

     * 

     */

    public interface OnScrollOverListener {

        /**

         * 到达最顶部触发

         * 

         * @param delta

         *            手指点击移动产生的偏移量

         * @return

         */

        boolean onListViewTopAndPullDown(int delta);







        /**

         * 到达最底部触发

         * 

         * @param delta

         *            手指点击移动产生的偏移量

         * @return

         */

        boolean onListViewBottomAndPullUp(int delta);



        /**

         * 手指触摸按下触发,相当于{@link MotionEvent#ACTION_DOWN}

         * 

         * @return 返回true表示自己处理

         * @see View#onTouchEvent(MotionEvent)

         */

        boolean onMotionDown(MotionEvent ev);



        /**

         * 手指触摸移动触发,相当于{@link MotionEvent#ACTION_MOVE}

         * 

         * @return 返回true表示自己处理

         * @see View#onTouchEvent(MotionEvent)

         */

        boolean onMotionMove(MotionEvent ev, int delta);



        /**

         * 手指触摸后提起触发,相当于{@link MotionEvent#ACTION_UP}

         * 

         * @return 返回true表示自己处理

         * @see View#onTouchEvent(MotionEvent)

         */

        boolean onMotionUp(MotionEvent ev);

    }



    

    @Override

    public void onScroll(AbsListView arg0, int firstVisiableItem, int arg2,

            int arg3) {

        firstItemIndex = firstVisiableItem;

        

    }



    @Override

    public void onScrollStateChanged(AbsListView view, int scrollState) {



    }



    // 此方法直接照搬自网络上的一个下拉刷新的demo,此处是“估计”headView的width以及height

    private void measureView(View child) {

        ViewGroup.LayoutParams p = child.getLayoutParams();

        if (p == null) {

            p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,

                    ViewGroup.LayoutParams.WRAP_CONTENT);

        }

        int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0 + 0, p.width);

        int lpHeight = p.height;

        int childHeightSpec;

        if (lpHeight > 0) {

            childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight,

                    MeasureSpec.EXACTLY);

        } else {

            childHeightSpec = MeasureSpec.makeMeasureSpec(0,

                    MeasureSpec.UNSPECIFIED);

        }

        child.measure(childWidthSpec, childHeightSpec);

    }

    



    public void onRefreshComplete() {

        state = DONE;

        lastUpdatedTextView.setText("最近更新:" + new Date().toLocaleString());

        changeHeaderViewByState();

    }



    // 当状态改变时候,调用该方法,以更新界面

    private void changeHeaderViewByState() {

        switch (state) {

        case RELEASE_TO_REFRESH:

            arrowImageView.setVisibility(View.VISIBLE);

            progressBar.setVisibility(View.GONE);

            tipsTextview.setVisibility(View.VISIBLE);

            lastUpdatedTextView.setVisibility(View.VISIBLE);



            arrowImageView.clearAnimation();

            arrowImageView.startAnimation(animation);



            tipsTextview.setText("松开刷新");



            Log.v(TAG, "当前状态,松开刷新");

            break;

        case PULL_TO_REFRESH:

            progressBar.setVisibility(View.GONE);

            tipsTextview.setVisibility(View.VISIBLE);

            lastUpdatedTextView.setVisibility(View.VISIBLE);

            arrowImageView.clearAnimation();

            arrowImageView.setVisibility(View.VISIBLE);

            // 是由RELEASE_To_REFRESH状态转变来的

            if (isBack) {

                isBack = false;

                arrowImageView.clearAnimation();

                arrowImageView.startAnimation(reverseAnimation);



                tipsTextview.setText("下拉刷新");

            } else {

                tipsTextview.setText("下拉刷新");

            }

            Log.v(TAG, "当前状态,下拉刷新");

            break;



        case REFRESHING:



            headView.setPadding(0, 0, 0, 0);

            progressBar.setVisibility(View.VISIBLE);

            arrowImageView.clearAnimation();

            arrowImageView.setVisibility(View.GONE);

            tipsTextview.setText("正在刷新...");

            lastUpdatedTextView.setVisibility(View.VISIBLE);



            Log.v(TAG, "当前状态,正在刷新...");

            break;

        case DONE:

            headView.setPadding(0, -1 * headContentHeight, 0, 0);

            progressBar.setVisibility(View.GONE);

            arrowImageView.clearAnimation();

            arrowImageView.setImageResource(R.drawable.pull_down_arrow);

            tipsTextview.setText("下拉刷新");

            lastUpdatedTextView.setVisibility(View.VISIBLE);



            Log.v(TAG, "当前状态,done");

            break;

        default:

            break;

        }

    }

    

    public void shrinkListItem(int position) {

        View item = getChildAt(position);



        if (item != null) {

            try {

                ((SlideView) item).shrink();

            } catch (ClassCastException e) {

                e.printStackTrace();

            }

        }

    }

}

  ①由于这个类继承与listview,因此具有了listview常见属性与方法。

  ②由于他要记录上拉下拉刷新的方式,因此,我们这里需要用定义一些变量来保存,并且要记录他们的状态。  

  ③上拉刷新的状态是需要监听的了,怎么进行监听了,这里依照java中插件机制,依照相应的观察者(observer)模式,需要定义一个接口来监听了。

  ④为了实现动态,这里需要定义不同参数的构造函数了。

  ⑤这里是一个重点,我们定义了一个变量,接上面的论证,我们这里定义了一个变量来确定到底是调用那个触摸方法。

  ⑥一般在android中,init初始化,找到相应控件,数据加载,是一个司空见惯的方法,在这里了,我们还对相应的手势的方法进行了监听,但是后来发现在此情景下,主要是由于touch事件中,无法对手势进行监听。

  ⑦onTouchEvent事件,触摸事件,是这一切事件的重中之重,我这里首先要对其手势方向进行了监听,上文提到无法直接对手势的方向进行监听了,因此我这里粗略进行进行了判断,判断触摸按下与触摸抬起的时候,两点x轴之间的距离与两点y之间的距离孰大孰小。来判断他是竖向滚动,还是横向拖拽。倘若是横向的拖拽,进把触摸事件下放到slideview控件,由她将触摸事件进行处理,否则的话,就由此控件监听上拉下滑的刷新事件。

  ⑧与上节一样,对其滑动以后,也要对其控件的距离,控件位置进行调整,进行计算。

  ⑨这个控件本质是个列表控件,本质是对数据进行处理,因此,我们这里需要一个方法对其数据加以处理的。

  以上的内容就把slideview整合到项目中去了,其最终的效果是这样的:

  手把手实现腾讯qq拖拽删去效果(二)

  以上代码下载地址:

你可能感兴趣的:(qq)