自定义控件:ViewDragHelper(一) QQ5.0

思路

先看效果图
这里写图片描述

思路:
布局内放2个ViewGroup,leftView和mainView,leftView就是侧拉栏,mainView是主页面。这2个view都放到DragLayout中,leftView在下,mainView在上,当我们往右拖动mainView的时候,mainView使用缩放动画,leftView使用缩放+透明度+移动动画。

布局:

<?xml version="1.0" encoding="utf-8"?>
<com.cqc.viewdraghelper01.DragLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@mipmap/bg"
    tools:context="com.cqc.viewdraghelper01.MainActivity">


    <!--背景面板-->
    <LinearLayout
        android:id="@+id/leftView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_green_dark"
        android:orientation="vertical"
        android:paddingBottom="50dp"
        android:paddingLeft="10dp"
        android:paddingRight="50dp"
        android:paddingTop="50dp">

        <ListView
            android:id="@+id/listView_left"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

    <!--主面板-->
    <LinearLayout
        android:id="@+id/mainView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_orange_dark"
        android:orientation="vertical">

        <ListView
            android:id="@+id/listView_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>

</com.cqc.viewdraghelper01.DragLayout>

Step1:先实现滑动效果

这里写图片描述
创建类继承自FrameLayout,frameLayout已经实现了onMeasure()和onLayout(),这部分代码不需要我们写。

先获取width+height,和拖动范围,获取leftView和mainView。事件分发和事件处理都交给ViewDragHelper,由它决定如何处理。

构造方法中创建ViewDragHelper对象

ViewDragHelper dragHelper = ViewDragHelper.create(this, 1.0f, callback);

第二个参数sensitivity:灵敏度。

public class DragLayout extends FrameLayout {


    private ViewDragHelper dragHelper;
    private View leftView;
    private View mainView;
    private int range;
    private int width;
    private int height;

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

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

    public DragLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        dragHelper = ViewDragHelper.create(this, 1.0f, callback);
    }

    ViewDragHelper.Callback callback = new ViewDragHelper.Callback() {
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            return true;
        }

        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
        }

        @Override
        public int getViewHorizontalDragRange(View child) {
            return range;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;
        }
    };

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return dragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_CANCEL || event.getAction() == MotionEvent.ACTION_UP) {
            return false;
        }
        try {
            dragHelper.processTouchEvent(event);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        width = getMeasuredWidth();
        height = getMeasuredHeight();
        range = (int) (width * 0.6);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        leftView = getChildAt(0);
        mainView = getChildAt(1);
    }

}
方法名说明
tryCaptureView()是否捕获view,返回Boolean
onViewCaptured()childView被捕获后调用
getViewHorizontalDragRangechildView的水平拖动范围
getViewVerticalDragRange()childView的垂直拖动范围
clampViewPositionHorizontal()修正水平方向的拖动范围
clampViewPositionVertical()修正垂直方向的拖动范围
onEdgeDragStarted()侧拉栏默认放在哪边
onEdgeTouched()当边缘被触摸的时候调用
onEdgeLock()
onViewReleased()释放childView

上面我们已经实现了拖动的效果,但是leftView和mainView拖动的范围没有限制

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
    //限制mainView的拖动范围 [0,range]
    if (child == mainView) {
        if (left < 0) {
            left = 0;
        } else if (left > range) {
            left = range;
        }
    }
    return left;
}

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    super.onViewPositionChanged(changedView, left, top, dx, dy);
    //当拖动的是leftView的时候,把它固定住
    if (changedView == leftView) {
        leftView.layout(0, 0, width, height);
    }
}

对left的限制条件代码进行抽取成方法

private int fixLeft(int left) {
    if (left < 0) {
        left = 0;
    } else if (left > range) {
        left = range;
    }
    return left;
}

这里写图片描述

Step2:拖动的是leftView的时候,怎么让mainView滑动

在拖动leftView的时候,我们给mainView重新layout(),这样当我们手指虽然防盗leftView,拖动的是leftView,实际移动的缺失mainView。

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    super.onViewPositionChanged(changedView, left, top, dx, dy);
    //当拖动的是leftView的时候,把它固定住
    if (changedView == leftView) {
        leftView.layout(0, 0, width, height);
        int newLeft =fixLeft(left);
        int newLeft = mainView.getLeft()+dx;
        mainView.layout(newLeft,0,newLeft+width,+height);
    }
}

Step3:添加动画效果

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
    super.onViewPositionChanged(changedView, left, top, dx, dy);
    //当拖动的是leftView的时候,把它固定住
    if (changedView == leftView) {
        leftView.layout(0, 0, width, height);
        left = mainView.getLeft()+left;
        int newLeft =fixLeft(left);
        mainView.layout(newLeft,0,newLeft+width,+height);
    }

    initAnimation();
    invalidate();//不加也可以,不知道原因
}

leftView:缩放动画+平移动画+透明度动画
mainView:缩放动画

private void initAnimation() {
    //注意:分子必须*1.0f
    float percent = mainView.getLeft()*1.0f/range;//[0,1]

    leftView.setScaleX(percent*0.5f+0.5f);
    leftView.setScaleY(percent*0.5f+0.5f);

    leftView.setTranslationX(-0.5f+percent*0.5f);
    leftView.setAlpha(percent*0.7f+0.3f);

    mainView.setScaleX(1.0f-percent*0.5f/2);
    mainView.setScaleY(1.0f-percent*0.5f/2);
}

效果图:
这里写图片描述

Step4:当手指移开时,根据拖动距离判断打开或关闭mainView

releasedChild
xvel 水平方向的速度
yvel 竖直方向的速度

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
    super.onViewReleased(releasedChild, xvel, yvel);
    //如果移动过了range/2,就打开leftView.
    if (mainView.getLeft() > range / 2) {
        mainView.layout(range, 0, range + width, height);
    } else {
        mainView.layout(0, 0, width, height);
    }
}

当时这样比较生硬,没有动画效果

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
    super.onViewReleased(releasedChild, xvel, yvel);
    if (mainView.getLeft() > range / 2) {
        openMainView();
    } else {
        closeMinaView();
    }
}
private void openMainView() {
    //方法一:滑动滑动的比较生硬
//        mainView.layout(range, 0, width + range, height);
    //方法二:平滑的滑动
    if (dragHelper.smoothSlideViewTo(mainView, range, 0)) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

private void closeMinaView() {
    //方法一:滑动滑动的比较生硬
//        mainView.layout(0, 0, width, height);
    //方法二:平滑的滑动
    if (dragHelper.smoothSlideViewTo(mainView, 0, 0)) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

还要重写方法

@Override
public void computeScroll() {
    super.computeScroll();
    if (dragHelper.continueSettling(true)) {
        ViewCompat.postInvalidateOnAnimation(this);
    }
}

demo:https://gitee.com/customView/ViewDragHelper01.git

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值