思路
先看效果图
思路:
布局内放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被捕获后调用 |
getViewHorizontalDragRange | childView的水平拖动范围 |
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);
}
}