转载

android-drag-square源码解析

Github: android-drag-square 分析版本: 886b738

android-drag-square 是一个仿探探的『个人编辑资料页』的头像拖动排序的开源库。

android-drag-square

android-drag-square源码解析

使用

将代码加入到工程,然后在 layout 布局中加入控件

<com.stone.dragsquare.DraggableSquareView
android:id="@+id/drag_square"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

添加图片

DraggableSquareView dragSquare = (DraggableSquareView) findViewById(R.id.drag_square);
dragSquare.fillItemImage(imageStatus, imagePath, isModify);

第一个参数是指图片的位置,第二个参数是图片的地址,第三个图片是判断图片是新添加还是只是修改。

源码

DraggableSquareView

public class DraggableSquareView extends ViewGroup {
// ACTION_DOWN按下后超过这个时间,就直接touch拦截,不会调用底层view的onClick事件
private static final int INTERCEPT_TIME_SLOP = 100;
//分别是指左上角的大图、右上角、右边中间、右下角,下边中间、左下角
private final int[] allStatus = {DraggableItemView.STATUS_LEFT_TOP, DraggableItemView.STATUS_RIGHT_TOP,
DraggableItemView.STATUS_RIGHT_MIDDLE, DraggableItemView.STATUS_RIGHT_BOTTOM,
DraggableItemView.STATUS_MIDDLE_BOTTOM, DraggableItemView.STATUS_LEFT_BOTTOM};

private int mTouchSlop = 5; // 判定为滑动的阈值,单位是像素
private int spaceInterval = 4; // 小方块之间的间隔
private final ViewDragHelper mDragHelper;
private GestureDetectorCompat moveDetector;

private List<Point> originViewPositionList = new ArrayList<>(); // 保存最初状态时每个itemView的坐标位置
private DraggableItemView draggingView; // 正在拖拽的view

public DraggableSquareView(Context context) {
this(context, null);
}

public DraggableSquareView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public DraggableSquareView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//ViewDragHelper
mDragHelper = ViewDragHelper
.create(this, 10f, new DragHelperCallback());
//手势判断
moveDetector = new GestureDetectorCompat(context,
new MoveDetector());
moveDetector.setIsLongpressEnabled(false); // 不能处理长按事件,否则违背最初设计的初衷
spaceInterval = (int) getResources().getDimension(R.dimen.drag_square_interval); // 小方块之间的间隔

// 滑动的距离阈值由系统提供
ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();

anchorHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (draggingView != null) {
// 开始移动重心的动画
draggingView.startAnchorAnimation();
}
}
};
}

