首先真相:
代码:
package com.dwdbz.draghelperdm.wight;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.customview.widget.ViewDragHelper;
import com.dwdbz.draghelperdm.R;
import com.dwdbz.draghelperdm.helper.MyDragHelper;
import java.util.ArrayList;
import java.util.List;
/**
* Author: Night
* Date: 2021/4/30 15:02
* Description:
*/
public class MyDragLayoutextends ViewGroup {
private static final int X_VEL_THRESHOLD =800;
private static final int X_DISTANCE_THRESHOLD =300;
public static final int VANISH_TYPE_LEFT =0;
public static final int VANISH_TYPE_RIGHT =1;
private int childWith =0;// 每一个子View对应的宽度
private ViewDragHelpermViewDragHelper;
private int mLeft;
private int mTop;
private StringTAG ="MyLinearLayout";
private int allWidth =0;// 面板的宽度
private int allHeight =0;// 面板的高度
private ListviewList =new ArrayList<>();// 存放的是每一层的view,从顶到底
private boolean isBack=false;
private final int OFFSET=10;
private boolean isDrag=false;
public MyDragLayout(Context context) {
this(context,null);
}
public MyDragLayout(Context context,@Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MyDragLayout(Context context,@Nullable AttributeSet attrs,int defStyleAttr) {
super(context, attrs, defStyleAttr);
MyDragHelper dragHelper =new MyDragHelper();
for (int i =0; i <10; i++) {
ImageView imageView =new ImageView(context);
switch (i%3){
case 0:
imageView.setImageResource(R.mipmap.icon_task_bg_color_1);
break;
case 1:
imageView.setImageResource(R.mipmap.icon_task_bg_color_2);
break;
case 2:
imageView.setImageResource(R.mipmap.icon_task_bg_color_3);
break;
}
// final int finalI = i;
// imageView.setOnClickListener(new OnClickListener() {
// @Override
// public void onClick(View v) {
// Log.e(TAG, "onClick: "+ finalI);
// }
// });
ViewGroup.LayoutParams vl =new ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
imageView.setLayoutParams(vl);
// viewList.add(imageView);
addView(imageView);
}
// setContainerViews(viewList);
mViewDragHelper = ViewDragHelper.create(this,new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(@NonNull View child,int pointerId) {
return child.getTag()==getChildAt(getChildCount()-1).getTag();
}
@Override
public void onViewCaptured(View capturedChild,int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
Log.e(TAG,"onViewCaptured: " + capturedChild.getTag());
}
@Override
public int clampViewPositionVertical(View child,int top,int dy) {
Log.e(TAG,"clampViewPositionVertical: "+top );
return top;
}
@Override
public int clampViewPositionHorizontal(View child,int left,int dx) {
Log.e(TAG,"clampViewPositionHorizontal: "+left);
return left;
}
/**
* 通过此方法可以把父布局中某一个子View移动到指定的左边,移动过程会调用
* smoothSlideViewTo()是指定一个子View,而settleCapturedViewAt()
* 是在ViewDragHelper.Callback的onViewReleased()方法中获取当前释放的子View,
* 然后实现手势惯性的效果
* @param changedView
* @param xvel
* @param yvel
*/
@Override
public void onViewReleased(View changedView,float xvel,float yvel) {
super.onViewReleased(changedView, xvel, yvel);
int finalX =mLeft;
int finalY =mTop;
int flyType = -1;
// 1. 下面这一坨计算finalX和finalY,要读懂代码需要建立一个比较清晰的数学模型才能理解,不信拉倒
int dx = changedView.getLeft() -mLeft;
int dy = changedView.getTop() -mTop;
// yvel < xvel * xyRate则允许以速度计算偏移
final float xyRate =3f;
if (xvel >X_VEL_THRESHOLD && Math.abs(yvel) < xvel * xyRate) {
// x正方向的速度足够大,向右滑动消失
finalX =allWidth;
finalY = (int) (yvel * (childWith + changedView.getLeft()) / xvel + changedView.getTop());
flyType =VANISH_TYPE_RIGHT;
}else if (xvel < -X_VEL_THRESHOLD && Math.abs(yvel) < -xvel * xyRate) {
// x负方向的速度足够大,向左滑动消失
finalX = -childWith;
finalY = (int) (yvel * (childWith + changedView.getLeft()) / (-xvel) + changedView.getTop());
flyType =VANISH_TYPE_LEFT;
}else if (dx >X_DISTANCE_THRESHOLD && Math.abs(dy) < dx * xyRate) {
// x正方向的位移足够大,向右滑动消失
finalX =allWidth;
finalY = dy * (childWith +mLeft) / dx +mTop;
flyType =VANISH_TYPE_RIGHT;
}else if (dx < -X_DISTANCE_THRESHOLD && Math.abs(dy) < -dx * xyRate) {
// x负方向的位移足够大,向左滑动消失
finalX = -childWith;
finalY = dy * (childWith +mLeft) / (-dx) +mTop;
flyType =VANISH_TYPE_LEFT;
}
// 如果斜率太高,就折中处理
if (finalY >allHeight) {
finalY =allHeight;
}else if (finalY < -allHeight /2) {
finalY = -allHeight /2;
}
if (getChildCount()==1){
mViewDragHelper.settleCapturedViewAt(mLeft,mTop);
}else {
if (finalX==mLeft){
mViewDragHelper.settleCapturedViewAt(mLeft,mTop);
}else {
// mViewDragHelper.settleCapturedViewAt(finalX, finalY);
isDrag=true;
if (mViewDragHelper.smoothSlideViewTo(changedView,finalX,finalY)){
ViewCompat.postInvalidateOnAnimation(MyDragLayout.this);
}
}
}
invalidate();
}
@Override
public void onViewPositionChanged(@NonNull View changedView,int left,int top,int dx,int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
Log.e(TAG,"onViewPositionChanged:left "+left );
}
});
}
@Override
public void computeScroll() {
Log.e(TAG,"computeScroll: " );
if (mViewDragHelper !=null) {
if (mViewDragHelper.continueSettling(true)){
// invalidate();
ViewCompat.postInvalidateOnAnimation(this);
}else {
Log.e(TAG,"computeScroll: false" );
if ( getChildCount()>1 &&mViewDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE &&isDrag){
isDrag=false;
//滑动结束
removeViewAt(getChildCount()-1);
requestLayout();
}
}
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
mViewDragHelper.processTouchEvent(event);
return true;
}
public void setContainerViews(List views) {
for (int i =0; i < views.size(); i++) {
addView(views.get(i));
}
}
@Override
protected void onLayout(boolean changed,int l,int t,int r,int b) {
Log.e(TAG,"onLayout:getChildCount= " + getChildCount());
int childCount = getChildCount();
for (int i =0; i < childCount; i++) {
int tagI=(childCount-1)-i;
View view = getChildAt(i);
view.setTag(i);
int viewTop=(getMeasuredHeight()-view.getMeasuredHeight())/2+(OFFSET*tagI);
int viewBottom = viewTop+view.getMeasuredHeight();
int viewLeft = (getMeasuredWidth() - view.getMeasuredWidth()) /2+(OFFSET*tagI);
int viewRight=viewLeft+view.getMeasuredWidth();
view.layout(viewLeft, viewTop, viewRight, viewBottom);
}
mLeft = getChildAt(childCount-1).getLeft();
mTop = getChildAt(childCount-1).getTop();
childWith=getChildAt(0).getMeasuredWidth();
}
@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec,heightMeasureSpec);
// int childCount = getChildCount();
// Log.e(TAG, "onMeasure: "+childCount );
// for (int i = 0; i < childCount; i++) {
// View childAt = getChildAt(i);
// this.measureChild(childAt,widthMeasureSpec,heightMeasureSpec);
// Log.e(TAG, "onMeasure: childAt.getMeasuredWidth="+childAt.getMeasuredWidth()
// +",childAt.getMeasuredHeight="+childAt.getMeasuredHeight() );
// }
measureChildren(widthMeasureSpec,heightMeasureSpec);
allWidth = getMeasuredWidth();
allHeight = getMeasuredHeight();
Log.e(TAG,"onMeasure:getMeasuredWidth()= " + getMeasuredWidth() +",getMeasuredHeight()=" + getMeasuredHeight());
}
}