一个可以拖动的自定义Gridview代码详解编程语言

这个可以拖动的gridview继承于gridview,所以,用法和gridview一样,

代码如下:

public class DragGridView extends GridView { 
private long dragResponseMS = 700; // item长按响应的时间 
private int mDragPosition;// 正在拖拽的position 
private boolean isDrag = false; // 是否可以拖拽,用于控件内部逻辑实现 
private boolean canDrag = true; // 是否可用拖拽,主要用于外部开放设置 
private boolean mAnimationEnd = true; 
private int mDownX; 
private int mDownY; 
private int moveX; 
private int moveY; 
private View mStartDragItemView = null; // 刚开始拖拽的item对应的View 
private ImageView mDragImageView; // 用于拖拽时显示的幻影镜像 
private Bitmap mDragBitmap; // 幻影镜像对应的Bitmap 
private final Vibrator mVibrator; // 震动器 
private final int mStatusHeight;// 状态栏的高度 
private final WindowManager mWindowManager; 
private WindowManager.LayoutParams mWindowLayoutParams; // item镜像的布局参数 
private int mPoint2ItemTop; // 按下的点到所在item的上边缘的距离 
private int mPoint2ItemLeft; 
private int mOffset2Top; // DragGridView距离屏幕顶部的偏移量 
private int mOffset2Left; 
private int mDownScrollBorder; // DragGridView自动向下滚动的边界值 
private int mUpScrollBorder; // DragGridView自动向上滚动的边界值 
private DragGridBaseAdapter mDragAdapter; 
private int mNumColumns; 
private int mColumnWidth; 
private boolean mNumColumnsSet; 
private int mHorizontalSpacing; 
private static final int speed = 20; // DragGridView自动滚动的速度 
private static final int MOVE_OFFSET = 25; 
private boolean moved = false; 
public static final int HANDLE_START = 0x3587; 
public static final int HANDLE_CANCLE = 0x3588; 
public static final int HANDLE_FINISH = 0x3589; 
private static OnMoveListener moveListener; // 拖拽开始与结束监听器 
private OnDeleteListener deleteListener; // 移动到垃圾桶时的监听器 
private float dragViewScale = 1.3f; 
private final TouchRect moveRect = new TouchRect(); 
private final TouchRect gridRect = new TouchRect(); 
private final TouchRect trashRect = new TouchRect(); 
public DragGridView(Context context) { 
this(context, null); 
} 
public DragGridView(Context context, AttributeSet attrs) { 
this(context, attrs, 0); 
} 
public DragGridView(Context context, AttributeSet attrs, int defStyle) { 
super(context, attrs, defStyle); 
mVibrator = (Vibrator) context 
.getSystemService(Context.VIBRATOR_SERVICE); 
mWindowManager = (WindowManager) context 
.getSystemService(Context.WINDOW_SERVICE); 
mStatusHeight = getStatusHeight(context); // 获取状态栏的高度 
if (!mNumColumnsSet) { 
mNumColumns = AUTO_FIT; 
} 
} 
/** 
* 获取状态栏的高度 
* 
* @param context 
* @return 
*/ 
private static int getStatusHeight(Context context) { 
int statusHeight = 0; 
Rect localRect = new Rect(); 
((Activity) context).getWindow().getDecorView() 
.getWindowVisibleDisplayFrame(localRect); 
statusHeight = localRect.top; 
if (0 == statusHeight) { 
Class<?> localClass; 
try { 
localClass = Class.forName("com.android.internal.R$dimen"); 
Object localObject = localClass.newInstance(); 
int i5 = Integer.parseInt(localClass 
.getField("status_bar_height").get(localObject) 
.toString()); 
statusHeight = context.getResources().getDimensionPixelSize(i5); 
} catch (Exception e) { 
e.printStackTrace(); 
} 
} 
return statusHeight; 
} 
private static final Handler mHandler = new Handler() { 
@Override 
public void handleMessage(android.os.Message msg) { 
if (moveListener != null) { 
if (msg.what == HANDLE_START) { 
moveListener.startMove(); 
} else if (msg.what == HANDLE_FINISH) { 
moveListener.finishMove(); 
} else if (msg.what == HANDLE_CANCLE) { 
moveListener.cancleMove(); 
} 
} 
} 
; 
}; 
// 用来处理是否为长按的Runnable 
private final Runnable mLongClickRunnable = new Runnable() { 
@Override 
public void run() { 
if (!canDrag) { 
return; 
} 
isDrag = true; // 设置可以拖拽 
moved = true; 
mHandler.sendEmptyMessage(HANDLE_START); 
mVibrator.vibrate(50); // 震动一下 
mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item 
createDragImage(mDragBitmap, mDownX, mDownY); 
mDragBitmap = null; 
} 
}; 
/** 
* 若设置为AUTO_FIT,计算有多少列 
*/ 
@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
if (mNumColumns == AUTO_FIT) { 
int numFittedColumns = 1; // 可用列 
if (mColumnWidth > 0) { 
int gridWidth = Math.max(MeasureSpec.getSize(widthMeasureSpec) 
- getPaddingLeft() - getPaddingRight(), 0); 
numFittedColumns = gridWidth / mColumnWidth; 
if (numFittedColumns > 0) { 
while (numFittedColumns != 1) { 
if (numFittedColumns * mColumnWidth 
+ (numFittedColumns - 1) * mHorizontalSpacing > gridWidth) { 
numFittedColumns--; 
} else { 
break; 
} 
} 
} 
} else { 
numFittedColumns = 2; 
} 
mNumColumns = numFittedColumns; 
} 
super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
} 
@Override 
protected void onDraw(Canvas canvas) { 
super.onDraw(canvas); 
initRecord(); 
} 
/** 
* 初始化坐标数据 
*/ 
private void initRecord() { 
gridRect.left = this.getLeft(); 
gridRect.right = this.getRight(); 
gridRect.top = this.getTop(); 
gridRect.bottom = this.getBottom(); 
} 
@Override 
public void setAdapter(ListAdapter adapter) { 
super.setAdapter(adapter); 
if (adapter instanceof DragGridBaseAdapter) { 
mDragAdapter = (DragGridBaseAdapter) adapter; 
} else { 
throw new IllegalStateException( 
"the adapter must be implements DragGridAdapter"); 
} 
} 
@Override 
public void setNumColumns(int numColumns) { 
super.setNumColumns(numColumns); 
mNumColumnsSet = true; 
this.mNumColumns = numColumns; 
} 
@Override 
public void setColumnWidth(int columnWidth) { 
super.setColumnWidth(columnWidth); 
mColumnWidth = columnWidth; 
} 
@Override 
public void setHorizontalSpacing(int horizontalSpacing) { 
super.setHorizontalSpacing(horizontalSpacing); 
this.mHorizontalSpacing = horizontalSpacing; 
} 
/** 
* 设置响应拖拽的毫秒数,默认是700毫秒 
* @param dragResponseMS 
*/ 
public void setDragResponseMS(long dragResponseMS) { 
this.dragResponseMS = dragResponseMS; 
} 
public void setOnDeleteListener(OnDeleteListener l) { 
this.deleteListener = l; 
} 
public void setDragEnable(boolean isDrag) { 
this.canDrag = isDrag; 
if (canDrag) { 
mHandler.removeCallbacks(mLongClickRunnable); 
} 
} 
@Override 
public boolean dispatchTouchEvent(MotionEvent ev) { 
if (canDrag) { 
switch (ev.getAction()) { 
case MotionEvent.ACTION_DOWN: 
mDownX = (int) ev.getX(); 
mDownY = (int) ev.getY(); 
moveRect.left = mDownX - MOVE_OFFSET; 
moveRect.right = mDownX + MOVE_OFFSET; 
moveRect.top = mDownY - MOVE_OFFSET; 
moveRect.bottom = mDownY + MOVE_OFFSET; 
// 根据按下的X,Y坐标获取所点击item的position 
mDragPosition = pointToPosition(mDownX, mDownY); 
if (mDragPosition == AdapterView.INVALID_POSITION || mDragPosition == getDragAdapter().getRealCount()) { 
return super.dispatchTouchEvent(ev); 
} 
// 使用Handler延迟dragResponseMS执行mLongClickRunnable 
mHandler.postDelayed(mLongClickRunnable, dragResponseMS); 
// 根据position获取该item所对应的View 
mStartDragItemView = getChildAt(mDragPosition 
- getFirstVisiblePosition()); 
mPoint2ItemTop = mDownY - mStartDragItemView.getTop(); 
mPoint2ItemLeft = mDownX - mStartDragItemView.getLeft(); 
mOffset2Top = (int) (ev.getRawY() - mDownY); 
mOffset2Left = (int) (ev.getRawX() - mDownX); 
// 获取DragGridView自动向上滚动的偏移量,小于这个值,DragGridView向下滚动 
mDownScrollBorder = getHeight() / 5; 
// 大于这个值,DragGridView向上滚动 
mUpScrollBorder = getHeight() * 4 / 5; 
// 开启mDragItemView绘图缓存 
mStartDragItemView.setDrawingCacheEnabled(true); 
// 获取mDragItemView在缓存中的Bitmap对象 
mDragBitmap = Bitmap.createBitmap(mStartDragItemView 
.getDrawingCache()); 
// 这一步很关键,释放绘图缓存,避免出现重复的镜像 
mStartDragItemView.destroyDrawingCache(); 
break; 
case MotionEvent.ACTION_MOVE: 
// 如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnable 
if (!isTouchInItem(moveRect, ev.getX(), ev.getY())) { 
mHandler.removeCallbacks(mLongClickRunnable); 
} 
break; 
case MotionEvent.ACTION_UP: 
mHandler.removeCallbacks(mScrollRunnable); 
mHandler.removeCallbacks(mLongClickRunnable); 
if (moved && getAdapter().getCount() > 0) { 
mHandler.sendEmptyMessage(HANDLE_FINISH); 
} else { 
mHandler.sendEmptyMessage(HANDLE_CANCLE); 
} 
moved = false; 
break; 
} 
} 
return super.dispatchTouchEvent(ev); 
} 
@Override 
public boolean onTouchEvent(MotionEvent ev) { 
if (isDrag && canDrag && mDragImageView != null) { 
switch (ev.getAction()) { 
case MotionEvent.ACTION_DOWN: 
initRecord(); 
break; 
case MotionEvent.ACTION_MOVE: 
moveX = (int) ev.getX(); 
moveY = (int) ev.getY(); 
onDragItem(moveX, moveY);// 拖动item 
break; 
case MotionEvent.ACTION_UP: 
onStopDrag(); 
isDrag = false; 
break; 
} 
return true; 
} 
return super.onTouchEvent(ev); 
} 
/** 
* 是否点击在GridView的item上面 
* 
* @param moveRect 
* @param x 
* @param y 
* @return 
*/ 
private boolean isTouchInItem(TouchRect moveRect, float x, float y) { 
// 防止手抖的处理,如果是横向在item上移动是没有影响的, 
// 但是纵向由于外层上下拉控件还是会有影响,具体解决请看NoteBookFragment类中的mSwipeRefreshLayout.setOnTouchListener方法 
if (x < moveRect.right && x > moveRect.left && y < moveRect.bottom 
&& y > moveRect.top) { 
return true; 
} else { 
return false; 
} 
} 
/** 
* 创建拖动的镜像 
* 
* @param bitmap 
* @param downX  按下的点相对父控件的X坐标 
* @param downY  按下的点相对父控件的X坐标 
*/ 
private void createDragImage(Bitmap bitmap, int downX, int downY) { 
mWindowLayoutParams = new WindowManager.LayoutParams(); 
mWindowLayoutParams.format = PixelFormat.TRANSLUCENT; // 图片之外的其他地方透明 
mWindowLayoutParams.gravity = Gravity.TOP | Gravity.LEFT; 
initDragOffset(bitmap); 
mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left; 
mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top - mStatusHeight; 
mWindowLayoutParams.alpha = 0.618f; // 透明度 
mWindowLayoutParams.width = (int) (bitmap.getWidth() * dragViewScale); 
mWindowLayoutParams.height = (int) (bitmap.getHeight() * dragViewScale); 
mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; 
mDragImageView = new ImageView(getContext()); 
mDragImageView.setScaleType(ImageView.ScaleType.FIT_XY); 
mDragImageView.setImageBitmap(bitmap); 
mWindowManager.addView(mDragImageView, mWindowLayoutParams); 
} 
private void initDragOffset(Bitmap bitmap){ 
mOffset2Left = mOffset2Left - (int) (bitmap.getWidth() * (dragViewScale - 1) / 2); 
mOffset2Top = mOffset2Top - (int) (bitmap.getHeight() * (dragViewScale - 1) / 2); 
} 
/** 
* 从界面上面移动拖动镜像 
*/ 
private void removeDragImage() { 
if (mDragImageView != null) { 
mWindowManager.removeView(mDragImageView); 
mDragImageView = null; 
} 
} 
/** 
* 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动 
* 
* @param moveX 
* @param moveY 
*/ 
private void onDragItem(int moveX, int moveY) { 
mWindowLayoutParams.x = moveX - mPoint2ItemLeft + mOffset2Left; 
mWindowLayoutParams.y = moveY - mPoint2ItemTop + mOffset2Top 
- mStatusHeight; 
mWindowManager.updateViewLayout(mDragImageView, mWindowLayoutParams); // 更新镜像的位置 
onSwapItem(moveX, moveY); 
// GridView自动滚动 
mHandler.post(mScrollRunnable); 
} 
/** 
* 当moveY的值大于向上滚动的边界值,触发GridView自动向上滚动 当moveY的值小于向下滚动的边界值,触发GridView自动向下滚动 
* 否则不进行滚动 
*/ 
private final Runnable mScrollRunnable = new Runnable() { 
@Override 
public void run() { 
int scrollY; 
if (getFirstVisiblePosition() == 0 
|| getLastVisiblePosition() == getCount() - 1) { 
mHandler.removeCallbacks(mScrollRunnable); 
} 
if (moveY > mUpScrollBorder) { 
scrollY = speed; 
mHandler.postDelayed(mScrollRunnable, 25); 
} else if (moveY < mDownScrollBorder) { 
scrollY = -speed; 
mHandler.postDelayed(mScrollRunnable, 25); 
} else { 
scrollY = 0; 
mHandler.removeCallbacks(mScrollRunnable); 
} 
smoothScrollBy(scrollY, 10); 
} 
}; 
/** 
* 交换item,并且控制item之间的显示与隐藏效果 
* 
* @param moveX 
* @param moveY 
*/ 
private void onSwapItem(int moveX, int moveY) { 
// 获取我们手指移动到的那个item的position 
final int tempPosition = pointToPosition(moveX, moveY); 
// 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换 
if (tempPosition != mDragPosition 
&& tempPosition != AdapterView.INVALID_POSITION 
&& tempPosition != getDragAdapter().getRealCount() 
&& mAnimationEnd) { 
mDragAdapter.reorderItems(mDragPosition, tempPosition); 
mDragAdapter.setHideItem(tempPosition); 
final ViewTreeObserver observer = getViewTreeObserver(); 
observer.addOnPreDrawListener(new OnPreDrawListener() { 
@Override 
public boolean onPreDraw() { 
observer.removeOnPreDrawListener(this); 
animateReorder(mDragPosition, tempPosition); 
mDragPosition = tempPosition; 
return true; 
} 
}); 
} 
} 
/** 
* 创建移动动画 
* 
* @param view 
* @param startX 
* @param endX 
* @param startY 
* @param endY 
* @return 
*/ 
private AnimatorSet createTranslationAnimations(View view, float startX, 
float endX, float startY, float endY) { 
ObjectAnimator animX = ObjectAnimator.ofFloat(view, "translationX", 
startX, endX); 
ObjectAnimator animY = ObjectAnimator.ofFloat(view, "translationY", 
startY, endY); 
AnimatorSet animSetXY = new AnimatorSet(); 
animSetXY.playTogether(animX, animY); 
return animSetXY; 
} 
/** 
* item的交换动画效果 
* 
* @param oldPosition 
* @param newPosition 
*/ 
private void animateReorder(final int oldPosition, final int newPosition) { 
boolean isForward = newPosition > oldPosition; 
List<Animator> resultList = new LinkedList<Animator>(); 
if (isForward) { 
for (int pos = oldPosition; pos < newPosition; pos++) { 
View view = getChildAt(pos - getFirstVisiblePosition()); 
if (view == null) { 
continue; 
} 
if ((pos + 1) % mNumColumns == 0) { 
resultList.add(createTranslationAnimations(view, 
-view.getWidth() * (mNumColumns - 1), 0, 
view.getHeight(), 0)); 
} else { 
resultList.add(createTranslationAnimations(view, 
view.getWidth(), 0, 0, 0)); 
} 
} 
} else { 
for (int pos = oldPosition; pos > newPosition; pos--) { 
View view = getChildAt(pos - getFirstVisiblePosition()); 
if ((pos + mNumColumns) % mNumColumns == 0) { 
resultList.add(createTranslationAnimations(view, 
view.getWidth() * (mNumColumns - 1), 0, 
-view.getHeight(), 0)); 
} else { 
resultList.add(createTranslationAnimations(view, 
-view.getWidth(), 0, 0, 0)); 
} 
} 
} 
AnimatorSet resultSet = new AnimatorSet(); 
resultSet.playTogether(resultList); 
resultSet.setDuration(300); 
resultSet.setInterpolator(new AccelerateDecelerateInterpolator()); 
resultSet.addListener(new AnimatorListenerAdapter() { 
@Override 
public void onAnimationStart(Animator animation) { 
mAnimationEnd = false; 
} 
@Override 
public void onAnimationEnd(Animator animation) { 
mAnimationEnd = true; 
} 
}); 
resultSet.start(); 
} 
/** 
* 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除 
*/ 
private void onStopDrag() { 
View view = getChildAt(mDragPosition - getFirstVisiblePosition()); 
if (view != null) { 
view.setVisibility(View.VISIBLE); 
} 
mDragAdapter.setHideItem(-1); 
removeDragImage(); 
} 
private DragGridBaseAdapter getDragAdapter() { 
return (DragGridBaseAdapter) getAdapter(); 
} 
public void setOnMoveListener(OnMoveListener listener) { 
moveListener = listener; 
} 
public interface OnMoveListener { 
void startMove(); 
void finishMove(); 
void cancleMove(); 
} 
public interface OnDeleteListener { 
void onDelete(int position); 
} 
public interface DragGridBaseAdapter { 
/** 
* 移动时回调 
*/ 
void reorderItems(int oldPosition, int newPosition); 
/** 
* 隐藏时回调 
*/ 
void setHideItem(int hidePosition); 
int getRealCount(); 
} 
private class TouchRect { 
int top; 
int bottom; 
int left; 
int right; 
} 
}

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/tech/pnotes/13044.html

(0)
上一篇 2021年7月19日 15:21
下一篇 2021年7月19日 15:22

相关推荐

发表回复

登录后才能评论