@Override
protected void onFinishInflate() {
super.onFinishInflate();
//初始化View
int len = allStatus.length;
for (int i = 0; i < len; i++) {
DraggableItemView itemView = new DraggableItemView(getContext());
//告诉当前DraggableItemView是哪个位置
itemView.setStatus(allStatus[i]);
//将自己设置给DraggableItemView
itemView.setParentView(this);
//添加位置信息,但是是空的
originViewPositionList.add(new Point());
//将DraggableItemView添加为子View
addView(itemView);
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//测量
measureChildren(widthMeasureSpec, widthMeasureSpec);
int maxWidth = MeasureSpec.getSize(widthMeasureSpec);
int width = resolveSizeAndState(maxWidth, widthMeasureSpec, 0);
setMeasuredDimension(width, width);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//spaceInterval是间隔
int everyLength = (getMeasuredWidth() - 4 * spaceInterval) / 3;
int itemLeft = 0;
int itemTop = 0;
int itemRight = 0;
int itemBottom = 0;
// 每个view的边长是everyLength * 2 + spaceInterval
sideLength = everyLength * 2 + spaceInterval;
int halfSideLength = sideLength / 2; // 边长的一半
int rightCenter = r - spaceInterval - everyLength / 2;
int bottomCenter = b - spaceInterval - everyLength / 2;

float scaleRate = (float) everyLength / sideLength;
int num = getChildCount();
for (int i = 0; i < num; i++) {
DraggableItemView itemView = (DraggableItemView) getChildAt(i);
itemView.setScaleRate(scaleRate);
switch (itemView.getStatus()) {
case DraggableItemView.STATUS_LEFT_TOP:
int centerPos = spaceInterval + everyLength + spaceInterval / 2;
itemLeft = centerPos - halfSideLength;
itemRight = centerPos + halfSideLength;
itemTop = centerPos - halfSideLength;
itemBottom = centerPos + halfSideLength;
break;
case DraggableItemView.STATUS_RIGHT_TOP:
itemLeft = rightCenter - halfSideLength;
itemRight = rightCenter + halfSideLength;
int hCenter1 = spaceInterval + everyLength / 2;
itemTop = hCenter1 - halfSideLength;
itemBottom = hCenter1 + halfSideLength;
break;
case DraggableItemView.STATUS_RIGHT_MIDDLE:
itemLeft = rightCenter - halfSideLength;
itemRight = rightCenter + halfSideLength;
int hCenter2 = t + getMeasuredHeight() / 2;
itemTop = hCenter2 - halfSideLength;
itemBottom = hCenter2 + halfSideLength;
break;
case DraggableItemView.STATUS_RIGHT_BOTTOM:
itemLeft = rightCenter - halfSideLength;
itemRight = rightCenter + halfSideLength;
itemTop = bottomCenter - halfSideLength;
itemBottom = bottomCenter + halfSideLength;
break;
case DraggableItemView.STATUS_MIDDLE_BOTTOM:
int vCenter1 = l + getMeasuredWidth() / 2;
itemLeft = vCenter1 - halfSideLength;
itemRight = vCenter1 + halfSideLength;
itemTop = bottomCenter - halfSideLength;
itemBottom = bottomCenter + halfSideLength;
break;
case DraggableItemView.STATUS_LEFT_BOTTOM:
int vCenter2 = l + spaceInterval + everyLength / 2;
itemLeft = vCenter2 - halfSideLength;
itemRight = vCenter2 + halfSideLength;
itemTop = bottomCenter - halfSideLength;
itemBottom = bottomCenter + halfSideLength;
break;
}
//通过设置LayoutParams来设置高宽
ViewGroup.LayoutParams lp = itemView.getLayoutParams();
lp.width = sideLength;
lp.height = sideLength;
itemView.setLayoutParams(lp);
//更新Point,该值为左上角的坐标值
Point itemPoint = originViewPositionList.get(itemView.getStatus());
itemPoint.x = itemLeft;
itemPoint.y = itemTop;
//固定位置
itemView.layout(itemLeft, itemTop, itemRight, itemBottom);
}
}
}

DraggableSquareView 继承于 ViewGroup ,那么肯定得重新构造方法和 onLayout 方法,那么照着 View 的生命周期来看代码,发现在构造函数的时候申明了一个 ViewDragHelper 变量,这个类是拖动 View 操作的类;同时还有手势操作的类 GestureDetectorCompat ,以及一个 Handler ,而这个 Handler 的左右是判断手势意图是移动还是单击。

接着在 onFinishInflate 的时候将 DraggableItemView 都添加进去。

接着在 onMeasure 中测量大小,其中 resolveSizeAndState 方法与 getDefaultSize 方法类似,其内部实现的逻辑是一样的,但是又有区别, getDefaultSize 仅仅返回最终量算的尺寸信息,但 resolveSizeAndState 除了返回最终尺寸信息还会有可能返回量算的 state 标志位信息。通过看 resolveSizeAndState 代码发现,当 specMode 为AT_MOST ,并且父控件指定的尺寸 specSize 小于 View 自己想要的尺寸时,我们就会用掩码 MEASURED_STATE_TOO_SMALL 向量算结果加入尺寸太小的标记,这样其父 ViewGroup 就可以通过该标记其给子 View 的尺寸太小了,然后可能分配更大一点的尺寸给子 View 调用 resolveSizeAndState 方法的情况主要有两种:Android 中的许多 layout 类都调用了 resolveSizeAndState 方法,比如 LinearLayout 在量算过程中会调用 resolveSizeAndState 方法而非 getDefaultSize 方法。 我们自己在实现自定义的 View 或 ViewGroup 时,我们可以重写 onMeasure 方法,并在该方法内调用 resolveSizeAndState 方法。

紧接着在 onLayout 中设置 View 的位置,大小以及更新位置坐标。

那么看看这个 DraggableSquareView 的手势操作:

