自定义ViewGroup——实现VerticalDragLayout

纯粹是为了自己学习的过程,demo很简单。
实现效果:
在这里插入图片描述
分析:

  • 首先这个布局是一个包含两个子View的ViewGroup,第二个子View是一个可以展示列表的View,比如ListView或者RecyclerView。VerticalDragLayout这个ViewGroup可以继承FrameLayout,这样默认第二个会显示在第一个上面。
  • 列表是可以被拖拽的,那么什么时候可以拖拽?两种情况:
    a、手指下滑,并且列表(RecyclerView或ListView)已经滑到顶部了,这时如果手指继续在屏幕上往下滑,列表就会被往下拖拽,第一个View就会显示出来。
    b、当我们手指上滑,并且列表的top大于0时。这时如果手指继续在屏幕上往上滑,列表就会被往上拖拽,第一个View会被逐渐隐藏。
  • 想要实现拖拽,需要借助ViewDragHelper这个类,并且只有第二个View也就是列表View可以被拖拽,被拖拽的方向是垂直方向,被拖拽的移动范围就是0到第一个View的高度。
  • VerticalDragLayout需要重写onInterceptTouchEvent方法,根据可以被拖拽的情况,拦截触摸事件,交给VerticalDragLayout自己的onTouchEvent方法,在不该被拖拽的情况下,将事件分发给子View,让子View自己处理。
  • 当我们的手指抬起时,如果第二个View被拖拽的距离大于头布局(第一个View)的一半就自动滚动到第一View高度处,否则滚回0处。

在布局中使用

<?xml version="1.0" encoding="utf-8"?>
<study.project.mao.view1.VerticalDragLayoutDemo.VerticalDragLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <TextView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:text="Hello"
        android:textSize="30sp"
        android:gravity="center"
        android:background="@color/colorAccent"
        />

    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        />
</study.project.mao.view1.VerticalDragLayoutDemo.VerticalDragLayout>

VerticalDragLayout的实现代码
VerticalDragLayout的实现很简单,主要的问题就是处理好事件的分发和拦截时机,直接贴代码。

/**
 * 创建者: mao
 * 功能描述:事件拦截demo
 */

public class VerticalDragLayout extends FrameLayout {

    private ViewDragHelper mDragHelper;

    private int mHeaderHeight;
    private View mAbsListView;

    private float mLastY;

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

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

    public VerticalDragLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mDragHelper=ViewDragHelper.create(this,0.1f,viewDragCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        int childCount=getChildCount();
        if (childCount!=2){
            throw new RuntimeException("VerticalDragLayout can only two children but you gave "+childCount);
        }
        //获取到第二个View,这个View是需要被拖拽的View
        mAbsListView= getChildAt(1);
    }

    /**
     * 获取头布局的高度
     */
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        View headerView=getChildAt(0);
        mHeaderHeight=headerView.getMeasuredHeight();
    }

    /**
     * View.canScrollVertically(1) 返回true表示还能下滑
     * View.canScrollVertically(-1) 返回true表示还能上滑
     *
     * @param ev
     * @return true 拦截事件 自己处理 ;false 不拦截 给子View处理
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        float currY= (float) ev.getY();
        if (ev.getAction()==MotionEvent.ACTION_DOWN){
            mDragHelper.processTouchEvent(ev);
            mLastY=currY;
        }
        float y=currY-mLastY;
        mLastY=currY;
        //拦截的情况
        //1、手指下滑,并且列表(RecyclerView或ListView)已经滑到顶部了
        //2、手指上滑,并且mAbsListView的top大于0
        if (y>0){
            if (mAbsListView.getTop()==0&&!mAbsListView.canScrollVertically(-1)){
                return true;
            }
        }else {
            if (mAbsListView.getTop()>0){
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_MOVE:
            case MotionEvent.ACTION_UP:
                mDragHelper.processTouchEvent(event);
                break;
        }
        return true;
    }

    @Override
    public void computeScroll()
    {
        if(mDragHelper.continueSettling(true))
        {
            invalidate();
        }
    }

    private ViewDragHelper.Callback  viewDragCallback=new ViewDragHelper.Callback() {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //只允许mAbsListView可以拽拽
            return mAbsListView==child;
        }

        /**
         *
         * @param child
         * @param top 垂直方向拖拽移动的距离
         * @param dy
         * @return
         */
        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            if (top<0){
                top=0;
            }
            if (top>mHeaderHeight){
                top=mHeaderHeight;
            }
            return top;
        }

        /**
         * 手指松开的时候调用
         * 如果手指松开时 mAbsListView的拖拽距离大于头布局的一半就自动滚动到top=mHeaderHeight处,
         * 否则滚动到top=0处
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            if (mAbsListView == releasedChild){
                int top=mAbsListView.getTop();
                if (top>mHeaderHeight/2){
                    mDragHelper.settleCapturedViewAt(0, mHeaderHeight);
                    invalidate();
                }else {
                    mDragHelper.settleCapturedViewAt(0, 0);
                    invalidate();
                }
            }
        }
    };
}

学习资料:
ViewDragHelper实战 自己打造Drawerlayout
源码阅读分析 - View的Touch事件分发

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
DragLinearLayout是一个LinearLayout, 他可是其子View在其范围内可拖动、可交换位置。默认情况下,子View是不可拖动的,你需要调用DragLinearLayout.setViewDraggable(child, child)方法让其可拖动。项目地址:https://github.com/justasm/DragLinearLayout 效果图:如何使用和使用LinearLayout一样:                        2. 让子View可拖动默认情况下子View是不可拖动的,你需要调用dragLinearLayout.setViewDraggable()让子View变为可拖动的。DragLinearLayout dragLinearLayout = (DragLinearLayout) findViewById(R.id.container); //让子view可拖动,默认是不能拖动的         for(int i = 1; i < dragLinearLayout.getChildCount(); i ){             View child = dragLinearLayout.getChildAt(i);             dragLinearLayout.setViewDraggable(child, child);          }可以动态添加可拖动子viewfinal View view = View.inflate(context, R.layout.view_layout, null); dragLinearLayout.addDragView(viewview.findViewById(R.id.view_drag_handle));   // ..   dragLinearLayout.removeDragView(view);使用OnViewSwapListener检测子view之间的排序变化事件:dragLinearLayout.setOnViewSwapListener(new DragLinearLayout.OnViewSwapListener() {     @Override     public void onSwap(View firstView, int firstPosition,             View secondView, int secondPosition) {         // update data, etc..     } });当在ScrollView中使用DragLinearLayout的时候,如果你想在拖拽的时候ScrollView也能滚动,需要调用setContainerScrollView(Scroll
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值