仿二手车之家的竖直拖动栏自定义View

大家好,本篇博客带来的一个效果是仿二手车之家的竖直拖动蓝的一个效果,具体如下:这里写图片描述

为什么会选择写一个这样的效果呢?是因为这里牵涉到了两个知识点,一个是最近正在学习的事件分发,还有一个是ViewDragHelper这个拖拽的工具类。

首先 我们先实现这个一个拖拽的效果:
这里写图片描述

下面的红色的区域块,可以在竖直方向上拖动,当区域仅限于后面颜色的高度,话不多说,直接上代码:

/**
 * Created by DELL on 2017/9/12.
 * Description :
 */

public class VerticalDragView extends FrameLayout {

    private ViewDragHelper mDragHelper;
    private View mFirstView;
    private View mDragView;
    private int mFirstViewHeight;

    public VerticalDragView(@NonNull Context context) {
        this(context,null);
    }

    public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper = ViewDragHelper.create(this,mCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        int childCount = getChildCount();
        if(childCount != 2){
            throw new RuntimeException("子Vview必须为两个view");
        }
        mFirstView = getChildAt(0);
        mDragView = getChildAt(1);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //获取第一个子View的高度   方便后面判断只能在搞高度范围内被拖动
        mFirstViewHeight = mFirstView.getMeasuredHeight();
        Log.i("JUSTH","firstViewHeight=="+mFirstViewHeight);
    }

    //创建ViewDragHelper的callBack对象
    private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
        /**
         * 返回true 代表能拖到   返回false  代表不能拖动
         * @param child
         * @param pointerId
         * @return
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //代表只有在拖动的view是第二个view的时候,才允许拖动
            return child == mDragView;
        }

        //当触摸释放时  会执行该方法

        /**
         * 当拖动距离大于firstView的一半高度是  打开  否则  关闭
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if(mDragView.getTop() > mFirstViewHeight/2){
                mDragHelper.settleCapturedViewAt(0,mFirstViewHeight);
            }else if(mDragView.getTop() <= mFirstViewHeight/2){
                mDragHelper.settleCapturedViewAt(0,0);
            }
            invalidate();
        }

        //管理竖直方向上是否能被拖动 以及被拖动的范围
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if(top < 0){
                top = 0;
            }
            if(top>mFirstViewHeight){
                top = mFirstViewHeight;
            }
            return top;
        }
    };

    //相应drawerHelper中的释放滚动事件
    @Override
    public void computeScroll() {
        if(mDragHelper.continueSettling(true)){
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }
}

好了,到这里,拖拽的效果就已经写完了!

下面,我们接着看,看二手车之家的效果,可以拖拽的部分,应该是一个类似ListView,RecycleView这样的一个效果,那我们们这里就有listView来代替吧,只要实现了事件的拦截与分发,不管使用哪种控件,都是一样的:

/**
 * Created by DELL on 2017/9/12.
 * Description :
 */

public class VerticalDragView extends FrameLayout {

    private ViewDragHelper mDragHelper;
    private View mFirstView;
    private View mDragView;
    private int mFirstViewHeight;
    private boolean isFirstViewOpen = false;

    public VerticalDragView(@NonNull Context context) {
        this(context,null);
    }

    public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public VerticalDragView(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper = ViewDragHelper.create(this,mCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();

        int childCount = getChildCount();
        if(childCount != 2){
            throw new RuntimeException("子Vview必须为两个view");
        }
        mFirstView = getChildAt(0);
        mDragView = getChildAt(1);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        //获取第一个子View的高度   方便后面判断只能在搞高度范围内被拖动
        mFirstViewHeight = mFirstView.getMeasuredHeight();
        Log.i("JUSTH","firstViewHeight=="+mFirstViewHeight);
    }

    //创建ViewDragHelper的callBack对象
    private ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {
        /**
         * 返回true 代表能拖到   返回false  代表不能拖动
         * @param child
         * @param pointerId
         * @return
         */
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //代表只有在拖动的view是第二个view的时候,才允许拖动
            return child == mDragView;
        }

        //当触摸释放时  会执行该方法

        /**
         * 当拖动距离大于firstView的一半高度是  打开  否则  关闭
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if(mDragView.getTop() > mFirstViewHeight/2){
                mDragHelper.settleCapturedViewAt(0,mFirstViewHeight);
                isFirstViewOpen = true;
            }else if(mDragView.getTop() <= mFirstViewHeight/2){
                mDragHelper.settleCapturedViewAt(0,0);
                isFirstViewOpen = false;
            }
            invalidate();
        }

        //管理竖直方向上是否能被拖动 以及被拖动的范围
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if(top < 0){
                top = 0;
            }
            if(top>mFirstViewHeight){
                top = mFirstViewHeight;
            }
            return top;
        }
    };

    //相应drawerHelper中的释放滚动事件
    @Override
    public void computeScroll() {
        if(mDragHelper.continueSettling(true)){
            invalidate();
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        mDragHelper.processTouchEvent(event);
        return true;
    }

    int downY;
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //当第一个view打开的时候  拦截事件
        if(isFirstViewOpen){
            return true;
        }
        //当listView下拉的时候,当其可见的最上面一个条目是第一条 且该listview在最上端的时候 拦截事件
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                downY = (int) ev.getY();
                mDragHelper.processTouchEvent(ev);
                break;
            case MotionEvent.ACTION_MOVE:
                if((ev.getY() - downY)>0 && !canChildScrollUp()){
                    return true;
                }
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    private boolean isFirst(){
        ListView listView = (ListView) mDragView;
        return listView.getFirstVisiblePosition() == 0;
    }

    public boolean canChildScrollUp() {
        if (android.os.Build.VERSION.SDK_INT < 14) {
            if (mDragView instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) mDragView;
                return absListView.getChildCount() > 0
                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                        .getTop() < absListView.getPaddingTop());
            } else {
                return ViewCompat.canScrollVertically(mDragView, -1) || mDragView.getScrollY() > 0;
            }
        } else {
            return ViewCompat.canScrollVertically(mDragView, -1);
        }
    }
}

效果如下:(虽然可能是丑了点,但毕竟效果是有了)
这里写图片描述

好了,今天就到这里吧!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值