public class DraggableSquareView extends ViewGroup {

private DraggableItemView draggingView; // 正在拖拽的view
private int sideLength; // 每一个小方块的边长
private long downTime = 0; // 按下的时间
private int downX, downY; // 按下时的坐标位置
private Thread moveAnchorThread; // 按下的时候,itemView的重心移动,此为对应线程
private Handler anchorHandler; // itemView需要移动重心,此为对应的Handler

/**
* 这是viewdraghelper拖拽效果的主要逻辑
*/

private class DragHelperCallback extends ViewDragHelper.Callback {

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//如果changedView是当前正在drag的View
if (changedView == draggingView) {
DraggableItemView changedItemView = (DraggableItemView) changedView;
//换位置
switchPositionIfNeeded(changedItemView);
}
}

@Override
public boolean tryCaptureView(View child, int pointerId) {
// 按下的时候,缩放到最小的级别
draggingView = (DraggableItemView) child;
return draggingView.isDraggable();
}

@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
//释放
DraggableItemView itemView = (DraggableItemView) releasedChild;
itemView.onDragRelease();
}

@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
//横向的移动
DraggableItemView itemView = (DraggableItemView) child;
itemView.updateEndSpringX(dx);
System.out.println("clampViewPositionHorizontal dx=" + dx);
return left + dx;
}

@Override
public int clampViewPositionVertical(View child, int top, int dy) {
//纵向的移动
DraggableItemView itemView = (DraggableItemView) child;
itemView.updateEndSpringY(dy);
System.out.println("clampViewPositionVertical dy=" + dy);
return top + dy;
}
}

class MoveDetector extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float dx,
float dy)
{

// 拖动了,touch不往下传递
return Math.abs(dy) + Math.abs(dx) > mTouchSlop;
}
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//拦截父View事件
getParent().requestDisallowInterceptTouchEvent(true);
downX = (int) ev.getX();
downY = (int) ev.getY();
downTime = System.currentTimeMillis();
// 手指按下的时候,需要把某些view bringToFront,否则的话,tryCapture将不按预期工作
bringToFrontWhenTouchDown(downX, downY);
} else if (ev.getAction() == MotionEvent.ACTION_UP) {
//释放
if (draggingView != null) {
draggingView.onDragRelease();
}
draggingView = null;
//阻断
if (null != moveAnchorThread) {
moveAnchorThread.interrupt();
moveAnchorThread = null;
}
}
return super.dispatchTouchEvent(ev);
}

/**
* 按下时根据触点的位置,将某个view bring到前台
*/

private void bringToFrontWhenTouchDown(final int downX, final int downY) {
int statusIndex = getStatusByDownPoint(downX, downY);
final DraggableItemView itemView = getItemViewByStatus(statusIndex);
//如果该View不是在最前面,放到最前面
if (indexOfChild(itemView) != getChildCount() - 1) {
bringChildToFront(itemView);
}
//判断该View能不能drag
if (!itemView.isDraggable()) {
return;
}
//保存位置
itemView.saveAnchorInfo(downX, downY);
//判断到底是移动还是单击的一个判断Thread
moveAnchorThread = new Thread() {
@Override
public void run() {
//判断时间来做处理
try {
sleep(INTERCEPT_TIME_SLOP);
} catch (InterruptedException e) {
e.printStackTrace();
}

Message msg = anchorHandler.obtainMessage();
msg.sendToTarget();
}
};
moveAnchorThread.start();
}

/**
* 通过downX和downY来判断当前的status
*
* @param downX
* @param downY
* @return
*/

private int getStatusByDownPoint(int downX, int downY) {
int everyWidth = getMeasuredWidth() / 3;
if (downX < everyWidth) {
if (downY < everyWidth * 2) {
return DraggableItemView.STATUS_LEFT_TOP;
} else {
return DraggableItemView.STATUS_LEFT_BOTTOM;
}
} else if (downX < everyWidth * 2) {
if (downY < everyWidth * 2) {
return DraggableItemView.STATUS_LEFT_TOP;
} else {
return DraggableItemView.STATUS_MIDDLE_BOTTOM;
}
} else {
if (downY < everyWidth) {
return DraggableItemView.STATUS_RIGHT_TOP;
} else if (downY < everyWidth * 2) {
return DraggableItemView.STATUS_RIGHT_MIDDLE;
} else {
return DraggableItemView.STATUS_RIGHT_BOTTOM;
}
}
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
//拦截
if (downTime > 0 && System.currentTimeMillis() - downTime > INTERCEPT_TIME_SLOP) {
return true;
}
//ViewDragHelper
boolean shouldIntercept = mDragHelper.shouldInterceptTouchEvent(ev);
int action = ev.getActionMasked();
if (action == MotionEvent.ACTION_DOWN) {
mDragHelper.processTouchEvent(ev);
}
//是否要有移动意图
boolean moveFlag = moveDetector.onTouchEvent(ev);
if (moveFlag) {
if (null != moveAnchorThread) {
moveAnchorThread.interrupt();
moveAnchorThread = null;
}

if (null != draggingView) {
draggingView.startAnchorAnimation();
}
}
return shouldIntercept && moveFlag;
}

