Android GridView长按拖动,删除

来源:互联网 发布:java常用算法手册 编辑:程序博客网 时间:2024/05/21 09:11

最近因为公司项目,需要用到两个GridView之间交互,一个GridView可以进行item的长按拖动,可拖动的item可以进行点击删除。本文主要介绍如何实现GridView的拖动和删除。可拖动的GridView源自网络,感谢提供者,本人对其代码进行优化和添加新功能,希望能对读者有所帮助。实现思路是GridView的item设置长按事件,更新GridView让删除按钮可见。具体效果可详见下图:



先上可拖动GridView的实现代码:

public class DragGridView extends GridView {/** * DragGridView的item长按响应的时间, 默认是1000毫秒,也可以自行设置 */private long dragResponseMS = 1000;/** * 是否可以拖拽,默认不可以 */private boolean isDrag = false;private int mDownX;private int mDownY;private int moveX;private int moveY;/** * 正在拖拽的position */private int mDragPosition;/** * 刚开始拖拽的item对应的View */private View mStartDragItemView = null;/** * 用于拖拽的镜像,这里直接用一个ImageView */private ImageView mDragImageView;/** * 震动器 */private Vibrator mVibrator;private WindowManager mWindowManager;/** * item镜像的布局参数 */private WindowManager.LayoutParams mWindowLayoutParams;/** * 我们拖拽的item对应的Bitmap */private Bitmap mDragBitmap;/** * 按下的点到所在item的上边缘的距离 */private int mPoint2ItemTop;/** * 按下的点到所在item的左边缘的距离 */private int mPoint2ItemLeft;/** * DragGridView距离屏幕顶部的偏移量 */private int mOffset2Top;/** * DragGridView距离屏幕左边的偏移量 */private int mOffset2Left;/** * 状态栏的高度 */private int mStatusHeight;/** * DragGridView自动向下滚动的边界值 */private int mDownScrollBorder;/** * DragGridView自动向上滚动的边界值 */private int mUpScrollBorder;/** * DragGridView自动滚动的速度 */private static final int speed = 20;private boolean mAnimationEnd = true;private DragGridBaseAdapter mDragAdapter;private int mNumColumns;private int mColumnWidth;private boolean mNumColumnsSet;private int mHorizontalSpacing;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;}}private Handler mHandler = new Handler();// 用来处理是否为长按的Runnableprivate Runnable mLongClickRunnable = new Runnable() {@Overridepublic void run() {isDrag = true; // 设置可以拖拽mVibrator.vibrate(50); // 震动一下mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item// 根据我们按下的点显示item镜像createDragImage(mDragBitmap, mDownX, mDownY);}};@Overridepublic void setAdapter(ListAdapter adapter) {super.setAdapter(adapter);if (adapter instanceof DragGridBaseAdapter) {mDragAdapter = (DragGridBaseAdapter) adapter;} else {throw new IllegalStateException("the adapter must be implements DragGridAdapter");}}@Overridepublic void setNumColumns(int numColumns) {super.setNumColumns(numColumns);mNumColumnsSet = true;this.mNumColumns = numColumns;}@Overridepublic void setColumnWidth(int columnWidth) {super.setColumnWidth(columnWidth);mColumnWidth = columnWidth;}@Overridepublic void setHorizontalSpacing(int horizontalSpacing) {super.setHorizontalSpacing(horizontalSpacing);this.mHorizontalSpacing = horizontalSpacing;}/** * 若设置为AUTO_FIT,计算有多少列 */@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {if (mNumColumns == AUTO_FIT) {int numFittedColumns;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 = 1;}} else {numFittedColumns = 2;}mNumColumns = numFittedColumns;}super.onMeasure(widthMeasureSpec, heightMeasureSpec);}/** * 设置响应拖拽的毫秒数,默认是1000毫秒 *  * @param dragResponseMS */public void setDragResponseMS(long dragResponseMS) {this.dragResponseMS = dragResponseMS;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:mDownX = (int) ev.getX();mDownY = (int) ev.getY();// 根据按下的X,Y坐标获取所点击item的positionmDragPosition = pointToPosition(mDownX, mDownY);if (mDragPosition == AdapterView.INVALID_POSITION) {return super.dispatchTouchEvent(ev);}// 使用Handler延迟dragResponseMS执行mLongClickRunnablemHandler.postDelayed(mLongClickRunnable, dragResponseMS);// 根据position获取该item所对应的ViewmStartDragItemView = 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自动向下滚动的偏移量,大于这个值,DragGridView向上滚动mUpScrollBorder = getHeight() * 4 / 5;// 开启mDragItemView绘图缓存mStartDragItemView.setDrawingCacheEnabled(true);// 获取mDragItemView在缓存中的Bitmap对象mDragBitmap = Bitmap.createBitmap(mStartDragItemView.getDrawingCache());// 这一步很关键,释放绘图缓存,避免出现重复的镜像mStartDragItemView.destroyDrawingCache();break;case MotionEvent.ACTION_MOVE:int moveX = (int) ev.getX();int moveY = (int) ev.getY();// 如果我们在按下的item上面移动,只要不超过item的边界我们就不移除mRunnableif (!isTouchInItem(mStartDragItemView, moveX, moveY)) {mHandler.removeCallbacks(mLongClickRunnable);}break;case MotionEvent.ACTION_UP:mHandler.removeCallbacks(mLongClickRunnable);mHandler.removeCallbacks(mScrollRunnable);if (isDrag) {//拖动情况下抬起手势才需要调用onStopDrag函数,否则会造成点击事件冲突onStopDrag();isDrag = false;}break;}return super.dispatchTouchEvent(ev);}/** * 是否点击在GridView的item上面 *  * @param itemView * @param x * @param y * @return */private boolean isTouchInItem(View dragView, int x, int y) {if (dragView == null) {return false;}int leftOffset = dragView.getLeft();int topOffset = dragView.getTop();if (x < leftOffset || x > leftOffset + dragView.getWidth()) {return false;}if (y < topOffset || y > topOffset + dragView.getHeight()) {return false;}return true;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (isDrag && mDragImageView != null) {switch (ev.getAction()) {case MotionEvent.ACTION_MOVE:moveX = (int) ev.getX();moveY = (int) ev.getY();// 拖动itemonDragItem(moveX, moveY);break;case MotionEvent.ACTION_UP:onStopDrag();isDrag = false;break;}return true;}return super.onTouchEvent(ev);}/** * 创建拖动的镜像 *  * @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;mWindowLayoutParams.x = downX - mPoint2ItemLeft + mOffset2Left;mWindowLayoutParams.y = downY - mPoint2ItemTop + mOffset2Top- mStatusHeight;mWindowLayoutParams.alpha = 0.55f; // 透明度mWindowLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;mWindowLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;mWindowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;mDragImageView = new ImageView(getContext());mDragImageView.setImageBitmap(bitmap);mWindowManager.addView(mDragImageView, mWindowLayoutParams);}/** * 从界面上面移动拖动镜像 */private void removeDragImage() {if (mDragImageView != null) {mWindowManager.removeView(mDragImageView);mDragImageView = null;}}/** * 拖动item,在里面实现了item镜像的位置更新,item的相互交换以及GridView的自行滚动 *  * @param x * @param y */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 Runnable mScrollRunnable = new Runnable() {@Overridepublic 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的positionfinal int tempPosition = pointToPosition(moveX, moveY);// 假如tempPosition 改变了并且tempPosition不等于-1,则进行交换if (tempPosition != mDragPosition&& tempPosition != AdapterView.INVALID_POSITION&& mAnimationEnd) {mDragAdapter.reorderItems(mDragPosition, tempPosition);mDragAdapter.setHideItem(tempPosition);final ViewTreeObserver observer = getViewTreeObserver();observer.addOnPreDrawListener(new OnPreDrawListener() {@Overridepublic 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());System.out.println(pos);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() {@Overridepublic void onAnimationStart(Animator animation) {mAnimationEnd = false;}@Overridepublic 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();}/** * 获取状态栏的高度 *  * @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;}}

