仿ios可上提下拉的ScrollView

来源:互联网 发布:ubuntu安装nodejs 编辑:程序博客网 时间:2024/05/24 16:15

新建仿ios可上提下拉ScrollView的Java类,直接在xml布局文件中包含所需控件即可

package com.zhansy.mytext;import android.content.Context;import android.graphics.Rect;import android.os.Handler;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;import android.view.animation.TranslateAnimation;import android.widget.ScrollView;//仿ios可上提下拉的ScrollViewpublic class MyScrollowView extends ScrollView {private static final String TAG = "ElasticScrollView";        //移动因子, 是一个百分比, 比如手指移动了100px, 那么View就只移动50px    //目的是达到一个延迟的效果    private static final float MOVE_FACTOR = 0.5f;         //松开手指后, 界面回到正常位置需要的动画时间    private static final int ANIM_TIME = 300;         //ScrollView的子View, 也是ScrollView的唯一一个子View    private View contentView;          //手指按下时的Y值, 用于在移动时计算移动距离    //如果按下时不能上拉和下拉, 会在手指移动时更新为当前手指的Y值    private float startY;         //用于记录正常的布局位置    private Rect originalRect = new Rect();         //手指按下时记录是否可以继续下拉    private boolean canPullDown = false;         //手指按下时记录是否可以继续上拉    private boolean canPullUp = false;         //在手指滑动的过程中记录是否移动了布局    private boolean isMoved = false;    //滚动监听    private OnScrollListener srollListenter;    /**      * 主要是用在用户手指离开MyScrollView,MyScrollView还在继续滑动,我们用来保存Y的距离,然后做比较      */      private int lastScrollY;     public MyScrollowView(Context context) {        super(context);    }         public MyScrollowView(Context context, AttributeSet attrs) {        super(context, attrs);    }     @Override    protected void onFinishInflate() {        if (getChildCount() > 0) {            contentView = getChildAt(0);        }    }         @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        super.onLayout(changed, l, t, r, b);                 if(contentView == null) return;         //ScrollView中的唯一子控件的位置信息, 这个位置信息在整个控件的生命周期中保持不变        originalRect.set(contentView.getLeft(), contentView.getTop(), contentView                .getRight(), contentView.getBottom());    }     //在触摸事件中, 处理上拉和下拉的逻辑    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {                 if (contentView == null) {            return super.dispatchTouchEvent(ev);        } //        int action = ev.getAction();//         //        switch (action) {//        case MotionEvent.ACTION_DOWN://             //            //判断是否可以上拉和下拉//            canPullDown = isCanPullDown();//            canPullUp = isCanPullUp();//             //            //记录按下时的Y值//            startY = ev.getY();//            break;//             //        case MotionEvent.ACTION_UP://             //            if(!isMoved) break;  //如果没有移动布局, 则跳过执行//             //            // 开启动画//            TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(),//                    originalRect.top);//            anim.setDuration(ANIM_TIME);//             //            contentView.startAnimation(anim);//             //            // 设置回到正常的布局位置//            contentView.layout(originalRect.left, originalRect.top, //                    originalRect.right, originalRect.bottom);//             //            //将标志位设回false//            canPullDown = false;//            canPullUp = false;//            isMoved = false;//             //            break;//        case MotionEvent.ACTION_MOVE://             //            //在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度//            if(!canPullDown && !canPullUp) {//                startY = ev.getY();//                canPullDown = isCanPullDown();//                canPullUp = isCanPullUp();//                 //                break;//            }//             //            //计算手指移动的距离//            float nowY = ev.getY();//            int deltaY = (int) (nowY - startY);//             //            //是否应该移动布局//            boolean shouldMove = //                    (canPullDown && deltaY > 0)    //可以下拉, 并且手指向下移动//                    || (canPullUp && deltaY< 0)    //可以上拉, 并且手指向上移动//                    || (canPullUp && canPullDown); //既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)//             //            if(shouldMove){//                //计算偏移量//                int offset = (int)(deltaY * MOVE_FACTOR);//                 //                //随着手指的移动而移动布局//                contentView.layout(originalRect.left, originalRect.top + offset,//                        originalRect.right, originalRect.bottom + offset);//                 //                isMoved = true;  //记录移动了布局//            }//             //            break;//        default://            break;//        }         return super.dispatchTouchEvent(ev);    }    /**      * 用于用户手指离开MyScrollView的时候获取MyScrollView滚动的Y距离,然后回调给onScroll方法中      */      private Handler handler = new Handler() {            public void handleMessage(android.os.Message msg) {              int scrollY = MyScrollowView.this.getScrollY();                            //此时的距离和记录下的距离不相等,在隔5毫秒给handler发送消息              if(lastScrollY != scrollY){                  lastScrollY = scrollY;                  handler.sendMessageDelayed(handler.obtainMessage(), 5);                }              if( srollListenter!= null){              srollListenter.onScroll(scrollY);              }                        };        };      @Overridepublic boolean onTouchEvent(MotionEvent ev) {// TODO Auto-generated method stub    //    lastScrollY = this.getScrollY();//    if(srollListenter!= null){  //    srollListenter.onScroll(lastScrollY = this.getScrollY());  //        }  //        switch(ev.getAction()){  //        case MotionEvent.ACTION_UP:  //             handler.sendMessageDelayed(handler.obtainMessage(), 5);    //            break;  //        }        int action = ev.getAction();                switch (action) {        case MotionEvent.ACTION_DOWN:                         //判断是否可以上拉和下拉            canPullDown = isCanPullDown();            canPullUp = isCanPullUp();                         //记录按下时的Y值            startY = ev.getY();            break;                     case MotionEvent.ACTION_UP:                         if(!isMoved) break;  //如果没有移动布局, 则跳过执行                         // 开启动画            TranslateAnimation anim = new TranslateAnimation(0, 0, contentView.getTop(),                    originalRect.top);            anim.setDuration(ANIM_TIME);                         contentView.startAnimation(anim);                         // 设置回到正常的布局位置            contentView.layout(originalRect.left, originalRect.top,                     originalRect.right, originalRect.bottom);                         //将标志位设回false            canPullDown = false;            canPullUp = false;            isMoved = false;                         break;        case MotionEvent.ACTION_MOVE:                         //在移动的过程中, 既没有滚动到可以上拉的程度, 也没有滚动到可以下拉的程度            if(!canPullDown && !canPullUp) {                startY = ev.getY();                canPullDown = isCanPullDown();                canPullUp = isCanPullUp();                break;            }            canPullDown = isCanPullDown();            canPullUp = isCanPullUp();            //计算手指移动的距离            float nowY = ev.getY();            int deltaY = (int) (nowY - startY);                         //是否应该移动布局            boolean shouldMove =                     (canPullDown && deltaY > 0)    //可以下拉, 并且手指向下移动                    || (canPullUp && deltaY< 0)    //可以上拉, 并且手指向上移动                    || (canPullUp && canPullDown); //既可以上拉也可以下拉(这种情况出现在ScrollView包裹的控件比ScrollView还小)                         if(shouldMove){                //计算偏移量                int offset = (int)(deltaY * MOVE_FACTOR);                                 //随着手指的移动而移动布局                contentView.layout(originalRect.left, originalRect.top + offset,                        originalRect.right, originalRect.bottom + offset);                                 isMoved = true;  //记录移动了布局            }                         break;        default:            break;        }return super.onTouchEvent(ev);}@Overrideprotected int computeVerticalScrollOffset() {// TODO Auto-generated method stubhandler.sendMessageDelayed(handler.obtainMessage(), 5);return super.computeVerticalScrollOffset();}//判断是否滚动到顶部    private boolean isCanPullDown() {        return getScrollY() == 0 ||                 contentView.getHeight() < getHeight() + getScrollY();    }         //判断是否滚动到底部    private boolean isCanPullUp() {        return  contentView.getHeight() <= getMeasuredHeight() + getScrollY();    }    public void setOnScrollListener(OnScrollListener onscrolllistener){    this.srollListenter = onscrolllistener;    }    /**     * 滚动的回调接口     * @author chc     *     */    public interface OnScrollListener{    /**          * 回调方法, 返回MyScrollView滑动的Y方向距离          * @param scrollY          *              、          */          public void onScroll(int scrollY);    }        }

xml文件:

<com.zhansy.mytext.MyScrollowView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:scrollbars="none">    <RelativeLayout        android:layout_width="match_parent"        android:layout_height="match_parent">        <TextView            android:id="@+id/tv"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_centerInParent="true"            android:text="@string/hello_world"            android:textSize="20sp" />    </RelativeLayout></com.zhansy.mytext.MyScrollowView>



0 0