@Override
public boolean onTouchEvent(MotionEvent e) {
try {
// 该行代码可能会抛异常,正式发布时请将这行代码加上try catch
mDragHelper.processTouchEvent(e);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
}

这里有几点值得学习和探讨:

  1. moveAnchorThread 的作用,是在子线程中进行时间的延时,而我在做的时候直接用的 Handler 的 sendEmptyMessageDelay() 方法,如果想取消的话就 handler.removeMessage()
  2. ViewGroup#bringChildToFront(View) 该方法将 View 放到最前面那层,赞!之前我在做的时候是 重新 new 了个 ImageView 放在 ViewGroup 的最前的
  3. 通过手势与 View 的在位置的坐标(相对长宽)对比来判断在哪个位置了,而我在做的时候用的是 ListView#pointToPosition(int, int) 那套代码
  4. 正是因为 bringChildToFront 这部分,我在写 drag 的时候通过 View#layout(int, int, int, int) 来实现的移动

调换位置:

public class DraggableSquareView extends ViewGroup {

private class DragHelperCallback extends ViewDragHelper.Callback {

@Override
public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
//如果changedView是当前正在drag的View
if (changedView == draggingView) {
DraggableItemView changedItemView = (DraggableItemView) changedView;
//换位置
switchPositionIfNeeded(changedItemView);
}
}
}

/**
* view拖动的时候,看看是否需要互换位置
*/

private void switchPositionIfNeeded(DraggableItemView draggingView) {
int centerX = draggingView.getLeft() + sideLength / 2;
int centerY = draggingView.getTop() + sideLength / 2;
int everyWidth = getMeasuredWidth() / 3;

int fromStatus = -1, toStatus = draggingView.getStatus();

switch (draggingView.getStatus()) {
case DraggableItemView.STATUS_LEFT_TOP:// 拖动的是左上角的大图, 依次将小图向上顶
int fromChangeIndex = 0;
if (centerX > everyWidth * 2) { // 大图往右越过了位置,一定会跟右侧的三个View交换位置才行
if (centerY < everyWidth) {// 跟右上角的View交换位置
fromChangeIndex = DraggableItemView.STATUS_RIGHT_TOP;
} else if (centerY < everyWidth * 2) {// 跟右边中间的View交换位置
fromChangeIndex = DraggableItemView.STATUS_RIGHT_MIDDLE;
} else {// 跟右边下面的View交换位置
fromChangeIndex = DraggableItemView.STATUS_RIGHT_BOTTOM;
}
} else if (centerY > everyWidth * 2) {//大图往下越过了位置,跟下面的3个View交换位置
if (centerX < everyWidth) {//左下角的View
fromChangeIndex = DraggableItemView.STATUS_LEFT_BOTTOM;
} else if (centerX < everyWidth * 2) {//下边中间的View
fromChangeIndex = DraggableItemView.STATUS_MIDDLE_BOTTOM;
} else {//右边的位置
fromChangeIndex = DraggableItemView.STATUS_RIGHT_BOTTOM;
}
}

DraggableItemView toItemView = getItemViewByStatus(fromChangeIndex);
//如果该位置不可drag,也就是没有图
if (!toItemView.isDraggable()) {
return;
}

synchronized (this) {
for (int i = 1; i <= fromChangeIndex; i++) {
switchPosition(i, i - 1);
}
draggingView.setStatus(fromChangeIndex);
}
return;
case DraggableItemView.STATUS_RIGHT_TOP://拖动的是右上角的View
if (centerX < everyWidth * 2) {//与大图换
fromStatus = DraggableItemView.STATUS_LEFT_TOP;
} else if (centerY > everyWidth) {//与下边的那个图换
fromStatus = DraggableItemView.STATUS_RIGHT_MIDDLE;
}
break;

case DraggableItemView.STATUS_RIGHT_MIDDLE://拖动的是右边中间的图
if (centerX < everyWidth * 2 && centerY < everyWidth * 2) {//与大图换
fromStatus = DraggableItemView.STATUS_LEFT_TOP;
} else if (centerY < everyWidth) {//与上边的图换,也就是右上角那个
fromStatus = DraggableItemView.STATUS_RIGHT_TOP;
} else if (centerY > everyWidth * 2) {//与下边的图换,也就是右下角那个
fromStatus = DraggableItemView.STATUS_RIGHT_BOTTOM;
}
break;
case DraggableItemView.STATUS_RIGHT_BOTTOM://拖动的是右下角的图
if (centerX < everyWidth * 2) {//与左边的图换,就是下边中间那个
fromStatus = DraggableItemView.STATUS_MIDDLE_BOTTOM;
} else if (centerY < everyWidth * 2) {//与上边的图换,就是右边中间那个
fromStatus = DraggableItemView.STATUS_RIGHT_MIDDLE;
}
break;
case DraggableItemView.STATUS_MIDDLE_BOTTOM://拖动的是下边中间那个
if (centerX < everyWidth) {
fromStatus = DraggableItemView.STATUS_LEFT_BOTTOM;//与左边那个换,也就是左下角那个
} else if (centerX > everyWidth * 2) {
fromStatus = DraggableItemView.STATUS_RIGHT_BOTTOM;//与右边那个换,也就是右下角那个
} else if (centerY < everyWidth * 2) {
fromStatus = DraggableItemView.STATUS_LEFT_TOP;//与大图换
}
break;
case DraggableItemView.STATUS_LEFT_BOTTOM://拖动的是左下角那个
if (centerX > everyWidth) {
fromStatus = DraggableItemView.STATUS_MIDDLE_BOTTOM;//与右边的换,也就是下边中间那个
} else if (centerY < everyWidth * 2) {
fromStatus = DraggableItemView.STATUS_LEFT_TOP;//与大图换
}
break;
default:
break;
}
//换位置
synchronized (synObj) {
if (fromStatus > 0) {
if (switchPosition(fromStatus, toStatus)) {
draggingView.setStatus(fromStatus);
}
} else if (fromStatus == 0) {
for (int i = toStatus - 1; i >= 0; i--) {
switchPosition(i, i + 1);
}
draggingView.setStatus(fromStatus);
}
}
}

/**
* 调换位置
*/

private boolean switchPosition(int fromStatus, int toStatus) {
DraggableItemView itemView = getItemViewByStatus(fromStatus);
if (itemView.isDraggable()) {
itemView.switchPosition(toStatus);
return true;
}
return false;
}

/**
* 通过status来找到DraggableItemView
*
* @param status
* @return
*/

private DraggableItemView getItemViewByStatus(int status) {
int num = getChildCount();
for (int i = 0; i < num; i++) {
DraggableItemView itemView = (DraggableItemView) getChildAt(i);
if (itemView.getStatus() == status) {
return itemView;
}
}
return null;
}
}

