Android 自定义滑动容器View(2)

来源:互联网 发布:深圳壹基金知乎 编辑:程序博客网 时间:2024/06/07 03:59

书接上回,上次我们实现了一个能滑动的view ,这次我们来改造下,允许他快速滑动的时候 可以 花很多个
快速向上向下滑动 这个 明显是个 “手势”,然而android 提供了一个 很爽的 辅助类来处理这种手势

android.view.GestureDetector

配合 scroller使用 简直无敌
GestureDetector 使用需要 配合 GestureDetector.OnGestureListener 来处理各种手势操作
使用方法
1. 初始化 GestureDetector
2. 实现 GestureDetector.OnGestureListener
3. 在 View 的onTouchEvent( 当然 也可以是 dispatchTouchEvent,这个看你需要) 方法中调用 GestureDetector.onTouchEvent 方法来分发事件
4. 在需要的处理的手势动作方法上实现对应的方法,比如调用scroller 去滑动

基本上,从GestureDetector.OnGestureListener 接口方法名都可以知道这个接口就是通过 GestureDetector 来处理 touch event 然后分发到 OnGestureListener 的各个方法。

下面来改造下之前的 滑动View
之前我们 都在 dispatchTouchEvent 里面判断event类型 ,然后手工计算 滑动的距离,然后调用scroll去滑动,这些通过 GestureDetector 现在把事件分发给 OnGestureListener 了,所以我们现在在这个OnGestureListener 的对应 的方法里面实现,刚好 OnGestureListener 里面就有个onScroll方法,这个特么的还帮你算好距离,爽!!!

根据GestureDetector 使用方式,把我们之前的View 实现 OnGestureListener ,重写里面 dispathTouchEvent 和实现onScroll 方法

    int mScrollableHeight = 0;    protected void init() {        mScroller = new Scroller(getContext());        mGestureDetector = new GestureDetector(getContext(), this);    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        .....省略布局代码        //计算出可滑动的距离,因为 算到是Y坐标 所以 要减去 当前view的height 来算出 允许垂直滑动的距离        mScrollableHeight = (layoutBottom - getHeight());    }    /**     * 获取允许滑动的距离     * @param y     * @return     */    private int getScrollableHeight(int y) {        return Math.max(0, Math.min(mScrollableHeight, y));    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        //分发事件到 GestureDetector        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);    }    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {        //把之前在dispatchTouchEvent里面的逻辑搬到这里来        int tx = (int) (getScrollX() + distanceX);        int ty = (int) (getScrollY() + distanceY);        //计算可滑动距离        ty = getScrollableHeight(ty);        scrollTo(0, ty);        return false;    }

好了,这些改造完滑动的了。

下面我们来改动他可以快速滑动 一次滑动多个 ,这里面用到 fling 这个手势,也就是说 在重写 onFling 的方法即可

     @Override    public boolean onDown(MotionEvent e) {        //按下的时候 停止 滑动        mScroller.abortAnimation();        return false;    }    @Override    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {        if (Math.abs(velocityY) > 200) {            //调用快速滑动            mScroller.fling(getScrollX(), getScrollY(), (int) -velocityX, (int) -velocityY, 0, 0, 0, mScrollableHeight);        }        return false;    }

啊?mScroller.fling 是什么鬼?这个是 Scroller 专门提供出来方便这个使用的方法,直接使用他的方法即可,而且 在onDown里面 把滑动停下,因为如果不做这个 在滑动的时候,必须等待到他滑动完毕才能下次再滑,所以这里就把他手工停掉。

好了 下面放出完整代码

/** * 滑动 */public class MyScrollerView extends ViewGroup implements GestureDetector.OnGestureListener {    GestureDetector mGestureDetector;    Scroller mScroller;    int mScrollableHeight = 0;//允许滑动最大距离    public MyScrollerView(Context context) {        super(context);        init();    }    public MyScrollerView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        init();    }    public MyScrollerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);        init();    }    protected void init() {        mScroller = new Scroller(getContext());        mGestureDetector = new GestureDetector(getContext(), this);    }    @Override    public void computeScroll() {        if (mScroller.computeScrollOffset()) {//判断是否有滑动过            int y = getScrollableHeight(mScroller.getCurrY());            scrollTo(mScroller.getCurrX(), y);            postInvalidate();//重画        }        super.computeScroll();    }    public void smoothScrollTo(int y) {        //TODO 检测y的边界        mScroller.abortAnimation();//标志上次 的滑动结束        mScroller.startScroll(getScrollX(), getScrollY(), 0, getScrollableHeight(y));//移动到xy        postInvalidate();//让view重画    }    @Override    public boolean dispatchTouchEvent(MotionEvent event) {        return mGestureDetector.onTouchEvent(event) || super.dispatchTouchEvent(event);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            child.measure(widthMeasureSpec, 0);//child 的 height 必须是 wrapcontent,由于此view允许滑动,所以子view都要计算出来        }        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    /**     * 获取允许滑动的距离     * @param y     * @return     */    private int getScrollableHeight(int y) {        return Math.max(0, Math.min(mScrollableHeight, y));    }    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        //垂直布局        int layoutLeft = 0;        int layoutTop = 0;        int layoutRight = r;        int layoutBottom = 0;        int childCount = getChildCount();        for (int i = 0; i < childCount; i++) {            View child = getChildAt(i);            layoutBottom = layoutTop + child.getMeasuredHeight();            child.layout(layoutLeft, layoutTop, layoutRight, layoutBottom);            layoutTop = layoutBottom;        }        //计算出可滑动的距离,因为 算到是Y坐标 所以 要减去 当前view的height 来算出 允许垂直滑动的距离        mScrollableHeight = (layoutBottom - getHeight());    }    @Override    public boolean onDown(MotionEvent e) {        //按下的时候 停止 滑动        mScroller.abortAnimation();        return false;    }    @Override    public void onShowPress(MotionEvent e) {    }    @Override    public boolean onSingleTapUp(MotionEvent e) {        return false;    }    @Override    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {        int tx = (int) (getScrollX() + distanceX);        int ty = (int) (getScrollY() + distanceY);        //计算可滑动距离        ty = getScrollableHeight(ty);        scrollTo(0, ty);        return false;    }    @Override    public void onLongPress(MotionEvent e) {    }    @Override    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {        if (Math.abs(velocityY) > 200) {            //调用快速滑动            mScroller.fling(getScrollX(), getScrollY(), (int) -velocityX, (int) -velocityY, 0, 0, 0, mScrollableHeight);        }        return false;    }}

告一段落

1 0
原创粉丝点击