/* * Copyright (C) 2018 yydcdut ([email protected]) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.yydcdut.sdlv; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Bitmap; import android.os.Handler; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.ImageView; /** * Created by yuyidong on 2018/4/21. */ class DragManager implements Callback.OnDragDropListener { /* drag 的时候透明度 */ private static final float DRAG_VIEW_ALPHA = 0.7f; /* drag 的 View */ private ImageView mDragView; /* Inner View */ private DragListView mDragListView; /* Handler的延时 */ private final long SCROLL_HANDLER_DELAY_MILLIS = 5; /* 移动距离 */ private final int DRAG_SCROLL_PX_UNIT = 25; /* Handler */ private Handler mScrollHandler; /* 边界比例,到这个比例的位置就开始移动 */ private final float BOUND_GAP_RATIO = 0.2f; /* 边界 */ private int mTopScrollBound; private int mBottomScrollBound; /* 按下的时候的Y轴坐标 */ private int mTouchDownForDragStartY; /* move 的时候的 Y 轴坐标 */ private int mLastDragY; /* 是否进入了scroll的handler里面了 */ private boolean mIsDragScrollerRunning = false; /* 最小距离 */ private float mTouchSlop; /* drag 的 View 的 Bitmap */ private Bitmap mDragViewBitmap; /* drag 的误差 */ private int mDragDelta; /* 是否正在被 drag */ private boolean isDragging; /* 是否已经回调了 handleDragStarted */ private boolean isInvokedDraggingStarted; /* 手指的 X 和 Y */ private float mFingerX, mFingerY; /* decorView */ private ViewGroup mDecorView; /* 当前 View 与 decorView 的差 */ private int[] mLeftAndTopOffset; /* 被 drag 的 View */ private ItemMainLayout mItemMainLayout; public DragManager(Context context, DragListView dragListView, ViewGroup decorView) { mTouchSlop = ViewConfiguration.get(context).getScaledPagingTouchSlop(); mDragListView = dragListView; mDragListView.setListDragDropListener(this); mDragView = new ImageView(context); mDecorView = decorView; mDecorView.addView(mDragView, new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT)); mLeftAndTopOffset = new int[]{0, 0}; } protected void onSizeChanged() { mLeftAndTopOffset = new int[]{0, 0}; getOffset(mDragListView, mDecorView, mLeftAndTopOffset); } private void getOffset(View current, View decorView, int[] array) { if (current != decorView) { getOffset(current, array); if (current.getParent() != null) { View view = (View) current.getParent(); getOffset(view, decorView, array); } } } private void getOffset(View view, int[] array) { array[0] = array[0] + view.getLeft() + view.getPaddingLeft(); array[1] = array[1] + view.getTop() + view.getPaddingLeft(); } protected void setDragging(boolean dragging) { this.isDragging = dragging; invokedDraggingStarted(); } protected boolean isDragging() { return isDragging; } private void invokedDraggingStarted() { if (!isInvokedDraggingStarted && isDragging) { mDragListView.handleDragStarted((int) mFingerX, (int) mFingerY); isInvokedDraggingStarted = true; calculateBound(); } } private void calculateBound() { final int boundGap = (int) (mDragListView.getHeight() * BOUND_GAP_RATIO); mTopScrollBound = (mDragListView.getTop() + boundGap); mBottomScrollBound = (mDragListView.getBottom() - boundGap); } protected boolean onInterceptTouchEvent(MotionEvent ev) { mFingerX = ev.getX(); mFingerY = ev.getY(); if (ev.getAction() == MotionEvent.ACTION_DOWN) { mTouchDownForDragStartY = (int) ev.getY(); } invokedDraggingStarted(); return isDragging; } protected boolean onTouchEvent(MotionEvent event) { mFingerX = event.getX(); mFingerY = event.getY(); if (!isDragging) { return false; } int eX = (int) event.getX(); int eY = (int) event.getY(); switch (event.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: invokedDraggingStarted(); break; case MotionEvent.ACTION_MOVE: invokedDraggingStarted(); mLastDragY = eY; mDragListView.handleDragMoving(eX, eY); if (!mIsDragScrollerRunning && (Math.abs(mLastDragY - mTouchDownForDragStartY) >= 4 * mTouchSlop)) { mIsDragScrollerRunning = true; ensureScrollHandler(); mScrollHandler.postDelayed(mDragScroller, SCROLL_HANDLER_DELAY_MILLIS); } break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: ensureScrollHandler(); mScrollHandler.removeCallbacks(mDragScroller); mIsDragScrollerRunning = false; mDragListView.handleDragFinished(eX, eY); isDragging = false; isInvokedDraggingStarted = false; break; } return isDragging; } private void ensureScrollHandler() { if (mScrollHandler == null) { mScrollHandler = new Handler(); } } private final Runnable mDragScroller = new Runnable() { @Override public void run() { if (mLastDragY <= mTopScrollBound) { mDragListView.smoothScrollBy(-DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS); } else if (mLastDragY >= mBottomScrollBound) { mDragListView.smoothScrollBy(DRAG_SCROLL_PX_UNIT, (int) SCROLL_HANDLER_DELAY_MILLIS); } mScrollHandler.postDelayed(this, SCROLL_HANDLER_DELAY_MILLIS); } }; @Override public boolean onDragStarted(int x, int y, View view) { mDragViewBitmap = createDraggedChildBitmap(view); if (mDragViewBitmap == null) { return false; } if (view instanceof ItemMainLayout) { mItemMainLayout = (ItemMainLayout) view; mItemMainLayout.disableBackgroundDrawable(); } mDragView.setImageBitmap(mDragViewBitmap); mDragView.setVisibility(View.VISIBLE); mDragView.setAlpha(DRAG_VIEW_ALPHA); mDragView.setX(mDragListView.getPaddingLeft()); mDragDelta = y - view.getTop(); mDragView.setY(y - mDragDelta + mLeftAndTopOffset[1]); return true; } private Bitmap createDraggedChildBitmap(View view) { if (view instanceof ItemMainLayout) { ((ItemMainLayout) view).disableBackgroundDrawable(); } view.setDrawingCacheEnabled(true); Bitmap cache = view.getDrawingCache(); Bitmap bitmap = null; if (cache != null) { try { bitmap = cache.copy(Bitmap.Config.ARGB_8888, false); } catch (OutOfMemoryError e) { bitmap = null; } } view.destroyDrawingCache(); view.setDrawingCacheEnabled(false); if (view instanceof ItemMainLayout) { ((ItemMainLayout) view).enableBackgroundDrawable(); } return bitmap; } @Override public void onDragMoving(int x, int y, View view, SlideAndDragListView.OnDragDropListener listener) { mDragView.setX(mLeftAndTopOffset[0]); mDragView.setY(y - mDragDelta + mLeftAndTopOffset[1]); } @Override public void onDragFinished(int x, int y, SlideAndDragListView.OnDragDropListener listener) { if (mDragView != null && mDragView.getVisibility() == View.VISIBLE) { ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(mDragView, "alpha", DRAG_VIEW_ALPHA, 0.0f); objectAnimator.setDuration(100); objectAnimator.addListener(new DragFinishAnimation()); objectAnimator.start(); } } private class DragFinishAnimation extends AnimatorListenerAdapter { @Override public void onAnimationEnd(Animator animation) { if (mDragViewBitmap != null) { mDragViewBitmap.recycle(); mDragViewBitmap = null; } mDragView.setVisibility(View.GONE); mDragView.setImageBitmap(null); if (mItemMainLayout != null) { mItemMainLayout.enableBackgroundDrawable(); mItemMainLayout = null; } } } }