调换位置这部分看代码其实很简单,每个 View 都有他自己应该换的位置。

那么再看看暴露出去那几个 API :

public class DraggableSquareView extends ViewGroup {

/**
* 给imageView添加图片
*/

public void fillItemImage(int imageStatus, String imagePath, boolean isModify) {
// 1. 如果是修改图片,直接填充就好
if (isModify) {
DraggableItemView itemView = getItemViewByStatus(imageStatus);
itemView.fillImageView(imagePath);
return;
}

// 2. 新增图片
for (int i = 0; i < allStatus.length; i++) {
DraggableItemView itemView = getItemViewByStatus(i);
if (!itemView.isDraggable()) {
itemView.fillImageView(imagePath);
break;
}
}
}

// 删除某一个ImageView
public void onDedeleteImage(DraggableItemView deleteView) {
int status = deleteView.getStatus();
int lastDraggableViewStatus = -1;
//一个循环来处理
for (int i = status + 1; i < allStatus.length; i++) {
DraggableItemView itemView = getItemViewByStatus(i);
if (itemView.isDraggable()) {
lastDraggableViewStatus = i;
switchPosition(i, i - 1);
} else {
break;
}
}
if (lastDraggableViewStatus > 0) {
deleteView.switchPosition(lastDraggableViewStatus);
}
}

/**
* 通过status来找到DraggableItemView
*
* @param status
* @return
*/

private DraggableItemView getItemViewByStatus(int status) {
int num = getChildCount();
for (int i = 0; i < num; i++) {
DraggableItemView itemView = (DraggableItemView) getChildAt(i);
if (itemView.getStatus() == status) {
return itemView;
}
}
return null;
}
}

