android自定义控件Group之上下滑动回弹及快速滑动手势viewgroup

最近一周工作上比较闲,想来没有事做,,于是就想写个自定义控件巩固一下android的自定义控件开发,一开始的想法就是写一个简单的上下翻页的viewGroup,我们都知道viewpager是左右滑动的控件。

还是老规矩,先上效果图:

以上就是效果图,补充说一下实现的效果:实现子控件根据手势上下滑动翻页,慢速滑动过半时会翻页,快速滑动速度达到1000px/s时也会翻页,当然也要首页和尾页处理,首页和尾页部分不论向下或者向上翻动多少都会回弹;在事件分发上也有处理,当触摸或者滑动距离不会大于系统touchslop时就会当做点击事件来处理

到了上代码的时候了:

package com.xuganwen.testviewgroup2;

import android.content.Context;
import android.support.v4.view.ViewConfigurationCompat;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.widget.Scroller;

/**
 * 文件描述:上下滑动viewgroup
 * 作者:xuganwen
 * 创建时间:2019/5/8
 * 更改时间:2019/5/8
 * 版本号:1.0
 */
public class VerticalViewPager extends ViewGroup {

    private Scroller mScroller;
    private int childCount;
    private float downY;
    private int touchSlop;
    private int scrollY;
    private int position;

    private VelocityTracker velocityTracker;

    public VerticalViewPager(Context context) {
        super(context);
    }

    public VerticalViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
        mScroller = new Scroller(context);
        touchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(ViewConfiguration.get(context));
    }

    public VerticalViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            measureChild(getChildAt(i), widthMeasureSpec, heightMeasureSpec);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {

        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            child.layout(l, child.getMeasuredHeight() * i, r, child.getMeasuredHeight() * (i + 1));
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if(null==velocityTracker){
            velocityTracker=VelocityTracker.obtain();
        }
        velocityTracker.addMovement(event);
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                float moveY = event.getY();
                int deltaY = (int) (moveY - downY);
                scrollY = getScrollY();
                position = getScrollY() % getMeasuredHeight() > getMeasuredHeight() / 2
                        ? getScrollY() / getMeasuredHeight() + 1
                        : getScrollY() / getMeasuredHeight();
                if (position >= getChildCount()-1) {
                    position = getChildCount()-1;
                }else if(position<=0){
                    position=0;
                }
                scrollBy(0, -deltaY);
                downY = moveY;
                break;
            case MotionEvent.ACTION_UP:
                if(null!=velocityTracker){
                    velocityTracker.computeCurrentVelocity(1000);
                }
                if(Math.abs(velocityTracker.getYVelocity())>1000){
                    if(velocityTracker.getYVelocity()>1000){
                        mScroller.startScroll(0, getScrollY(), 0, (position==0?0:position-1) * getMeasuredHeight() - getScrollY());
                    }else if(velocityTracker.getYVelocity()<-1000){
                        mScroller.startScroll(0, getScrollY(), 0, (position==getChildCount()-1?getChildCount()-1:(position+1)) * getMeasuredHeight() - getScrollY());
                    }
                }else {
                    mScroller.startScroll(0, getScrollY(), 0, position * getMeasuredHeight() - getScrollY());
                }
                invalidate();
                release();
                break;
            case MotionEvent.ACTION_CANCEL:
                release();
                break;
        }
        return true;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                downY = ev.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if(Math.abs(ev.getY()-downY)>touchSlop){
                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return false;
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            //这里调用View的scrollTo() 完成实际的滚动
            scrollTo(0, mScroller.getCurrY());
            //必须调用该方法,否则不一定能看到滚动效果
            invalidate();
        }
        super.computeScroll();
    }


    public void release(){
        if(velocityTracker!=null){
            velocityTracker.clear();
            velocityTracker.recycle();
            velocityTracker=null;
        }
    }
}

代码如上,可以看得出来滑动效果是通过scroller滑动计算的距离来实现的,滑动速度处理方面是 使用的VelocityTracker来处理的。子控件的点击事件是通过onInterceptouchevent来处理的。

说实话我想了一下,我这个控件应该是支持放入布局类控件作为子控件的,然后使用framgment替换布局来实现放入fragment来实现fragment上下翻页界面效果的,不知道各位有什么看法没,可以提出来讨论讨论~

你可能感兴趣的:(移动开发)