以及适配器的具体代码:

public class DragAdapter extends BaseAdapter implements DragGridBaseAdapter {private List<HashMap<String, Object>> list;private LayoutInflater mInflater;private int mHidePosition = -1;private Context context;private boolean isDraging = false;public DragAdapter(Context context, List<HashMap<String, Object>> list) {this.list = list;this.context = context;mInflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return list.size();}@Overridepublic Object getItem(int position) {return list.get(position);}@Overridepublic long getItemId(int position) {return position;}/** * 由于复用convertView导致某些item消失了,所以这里不复用item, */@Overridepublic View getView(final int position, View convertView, ViewGroup parent) {convertView = mInflater.inflate(R.layout.grid_item, null);ImageView mImageView = (ImageView) convertView.findViewById(R.id.item_image);TextView mTextView = (TextView) convertView.findViewById(R.id.item_text);ImageView delete = (ImageView) convertView.findViewById(R.id.delete);mImageView.setImageResource((Integer) list.get(position).get("item_image"));mTextView.setText((CharSequence) list.get(position).get("item_text"));if (position == mHidePosition) {convertView.setVisibility(View.INVISIBLE);}delete.setVisibility(isDraging ? View.VISIBLE : View.GONE);delete.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stublist.remove(position);notifyDataSetChanged();}});convertView.setOnLongClickListener(new OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {// TODO Auto-generated method stubisDraging = true;notifyDataSetChanged();return false;}});return convertView;}@Overridepublic void reorderItems(int oldPosition, int newPosition) {HashMap<String, Object> temp = list.get(oldPosition);if (oldPosition < newPosition) {for (int i = oldPosition; i < newPosition; i++) {Collections.swap(list, i, i + 1);}} else if (oldPosition > newPosition) {for (int i = oldPosition; i > newPosition; i--) {Collections.swap(list, i, i - 1);}}list.set(newPosition, temp);}@Overridepublic void setHideItem(int hidePosition) {this.mHidePosition = hidePosition;notifyDataSetChanged();}}

本文主要介绍下可拖动GridView的长按拖动和删除,删除功能可根据自己需求进行修改,可对适配器中的点击事件进行具体化操作以实现自己所需的效果。


DEMO下载










0 0
原创粉丝点击