DraggableItemView

public class DraggableItemView extends FrameLayout {
public static final int STATUS_LEFT_TOP = 0;
public static final int STATUS_RIGHT_TOP = 1;
public static final int STATUS_RIGHT_MIDDLE = 2;
public static final int STATUS_RIGHT_BOTTOM = 3;
public static final int STATUS_MIDDLE_BOTTOM = 4;
public static final int STATUS_LEFT_BOTTOM = 5;

public static final int SCALE_LEVEL_1 = 1; // 最大状态,缩放比例是100%
public static final int SCALE_LEVEL_2 = 2; // 中间状态,缩放比例scaleRate
public static final int SCALE_LEVEL_3 = 3; // 最小状态,缩放比例是smallerRate

private ImageView imageView;
private View maskView;
private int status;
private float scaleRate = 0.5f;
private Spring springX, springY;
private boolean hasSetCurrentSpringValue = false;
private DraggableSquareView parentView;
//这两个参数可以参考http://facebook.github.io/rebound/
private SpringConfig springConfigCommon = SpringConfig.fromOrigamiTensionAndFriction(140, 7);
private SpringConfig springConfigDragging = SpringConfig.fromOrigamiTensionAndFriction(400, 7);

public DraggableItemView(Context context) {
this(context, null);
}

public DraggableItemView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public DraggableItemView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
//添加布局
inflate(context, R.layout.drag_item, this);
//找到drag的时候的ImageView
imageView = (ImageView) findViewById(R.id.drag_item_imageview);
//找到显示的ImageView
maskView = findViewById(R.id.drag_item_mask_view);
//找到那个『+』的View
addView = findViewById(R.id.add_view);
//单击的时候的对话框
dialogListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.getId() == R.id.pick_image) {
// 从相册选择图片
pickImage();
} else {
// 删除
imagePath = null;
imageView.setImageBitmap(null);
addView.setVisibility(View.VISIBLE);
parentView.onDedeleteImage(DraggableItemView.this);
}
}
};
//调整View的大小
getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
if (!hasSetCurrentSpringValue) {
adjustImageView();
hasSetCurrentSpringValue = true;
}
}
});
//显示的View的点击事件
maskView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!isDraggable()) {
pickImage();
} else {
CustDialog dialog = new CustDialog(getContext());
dialog.setClickListener(dialogListener);
dialog.show();
}
}
});
//初始化Facebook的库
initSpring();
}

/**
* 调整ImageView的宽度和高度
*/

private void adjustImageView() {
if (status != STATUS_LEFT_TOP) {
imageView.setScaleX(scaleRate);
imageView.setScaleY(scaleRate);

maskView.setScaleX(scaleRate);
maskView.setScaleY(scaleRate);
}

setCurrentSpringPos(getLeft(), getTop());
}

/**
* 设置当前spring位置
*/

private void setCurrentSpringPos(int xPos, int yPos) {
springX.setCurrentValue(xPos);
springY.setCurrentValue(yPos);
}

/**
* 初始化Spring相关
*/

private void initSpring() {
SpringSystem mSpringSystem = SpringSystem.create();
springX = mSpringSystem.createSpring();
springY = mSpringSystem.createSpring();

springX.addListener(new SimpleSpringListener() {
@Override
public void onSpringUpdate(Spring spring) {
int xPos = (int) spring.getCurrentValue();
setScreenX(xPos);
}
});

springY.addListener(new SimpleSpringListener() {
@Override
public void onSpringUpdate(Spring spring) {
int yPos = (int) spring.getCurrentValue();
setScreenY(yPos);
}
});

springX.setSpringConfig(springConfigCommon);
springY.setSpringConfig(springConfigCommon);
}

/**
* 横向的移动
*
* @param screenX
*/

public void setScreenX(int screenX) {
this.offsetLeftAndRight(screenX - getLeft());
}

/**
* 纵向的移动
*
* @param screenY
*/

public void setScreenY(int screenY) {
this.offsetTopAndBottom(screenY - getTop());
}
}

