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

简介:

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

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

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


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整合到项目中去了,其最终的效果是这样的:

  

  以上代码下载地址:

目录
相关文章
|
8月前
|
前端开发
仿qq音乐官网部分静态页面
仿qq音乐官网部分静态页面
68 0
|
11月前
|
小程序
仿QQ聊天室【方案】
仿QQ聊天室【方案】
102 0
在文章中添加网易云音乐
在文章中添加网易云音乐
112 0
|
搜索推荐 小程序 数据挖掘
仿今日头条实时新闻微信小程序项目源码
仿今日头条实时新闻微信小程序项目源码
330 0
仿今日头条实时新闻微信小程序项目源码
|
JavaScript 前端开发
仿网易云项目笔记
仿网易云项目笔记
126 0
|
存储 JavaScript Linux
网易云音乐
网易云音乐
1421 0
|
安全 JavaScript
微信公众号-- 微信分享功能(分享到朋友和朋友圈显示图片和简介)
页面设置一个隐藏的图片,宽高都是300像素,微信就会抓取这张图片做为分享图片
15459 0