Facebook 的库可以看看这里 Rebound ,一个很棒的库,把一开始 drag 和释放 drag 的动画都省略了,之前我做的时候都是用的 AnimatorSet 等属性动画来实现的。

看看暴露出去个 API :

当满足时间超过 100 ms,就进行往手指方向移动且缩小的动画

public class DraggableItemView extends FrameLayout {

/**
* 保存坐标
* MotionEvent.ACTION_DOWN 的时候调用
*
* @param downX
* @param downY
*/

public void saveAnchorInfo(int downX, int downY) {
int halfSide = getMeasuredWidth() / 2;
anchorX = downX - halfSide;
anchorY = downY - halfSide;
}

/**
* 真正开始动画
*/

public void startAnchorAnimation() {
//没有值的话就不做操作
if (anchorX == Integer.MIN_VALUE || anchorY == Integer.MIN_VALUE) {
return;
}

springX.setOvershootClampingEnabled(true);
springY.setOvershootClampingEnabled(true);
springX.setSpringConfig(springConfigDragging);
springY.setSpringConfig(springConfigDragging);
//移动到那个位置
animTo(anchorX, anchorY);
//缩放
scaleSize(DraggableItemView.SCALE_LEVEL_3);
}

/**
* 与 springX.addListener 和 springY.addListener 相接应
*
* @param xPos
* @param yPos
*/

public void animTo(int xPos, int yPos) {
springX.setEndValue(xPos);
springY.setEndValue(yPos);
}

/**
* 设置缩放大小
*/

public void scaleSize(int scaleLevel) {
float rate = scaleRate;
if (scaleLevel == SCALE_LEVEL_1) {
rate = 1.0f;
} else if (scaleLevel == SCALE_LEVEL_3) {
rate = smallerRate;
}

if (scaleAnimator != null && scaleAnimator.isRunning()) {
scaleAnimator.cancel();
}

scaleAnimator = ObjectAnimator
.ofFloat(this, "custScale", imageView.getScaleX(), rate)
.setDuration(200);
scaleAnimator.setInterpolator(new DecelerateInterpolator());
scaleAnimator.start();
}
}

切换位置:

public class DraggableItemView extends FrameLayout {

/**
* 从一个位置切换到另一个位置
*/

public void switchPosition(int toStatus) {
if (this.status == toStatus) {
throw new RuntimeException("程序错乱");
}

if (toStatus == STATUS_LEFT_TOP) {
scaleSize(SCALE_LEVEL_1);
} else if (this.status == STATUS_LEFT_TOP) {
scaleSize(SCALE_LEVEL_2);
}

this.status = toStatus;
Point point = parentView.getOriginViewPos(status);
animTo(point.x, point.y);
}
}

填充图片:

public class DraggableItemView extends FrameLayout {
public void fillImageView(String imagePath) {
this.imagePath = imagePath;
addView.setVisibility(View.GONE);
ImageLoader.getInstance().displayImage(imagePath, imageView);
}
}

当 drag 结束释放的时候:

public class DraggableItemView extends FrameLayout {
/**
* 释放
*/

public void onDragRelease() {
//通过status来判断要改变的size的大小
if (status == DraggableItemView.STATUS_LEFT_TOP) {
scaleSize(DraggableItemView.SCALE_LEVEL_1);
} else {
scaleSize(DraggableItemView.SCALE_LEVEL_2);
}

springX.setOvershootClampingEnabled(false);
springY.setOvershootClampingEnabled(false);
springX.setSpringConfig(springConfigCommon);
springY.setSpringConfig(springConfigCommon);
//得到将要移动过去的位置
Point point = parentView.getOriginViewPos(status);
//给facebook的库说动画起始的位置
setCurrentSpringPos(getLeft(), getTop());
//给facebook的库说位置移动到point.x和point.y的位置上
animTo(point.x, point.y);
}
}

更新位置:

public class DraggableItemView extends FrameLayout {

//与 springX.addListener 和 springY.addListener 相接应
public void updateEndSpringX(int dx) {
springX.setEndValue(springX.getEndValue() + dx);
}

//与 springX.addListener 和 springY.addListener 相接应
public void updateEndSpringY(int dy) {
springY.setEndValue(springY.getEndValue() + dy);
}
}
原文  http://yydcdut.com/2016/05/29/android-grag-square-analyse/
正文到此结束
Loading...