精华内容
下载资源
问答
  • 本例实现GridView的长按拖动和删除
  • Android GridView长按拖动,删除

    千次阅读 2017-04-12 21:54:20
    最近因为公司项目,需要用到两个GridView之间交互,一个GridView可以进行item的长按拖动,可拖动的item可以进行点击删除。本文主要介绍如何实现GridView的拖动和删除。可拖动的GridView源自网络,感谢提供者,本人对...

    最近因为公司项目,需要用到两个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();
    
    	// 用来处理是否为长按的Runnable
    	private Runnable mLongClickRunnable = new Runnable() {
    
    		@Override
    		public void run() {
    			isDrag = true; // 设置可以拖拽
    			mVibrator.vibrate(50); // 震动一下
    			mStartDragItemView.setVisibility(View.INVISIBLE);// 隐藏该item
    
    			// 根据我们按下的点显示item镜像
    			createDragImage(mDragBitmap, mDownX, mDownY);
    		}
    	};
    
    	@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;
    	}
    
    	/**
    	 * 若设置为AUTO_FIT,计算有多少列
    	 */
    	@Override
    	protected 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;
    	}
    
    	@Override
    	public boolean dispatchTouchEvent(MotionEvent ev) {
    		switch (ev.getAction()) {
    		case MotionEvent.ACTION_DOWN:
    			mDownX = (int) ev.getX();
    			mDownY = (int) ev.getY();
    
    			// 根据按下的X,Y坐标获取所点击item的position
    			mDragPosition = pointToPosition(mDownX, mDownY);
    
    			if (mDragPosition == AdapterView.INVALID_POSITION) {
    				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自动向下滚动的偏移量,大于这个值,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的边界我们就不移除mRunnable
    			if (!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;
    	}
    
    	@Override
    	public boolean onTouchEvent(MotionEvent ev) {
    		if (isDrag && mDragImageView != null) {
    			switch (ev.getAction()) {
    			case MotionEvent.ACTION_MOVE:
    				moveX = (int) ev.getX();
    				moveY = (int) ev.getY();
    
    				// 拖动item
    				onDragItem(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() {
    
    		@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
    				&& 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());
    				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() {
    			@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();
    	}
    
    	/**
    	 * 获取状态栏的高度
    	 * 
    	 * @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);
    	}
    
    	@Override
    	public int getCount() {
    		return list.size();
    	}
    
    	@Override
    	public Object getItem(int position) {
    		return list.get(position);
    	}
    
    	@Override
    	public long getItemId(int position) {
    		return position;
    	}
    
    	/**
    	 * 由于复用convertView导致某些item消失了,所以这里不复用item,
    	 */
    	@Override
    	public 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() {
    
    			@Override
    			public void onClick(View v) {
    				// TODO Auto-generated method stub
    				list.remove(position);
    				notifyDataSetChanged();
    			}
    		});
    		convertView.setOnLongClickListener(new OnLongClickListener() {
    
    			@Override
    			public boolean onLongClick(View v) {
    				// TODO Auto-generated method stub
    				isDraging = true;
    				notifyDataSetChanged();
    				return false;
    			}
    		});
    		return convertView;
    	}
    
    	@Override
    	public 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);
    	}
    
    	@Override
    	public void setHideItem(int hidePosition) {
    		this.mHidePosition = hidePosition;
    		notifyDataSetChanged();
    	}
    
    }

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


    DEMO下载










    展开全文
  • android自定义GridView长按拖动排序,类似支付宝首页,震动提示,固定最后一个“更多”item。退出应用程序后,保存修改的item位置到数据库,实现记住修改位置。
  • 类似于手机软件的长按卸载 1、创建监听的view和被拖曳的view,这里以TextView和ImageView为例 <TextView android:id="@+id/removethis" android:layout_width="match_parent" android:layout_height="match_...

    1、创建监听的view和被拖曳的view,这里以TextView和ImageView为例

    <TextView
        android:id="@+id/removethis"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="拖到此处删除"
        android:textStyle="bold" />
    
    <ImageView
        android:id="@+id/imagetl"
        android:layout_width="50dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="150dp"
        android:background="@color/black_40"
        android:scaleType="centerInside"
        android:src="@drawable/delete"/>
    

    2、activity代码
    //实例化view

    TextView removestv = findViewById(R.id.removethis);
    ImageView  imagetl = findViewById(R.id.imagetl);
    

    //开始拖曳

    imagetl.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            //震动
            Vibrator vib = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);   //获取系统震动服务
            vib.vibrate(VibrationEffect.createOneShot(70, AudioAttributes.USAGE_NOTIFICATION));
            //生成可拖动的镜像
            View.DragShadowBuilder builder = new View.DragShadowBuilder(imagetl);
            imagetl.startDragAndDrop(null, builder, null,0);
            removestv.setOnDragListener(new View.OnDragListener() {
                @Override
                public boolean onDrag(View v, DragEvent event) {
                    final int action = event.getAction();
                    switch (action) {
                        case DragEvent.ACTION_DRAG_STARTED: // 拖拽开始
                            removestv.setBackgroundColor(Color.parseColor("#5A909090"));
                            return true;
                        case DragEvent.ACTION_DRAG_ENTERED: // 被拖拽View进入目标区域
                            removestv.setBackgroundColor(Color.RED);
                            return true;
                        case DragEvent.ACTION_DRAG_LOCATION: // 被拖拽View在目标区域移动
                            return true;
                        case DragEvent.ACTION_DRAG_EXITED: // 被拖拽View离开目标区域
                            removestv.setBackgroundColor(Color.parseColor("#6A909090"));
                            return true;
                        case DragEvent.ACTION_DROP: // 在目标区域内放开被拖拽View;
    					//这里写相关事件,本文以隐藏view为例
    					imagetl.setVisibility(View.INVISIBLE);
                            return true;
                        case DragEvent.ACTION_DRAG_ENDED: // 拖拽完成
                            return false;
                    }
                    return false;
                }
            });
            return false;
        }
    });
    

    如果要拖动GridView、ListView指定的item则使用他们的item长按方法获取到对应item,然后操作与前面一样

    展开全文
  • 本demo实现GridView中item长按拖动删除,完美解决bug,仿支付宝含有动画,又不懂得可以参考我的博客:http://blog.csdn.net/u010151514/article/details/51273485,欢迎留言讨论
  • android长按拖拽控件

    千次阅读 2019-06-25 11:30:48
    要实现android长按拖拽控件,需要以下因素: 对于控件view,需要以下监听: 1.setOnDragListener() 2.setOnLongClickListener() 给控件view设置长按拖动,以一个imageView为例: 假设一个imageView已经...

    要实现android长按拖拽控件,需要以下因素:
    对于控件view,需要以下监听:

    1.setOnDragListener()
    2.setOnLongClickListener()

    给控件view设置长按拖动,以一个imageView为例:
    假设一个imageView已经被实例化了。

    imageView.setOnLongClickListener(v -> {
                    View.DragShadowBuilder builder = new View.DragShadowBuilder(v);
                    v.startDrag(null, builder, null, 0);//第三个参数是传入一个关于这个view信息的任意对象(getLocalState),它即你需要在拖拽监听中的调用event.getLocalState()获取到这个对象来操作用的(比如传入一个RecyclerView中的position)。如果不需要这个对象,传null
                     return true;
                });
    

    假设这个imageView的外围layout对象为containerLayout(比如一个RelativeLayout )
    当拖动这个imageView时,外围layout需要执行点什么操作时:

    containerLayout.setOnDragListener((v, event) -> {
                Log.d("containerLayout", "event.getAction():" + event.getAction());
                switch (event.getAction()) {
                    case DragEvent.ACTION_DRAG_ENDED: //拖拽停止
                       //your operation
                        break;
                    case DragEvent.ACTION_DROP://拖拽中
                            //your operation
                        break;
                    default:
                        break;
                }
                return true;
            });
    

    当然,不止是外围layout可以做拖拽监听,其他的view也可以针对这个imageView的拖拽行为而做点什么。举个栗子,拖拽这个imageView到另一个view上面时执行点操作:

    view1.setOnDragListener(((v, event) -> {
                switch (event.getAction()) {
                    case DragEvent.ACTION_DROP://拖拽中
                        int index = (int) event.getLocalState();
                        //之前讲到的v.startDrag(null, builder, null, 0);第三个参数如果传入一个int 类型的值,在这里便可以 通过(int) event.getLocalState()取出来。如果传的是null,上面这行代码就去掉好了
                        break;
                    case DragEvent.ACTION_DRAG_ENDED: //拖拽停止
                        break;
                    default:
                        break;
                }
                return true;
            }));
    

    补充:
    如果想要长按的瞬间震动一下,可以在长按回调处增加两行代码:

     Vibrator vib = (Vibrator) getSystemService(Service.VIBRATOR_SERVICE);   //获取系统震动服务
      vib.vibrate(70);   //震动70毫秒
    

    更详细的对拖拽的各个事件的解释看这个博主的博文即可:
    Android 用户界面ImageView拖动复制(Drag and Drop)

    展开全文
  • 领导这几天让做一个项目,就是可以实现像支付宝首页一样的可以长按拖动,删除的界面,以前没做过,领导让我做的时候觉得简直是老虎吃天,无从下手啊,可是领导的任务还是要实现的,没办法,就自己网上找咯,但是网上...

    领导这几天让做一个项目,就是可以实现像支付宝首页一样的可以长按拖动,删除的界面,以前没做过,领导让我做的时候觉得简直是老虎吃天,无从下手啊,可是领导的任务还是要实现的,没办法,就自己网上找咯,但是网上的demo五花八门无法满足我的需求,而且bug还很多,所以最后就自己实现了,说实话,这个效果困扰了我好几个星期,因为其中牵扯的知识点太多了,什么事件分发机制,动画效果,互换位置的算法,还有拖动,这些我都没有接触过,所以只有一点一点来做咯,如果大家还没有了解过这些知识点,我建议大家先去了解一下,毕竟这方面的知识也确实不少。

    关于事件的分发机制,大家可以看我相关的文章浅谈Android事件分发机制,请大家尊重我的劳动成果,转载请注明出处,每个人都不容易

    刚开始大家也不要灰心,毕竟做什么事都需要经历第一次,所以一定要迎风破浪,不要被困难吓倒,努力去克服它们,接下来我就先给大家说说具体的实现思路,毕竟我的gridview要实现长按拖动、删除,还带动画:

    1、根据自己在屏幕上按下的X,Y的坐标来获取要拖动的item的索引

    2、然后通过onInterceptTouchEvent方法,对事件进行分发,判断是长按还是点击

    3、如果我们对item实现了长按的操作,先隐藏该item,然后创建一个item镜像来代替该item进行拖动

    4、当我们移动到新的位置,通过onTouchEvent方法的ACTION_MOVE来判断我们是否移动到了一个新的位置

    5、如果移动到了一个新的位置,就根据移动得位置来对其他的item也进行移动

    6、松开时,判断当前是否移动到新的位置,如果是我,对其他的item依次进行移动,然后移除镜像,更新gridview

    7、删除是对item的操作,在adapter里面实现即可,删除完之后先实现动画,然后再刷新界面

    根据我上面说的这个步骤,我想大家应该都对这个效果心里有个大概的轮廓了吧,我这里实现的是批量操作,当前,如果大家想实现只对长按的那个item进行操作,只需要在onInterceptTouchEvent方法里面进行更改就可以了,我这里用了一个if语句进行判断,是否处于编辑状态,如果是,就不用再进行长安了,如果不是,则需要进行长按。

    好了,废话不多说了,让我们来看看具体的实现吧,我先给大家介绍一个这个自定义控件的实现,在介绍完之后,我会把demo上传,大家可以自己去研究。
    我们新建一个项目,DragGridView,在里面新建一个类,叫DragGrid.java,家下来我们看看代码,根据代码来具体讲解

    package com.roch.water_source_zwb.view;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import com.roch.water_source_zwb.MainActivity;
    import com.roch.water_source_zwb.R;
    import com.roch.water_source_zwb.adapter.DragAdapter;
    import com.roch.water_source_zwb.adapter.DragMoreAdapter;
    import com.roch.water_source_zwb.utils.Common;
    import com.roch.water_source_zwb.utils.Utils;
    
    import android.animation.Animator;
    import android.animation.AnimatorListenerAdapter;
    import android.animation.AnimatorSet;
    import android.animation.ObjectAnimator;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.PixelFormat;
    import android.os.Vibrator;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.Gravity;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewTreeObserver;
    import android.view.WindowManager;
    import android.view.ViewTreeObserver.OnPreDrawListener;
    import android.view.animation.AccelerateDecelerateInterpolator;
    import android.view.animation.Animation;
    import android.view.animation.Animation.AnimationListener;
    import android.view.animation.TranslateAnimation;
    import android.widget.AdapterView;
    import android.widget.GridView;
    import android.widget.ImageView;
    import android.widget.LinearLayout;
    import android.widget.RelativeLayout;
    import android.widget.TextView;
    import android.widget.Toast;
    import android.widget.AbsListView.LayoutParams;
    
    /**
     * 首页HomeFragment中的GridView
     * @author zhaodongshao
     *
     */
    public class DragGrid extends GridView {
        /** 是否在拖动*/
        public boolean isDrag = false;
        /** 点击时候的X位置 */
        public int downX;
        /** 点击时候的Y位置 */
        public int downY;
        /** 点击时候对应整个界面的X位置 */
        public int windowX;
        /** 点击时候对应整个界面的Y位置 */
        public int windowY;
        /** 屏幕上的X */
        private int win_view_x;
        /** 屏幕上的Y*/
        private int win_view_y;
        /** 拖动的里x的距离  */
        int dragOffsetX;
        /** 拖动的里Y的距离  */
        int dragOffsetY;
        /** 长按时候对应postion */
        public int dragPosition;
        /** Up后对应的ITEM的Position */
        private int dropPosition;
        /** 开始拖动的ITEM的Position*/
        private int startPosition;
        /** item高 */
        private int itemHeight;
        /** item宽 */
        private int itemWidth;
        /** 拖动的时候对应ITEM的VIEW */
        private View dragImageView = null;
        /** 长按的时候ITEM的VIEW*/
        private ViewGroup dragItemView = null;
        /** WindowManager管理器 */
        private WindowManager windowManager = null;
        /** 需要拖动的镜像*/
        private WindowManager.LayoutParams windowParams = null;
        /** item总量*/
        private int itemTotalCount;
        /** 一行的ITEM数量*/
        private int nColumns = 3;
        /** 行数 */
        private int nRows;
        /** 剩余部分 */
        private int Remainder;
        /** 是否在移动 */
        private boolean isMoving = false;
        /** 需要移动的position*/
        private int holdPosition;
        /** 拖动的时候放大的倍数 */
        private double dragScale = 1.2D;
        /** 震动器  */
        private Vibrator mVibrator;
        /** 每个ITEM之间的水平间距 */
        private int mHorizontalSpacing = 15;
        /** 每个ITEM之间的竖直间距 */
        private int mVerticalSpacing = 15;
        /* 移动时候最后个动画的ID */
        private String LastAnimationID;
        /**
         * 执行动画的布局
         */
        private RelativeLayout rootLayout;
    
        private Context mContext;
        public DragGrid(Context context) {
            super(context);
            init(context);
        }
    
        public DragGrid(Context context, AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context);
        }
    
        public DragGrid(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(context);
        }
    
        public void init(Context context){
            this.mContext = context;
            //获取振动器
            mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
            //将布局文件中设置的间距dip转为px
            mHorizontalSpacing = Utils.dip2px(context, mHorizontalSpacing);
        }
    
        /**
         * 获取状态栏的高度
         * @return
         */
        private int getStatusBarHeight() {
            int result = 0;
            int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
            if (resourceId > 0) {
                result = getResources().getDimensionPixelSize(resourceId);
            }
            return result;
        }
    
        /**
         * 判断是否点击在指定view中
         * @param view
         * @param ev
         * @return
         */
        private boolean inRangeOfView(View view, MotionEvent ev){
            int[] location = new int[2];
            view.getLocationOnScreen(location);
            int downX = (int)ev.getX();
            int downY = (int)ev.getY();
            int x = location[0];
            //这里需要减去状态栏的高度和导航栏的高度,否则座标会存在偏差
            int y = location[1] - getStatusBarHeight() - MainActivity.StruesHeight;
            //获取当前item的宽和高
            int viewWidth = view.getWidth();
            int viewHeight = view.getHeight();
    
            if(downX < x || downX > (x + viewWidth) || downY < y || downY > (y + viewHeight)){
                return false;
            }
            return true;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            if (ev.getAction() == MotionEvent.ACTION_DOWN) {
                downX = (int) ev.getX();
                downY = (int) ev.getY();
                windowX = (int) ev.getX();
                windowY = (int) ev.getY();
                //当前手指所点击的position
                int position = pointToPosition(downX, downY);
                if (position!=AdapterView.INVALID_POSITION) {
                    //获取当前点击的view
                    View view = getChildAt(position - getFirstVisiblePosition());
                    //得到该item中的删除按钮
                    ImageView iView = (ImageView)view.findViewById(R.id.delet_iv);
                    //这里判断是否处于编辑状态,如果是就不须要再长按,如果不是,还需要长按进入编辑状态,如果不想批量操作可以把这个if语句进行修改
                    if (isDrag) {
                        //判断是否点击在删除按钮中
                        if (!inRangeOfView(iView, ev)) {
                            int x = (int) ev.getX();// 长安事件的X位置
                            int y = (int) ev.getY();// 长安事件的y位置
                            isDrag = true;
                            startPosition = pointToPosition(x, y);// 第一次点击的postion
                            dragPosition = pointToPosition(x, y);//获取要拖动的item的position
                            if (dragPosition != AdapterView.INVALID_POSITION) {
                                ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition - getFirstVisiblePosition());
                                TextView dragTextView = (TextView)dragViewGroup.findViewById(R.id.name_tv);
                                dragTextView.setSelected(true);
                                dragTextView.setEnabled(false);
                                itemHeight = dragViewGroup.getHeight();
                                itemWidth = dragViewGroup.getWidth();
                                itemTotalCount = DragGrid.this.getCount();
                                int row = itemTotalCount / nColumns;// 算出行数
                                Remainder = (itemTotalCount % nColumns);// 算出最后一行多余的数量
                                if (Remainder != 0) {
                                    nRows = row + 1;
                                } else {
                                    nRows = row;
                                }
                                // 如果特殊的这个不等于拖动的那个,并且不等于-1
                                if (dragPosition != AdapterView.INVALID_POSITION) {
                                    // 释放的资源使用的绘图缓存。如果你调用buildDrawingCache()手动没有调用setDrawingCacheEnabled(真正的),你应该清理缓存使用这种方法。
                                    win_view_x = windowX - dragViewGroup.getLeft();//VIEW相对自己的X,半斤
                                    win_view_y = windowY - dragViewGroup.getTop();//VIEW相对自己的y,半斤
                                    dragOffsetX = (int) (ev.getRawX() - x);//手指在屏幕的上X位置-手指在控件中的位置就是距离最左边的距离
                                    dragOffsetY = (int) (ev.getRawY() - y);//手指在屏幕的上y位置-手指在控件中的位置就是距离最上边的距离
                                    //这里是为了获取当前item的图片缓存,提高绘图效率
                                    dragItemView = dragViewGroup;
                                    dragViewGroup.destroyDrawingCache();
                                    dragViewGroup.setDrawingCacheEnabled(true);
                                    Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup.getDrawingCache());
                                    //                          mVibrator.vibrate(50);//设置震动时间
                                    startDrag(dragBitmap, (int)ev.getRawX(),  (int)ev.getRawY());
                                    hideDropItem();
    //隐藏需要拖动的item本身                     
    dragViewGroup.setVisibility(View.INVISIBLE);
                                    isMoving = false;
                                    requestDisallowInterceptTouchEvent(true);
                                    return true;
                                }
                            }
                        }
                        else {
                            Log.i("DragGrid------------>>>>>>>>>", "已点击");
                        }
                    }
                    else {
                        //实现长按item的操作
                        setOnClickListener(ev);
                    }
                }
            }
            return super.onInterceptTouchEvent(ev);
        }
    
    
        /**
         * 删除item的消失动画
         * @param position
         */
        public void deleteInfo(int position)
        {
            DeleteAnimation(position);
        }
    
        /**
        * 这里需要获取gridview最外层的布局,因为我们无法直接去对item进行动     画,所以需要创建一个镜像,对镜像进行动画操作
        */
        public void setRelativeLayout(RelativeLayout layout)
        {
            this.rootLayout = layout;
        }
    
        /**
         * 删除View动画
         * @param adapter 
         * @param 需要执行的动画的View
         */
        public void DeleteAnimation(final int position)
        {
    
            final View view = getChildAt(position);
            view.setDrawingCacheEnabled(true);
            Bitmap bitmap = Bitmap.createBitmap(view.getDrawingCache());
            view.destroyDrawingCache();
            //创建一个imageview来显示缓存图片
            final ImageView imageView = new ImageView(mContext);
            imageView.setImageBitmap(bitmap);
            imageView.setLayoutParams(new LayoutParams((int) (Common.Width / 7),
                    (int) (Common.Width / 7)));
    
            LayoutParams ivlp = new LayoutParams(LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT);
    
            final int aimPosit = getCount() - 1;
            //将imageview添加到gridview外层的布局文件中,否则会不显示
            rootLayout.addView(imageView, ivlp);
            //这里用到了属性动画,关于属性动画我会专门来讲,所以这里大家需要自己去了解
            AnimatorSet animatorSet = createTranslationAnim(aimPosit, view, imageView);
            animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
            animatorSet.setDuration(500);
            animatorSet.addListener(new AnimatorListenerAdapter() {
    
                @Override
                public void onAnimationStart(Animator animation) {
                    // TODO Auto-generated method stub
                    DragAdapter adapter = (DragAdapter)getAdapter();
                    adapter.setHidePosition(position);
                    Common.isAnimaEnd = false;
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    // TODO Auto-generated method stub
                    imageView.setVisibility(View.GONE);
                    imageView.clearAnimation();
                    rootLayout.removeView(imageView);
                    DragAdapter adapter = (DragAdapter)getAdapter();
                    adapter.deletInfo(position);
                    final ViewTreeObserver vto = getViewTreeObserver();
                    vto.addOnPreDrawListener(new OnPreDrawListener() {
    
                        @Override
                        public boolean onPreDraw() {
                            // TODO Auto-generated method stub
                            vto.removeOnPreDrawListener(this);
                            animateReorder(position, aimPosit);
                            return false;
                        }
                    });
                }
            });
            animatorSet.start();
        }
    
        /**
         * 平移动画
         * @param view 需要移动的view
         * @param startX 开始的X坐标
         * @param endX 结束的X坐标
         * @param startY 开始的Y坐标
         * @param endY 结束的Y坐标
         * @return
         */
        private AnimatorSet createAnimator(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后,其他的item需要自动补齐,这是一个算法问题,如果有不懂的可以私信问我
         * @param position
         * @param aimPositin
         */
        private void animateReorder(int deletePosition, int itemCount)
        {
            boolean isForward = itemCount > deletePosition;
            List<Animator> list = new ArrayList<Animator>();
            if (isForward) {
    
                for (int pos = deletePosition; pos < itemCount; pos++) {
                    View view = getChildAt(pos - getFirstVisiblePosition());
                    if ((pos + 1) % nColumns == 0) {
                        list.add(createAnimator(view, -view.getWidth() * (nColumns - 1), 0, view.getHeight(), 0));
                    }
                    else {
                        list.add(createAnimator(view, view.getWidth(), 0, 0, 0));
                    }
                }
    
            }else {
    
                for (int pos = deletePosition; pos > itemCount; pos--) {
                    View view = getChildAt(pos - getFirstVisiblePosition());
                    if ((pos + nColumns) % nColumns == 0) {
                        list.add(createAnimator(view,view.getWidth() * (nColumns - 1), 0, -view.getHeight(), 0));
                    } else {
                        list.add(createAnimator(view, -view.getWidth(), 0, 0, 0));
                    }
                }
            }
            AnimatorSet set = new AnimatorSet();
            set.playTogether(list);
            set.setInterpolator(new AccelerateDecelerateInterpolator());
            set.setDuration(300);
            set.addListener(new AnimatorListenerAdapter() {
    
                @Override
                public void onAnimationStart(Animator animation) {
                    // TODO Auto-generated method stub
                }
    
                @Override
                public void onAnimationEnd(Animator animation) {
                    // TODO Auto-generated method stub
                    Common.isAnimaEnd = true;
                }
    
            });
            set.start();
        }
    
        /**
         * 
         * 方法: createTranslationAnim,同样,这里也是属性动画,大家可以去自己了解
         * <p>
         * 描述: TODO
         * <p>
         * 参数: @param position 参数: @param aimPosit 参数: @param view 参数: @param
         * animView 参数: @return
         * <p>
         * 返回: AnimatorSet
         * <p>
         * 异常
         * <p>
         * 作者: wedcel wedcel@gmail.com
         * <p>
         * 时间: 2015年8月25日 下午4:49:23
         */
        private AnimatorSet createTranslationAnim(int aimPosit, View view, ImageView animView) {
            int startx = view.getLeft();
            int starty = view.getTop();
            View aimView = getChildAt(aimPosit);
            int endx = aimView.getLeft();
            int endy = aimView.getTop();
    
            ObjectAnimator animX = ObjectAnimator.ofFloat(animView, "translationX",
                    startx, endx);
            ObjectAnimator animY = ObjectAnimator.ofFloat(animView, "translationY",
                    starty, endy);
            ObjectAnimator scaleX = ObjectAnimator.ofFloat(animView, "scaleX", 1f,
                    0.5f);
            ObjectAnimator scaleY = ObjectAnimator.ofFloat(animView, "scaleY", 1f,
                    0.5f);
            ObjectAnimator alpaAnim = ObjectAnimator.ofFloat(animView, "alpha", 1f,
                    0.0f);
    
            AnimatorSet animSetXY = new AnimatorSet();
            animSetXY.playTogether(animX,animY,scaleX, scaleY, alpaAnim);
            return animSetXY;
        }
    
        //这是ViewGroup的onTouchEvent事件,这个事件是需要在onInterceptTouchEvent没有向下分发事件,自己来进行处理
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            // TODO Auto-generated method stub
            if (dragImageView != null && dragPosition != AdapterView.INVALID_POSITION) {
                // 移动时候的对应x,y位置
                int x = (int) ev.getX();
                int y = (int) ev.getY();
                switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    downX = (int) ev.getX();
                    windowX = (int) ev.getX();
                    downY = (int) ev.getY();
                    windowY = (int) ev.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    //进行拖动时改变其位置
                    onDrag(x, y ,(int) ev.getRawX() , (int) ev.getRawY());
                    if (!isMoving){
                        OnMove(x, y);
                    }
                    //如果移动得位置没有item,则跳出
                    if (pointToPosition(x, y) != AdapterView.INVALID_POSITION){
                        break;
                    }
                    break;
                case MotionEvent.ACTION_UP:
                    stopDrag();
                    onDrop(x, y);
                    requestDisallowInterceptTouchEvent(false);
                    break;
    
                default:
                    break;
                }
            }
            return super.onTouchEvent(ev);
        }
    
        //刷新数据
        public void refresh()
        {
            stopDrag();
            isDrag = false;
            DragAdapter mDragAdapter = (DragAdapter) getAdapter();
            mDragAdapter.setisDelete(false);
            mDragAdapter.notifyDataSetChanged();
        }
    
        /** 在拖动的情况,这里用到了android的窗口机制,大家也可以先去了解一下 */
        private void onDrag(int x, int y , int rawx , int rawy) {
            if (dragImageView != null) {
                //透明度
                windowParams.alpha = 0.6f;
                //          windowParams.x = rawx - itemWidth / 2;
                //          windowParams.y = rawy - itemHeight / 2;
                //显示坐标
                windowParams.x = rawx - win_view_x;
                windowParams.y = rawy - win_view_y;
                //对window进行更新
                windowManager.updateViewLayout(dragImageView, windowParams);
            }
        }
    
        /** 在松手下放的情况 */
        private void onDrop(int x, int y) {
            // 根据拖动到的x,y坐标获取拖动位置下方的ITEM对应的POSTION
            int tempPostion = pointToPosition(x, y);
            //      if (tempPostion != AdapterView.INVALID_POSITION) {
            dropPosition = tempPostion;
            DragAdapter mDragAdapter = (DragAdapter) getAdapter();
            //显示刚拖动的ITEM
            mDragAdapter.setShowDropItem(true);
            //刷新适配器,让对应的ITEM显示
            mDragAdapter.notifyDataSetChanged();
            //      }
        }
        /**
         * 长按点击监听
         * @param ev
         */
        public void setOnClickListener(final MotionEvent ev) {
            setOnItemLongClickListener(new OnItemLongClickListener() {
    
                @Override
                public boolean onItemLongClick(AdapterView<?> parent, View view,
                        int position, long id) {
                    int x = (int) ev.getX();// 长安事件的X位置
                    int y = (int) ev.getY();// 长安事件的y位置
                    isDrag = true;
                    DragAdapter mDragAdapter = (DragAdapter) getAdapter();
                    mDragAdapter.setisDelete(true);
                    mDragAdapter.notifyDataSetChanged();
                    startPosition = position;// 第一次点击的postion
                    dragPosition = position;
                    //如果是最后一个更多,则不能进行长按
                    //              if (startPosition == getCount() - 1) {
                    //                  return false;
                    //              }
                    ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition - getFirstVisiblePosition());
                    TextView dragTextView = (TextView)dragViewGroup.findViewById(R.id.name_tv);
    
    
                    dragTextView.setSelected(true);
                    dragTextView.setEnabled(false);
                    itemHeight = dragViewGroup.getHeight();
                    itemWidth = dragViewGroup.getWidth();
                    itemTotalCount = DragGrid.this.getCount();
                    int row = itemTotalCount / nColumns;// 算出行数
                    Remainder = (itemTotalCount % nColumns);// 算出最后一行多余的数量
                    if (Remainder != 0) {
                        nRows = row + 1;
                    } else {
                        nRows = row;
                    }
                    // 如果特殊的这个不等于拖动的那个,并且不等于-1
                    if (dragPosition != AdapterView.INVALID_POSITION) {
                        // 释放的资源使用的绘图缓存。如果你调用buildDrawingCache()手动没有调用setDrawingCacheEnabled(真正的),你应该清理缓存使用这种方法。
                        win_view_x = windowX - dragViewGroup.getLeft();//VIEW相对自己的X,半斤
                        win_view_y = windowY - dragViewGroup.getTop();//VIEW相对自己的y,半斤
                        dragOffsetX = (int) (ev.getRawX() - x);//手指在屏幕的上X位置-手指在控件中的位置就是距离最左边的距离
                        dragOffsetY = (int) (ev.getRawY() - y);//手指在屏幕的上y位置-手指在控件中的位置就是距离最上边的距离
                        dragItemView = dragViewGroup;
                        dragViewGroup.destroyDrawingCache();
                        dragViewGroup.setDrawingCacheEnabled(true);
                        Bitmap dragBitmap = Bitmap.createBitmap(dragViewGroup.getDrawingCache());
                        mVibrator.vibrate(50);//设置震动时间
                        startDrag(dragBitmap, (int)ev.getRawX(),  (int)ev.getRawY());
                        hideDropItem();
                        dragViewGroup.setVisibility(View.INVISIBLE);
                        isMoving = false;
                        requestDisallowInterceptTouchEvent(true);
                        return true;
                    }
                    return false;
                }
            });
        }
    
        public void startDrag(Bitmap dragBitmap, int x, int y) {
            stopDrag();
            windowParams = new WindowManager.LayoutParams();// 获取WINDOW界面的
            //Gravity.TOP|Gravity.LEFT;这个必须加  
            windowParams.gravity = Gravity.TOP | Gravity.LEFT;
            //      windowParams.x = x - (int)((itemWidth / 2) * dragScale);
            //      windowParams.y = y - (int) ((itemHeight / 2) * dragScale);
            //得到preview左上角相对于屏幕的坐标   
            windowParams.x = x - win_view_x;
            windowParams.y = y  - win_view_y; 
            //      this.windowParams.x = (x - this.win_view_x + this.viewX);//位置的x值
            //      this.windowParams.y = (y - this.win_view_y + this.viewY);//位置的y值
            //设置拖拽item的宽和高  
            windowParams.width = (int) (dragScale * dragBitmap.getWidth());// 放大dragScale倍,可以设置拖动后的倍数
            windowParams.height = (int) (dragScale * dragBitmap.getHeight());// 放大dragScale倍,可以设置拖动后的倍数
            this.windowParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE                           
                    | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE                           
                    | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON                           
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
            this.windowParams.format = PixelFormat.TRANSLUCENT;
            this.windowParams.windowAnimations = 0;
            ImageView iv = new ImageView(getContext());
            iv.setImageBitmap(dragBitmap);
            windowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// "window"
            windowManager.addView(iv, windowParams);
            dragImageView = iv;
        }
    
        /** 停止拖动 ,释放并初始化 */
        private void stopDrag() {
            if (dragImageView != null) {
                windowManager.removeView(dragImageView);
                dragImageView = null;
            }
        }
    
        /** 在ScrollView内,所以要进行计算高度 */
        @Override
        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
            super.onMeasure(widthMeasureSpec, expandSpec);
        }
    
        /** 隐藏 放下 的ITEM*/
        private void hideDropItem() {
            ((DragAdapter) getAdapter()).setShowDropItem(false);
        }
    
        /** 获取移动动画 */
        public Animation getMoveAnimation(float toXValue, float toYValue) {
            TranslateAnimation mTranslateAnimation = new TranslateAnimation(
                    Animation.RELATIVE_TO_SELF, 0.0F,
                    Animation.RELATIVE_TO_SELF,toXValue, 
                    Animation.RELATIVE_TO_SELF, 0.0F,
                    Animation.RELATIVE_TO_SELF, toYValue);// 当前位置移动到指定位置
            mTranslateAnimation.setFillAfter(true);// 设置一个动画效果执行完毕后,View对象保留在终止的位置。
            mTranslateAnimation.setDuration(300L);
            return mTranslateAnimation;
        }
    
        /** 移动的时候触发,这里也用到了算法,具体的大家可以研究下*/
        public void OnMove(int x, int y) {
            // 拖动的VIEW下方的POSTION
            int dPosition = pointToPosition(x, y);
            // 判断下方的POSTION是否是最开始2个不能拖动的
            if (dPosition < getCount() - 1) {
                if ((dPosition == -1) || (dPosition == dragPosition)){
                    return;
                }
                dropPosition = dPosition;
                if (dragPosition != startPosition){
                    dragPosition = startPosition;
                }
                int movecount;
                //拖动的=开始拖的,并且 拖动的 不等于放下的
                if ((dragPosition == startPosition) || (dragPosition != dropPosition)){
                    //移需要移动的动ITEM数量
                    movecount = dropPosition - dragPosition;
                }else{
                    //移需要移动的动ITEM数量为0
                    movecount = 0;
                }
                if(movecount == 0){
                    return;
                }
    
                int movecount_abs = Math.abs(movecount);
    
                if (dPosition != dragPosition) {
                    //dragGroup设置为不可见
                    ViewGroup dragGroup = (ViewGroup) getChildAt(dragPosition);
                    dragGroup.setVisibility(View.INVISIBLE);
                    float to_x = 1;// 当前下方positon
                    float to_y;// 当前下方右边positon
                    //x_vlaue移动的距离百分比(相对于自己长度的百分比)
                    float x_vlaue = ((float) mHorizontalSpacing / (float) itemWidth) + 1.0f;
                    //y_vlaue移动的距离百分比(相对于自己宽度的百分比)
                    float y_vlaue = ((float) mVerticalSpacing / (float) itemHeight) + 1.0f;
                    Log.d("x_vlaue", "x_vlaue = " + x_vlaue);
                    for (int i = 0; i < movecount_abs; i++) {
                        to_x = x_vlaue;
                        to_y = y_vlaue;
                        //像左
                        if (movecount > 0) {
                            // 判断是不是同一行的
                            holdPosition = dragPosition + i + 1;
                            if (dragPosition / nColumns == holdPosition / nColumns) {
                                to_x = - x_vlaue;
                                to_y = 0;
                            } else if (holdPosition % 3 == 0) {
                                to_x = 2 * x_vlaue;
                                to_y = - y_vlaue;
                            } else {
                                to_x = - x_vlaue;
                                to_y = 0;
                            }
                        }else{
                            //向右,下移到上,右移到左
                            holdPosition = dragPosition - i - 1;
                            if (dragPosition / nColumns == holdPosition / nColumns) {
                                to_x = x_vlaue;
                                to_y = 0;
                            } else if((holdPosition + 1) % 3 == 0){
                                to_x = -2 * x_vlaue;
                                to_y = y_vlaue;
                            }else{
                                to_x = x_vlaue;
                                to_y = 0;
                            }
                        }
                        ViewGroup moveViewGroup = (ViewGroup) getChildAt(holdPosition);
                        Animation moveAnimation = getMoveAnimation(to_x, to_y);
                        moveViewGroup.startAnimation(moveAnimation);
                        //如果是最后一个移动的,那么设置他的最后个动画ID为LastAnimationID
                        if (holdPosition == dropPosition) {
                            LastAnimationID = moveAnimation.toString();
                        }
                        moveAnimation.setAnimationListener(new AnimationListener() {
    
                            @Override
                            public void onAnimationStart(Animation animation) {
                                // TODO Auto-generated method stub
                                isMoving = true;
                            }
    
                            @Override
                            public void onAnimationRepeat(Animation animation) {
                                // TODO Auto-generated method stub
    
                            }
    
                            @Override
                            public void onAnimationEnd(Animation animation) {
                                // TODO Auto-generated method stub
                                // 如果为最后个动画结束,那执行下面的方法
                                if (animation.toString().equalsIgnoreCase(LastAnimationID)) {
                                    DragAdapter mDragAdapter = (DragAdapter) getAdapter();
                                    mDragAdapter.exchange(startPosition,dropPosition);
                                    startPosition = dropPosition;
                                    dragPosition = dropPosition;
                                    isMoving = false;
                                }
                            }
                        });
                    }
                }
            }
        }
    }

    首先我在这里说一下自定义View的分发事件,不了解的可以去先了解一下,对于View因为他没有子View所以他没有onInterceptTouchEvent方法,而ViewGroup有子View所以,他有onInterceptTouchEvent方法,这个方法主要是拦截事件是否继续向下分发,返回true则自己处理,返回false就交给子View处理,getChildAt()用来获取当前item,但是这里要注意的是,他需要减去getFirstVisiblePosition()第一个可见的item的索引,对于上面的几个距离大家可以参考我下面的图
    这里写图片描述

    1、dragOffsetX在这里指的是当前拖动的item距离屏幕左边的距离,也就是图中的1号线
    2、dragOffsetY在这里指的是当前拖动的item距离屏幕上边的距离,图中的2号线

    这里主要就是用的windowManager类,android的窗口机制,要想真正实现拖动我们还要重写onTouchEvent(),在它的ACTION_MOVE中实现拖动操作,完了之后移除镜像,刷新数据,这里需要保存拖动后的顺序到数据库中,具体的我就不说了,大家可以进行Google了解,
    接下来我们看下布局文件吧

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        style="@style/top_title_strues"
        android:clickable="true"
        android:orientation="vertical" >
    
        <RelativeLayout
            android:id="@+id/ll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="14dip"
            android:layout_marginRight="14dip" >
    
            <com.roch.water_source_zwb.view.DragGrid
                android:id="@+id/userGridView"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:clickable="true"
                android:gravity="center"
                android:horizontalSpacing="14dip"
                android:listSelector="@android:color/transparent"
                android:longClickable="true"
                android:numColumns="3"
                android:scrollbars="vertical"
                android:stretchMode="columnWidth"
                android:verticalSpacing="14.0px" />
        </RelativeLayout>

    很简单,没什么说的,然后我们再看下item的布局

    <?xml version="1.0" encoding="utf-8"?>
    <com.roch.water_source_zwb.view.SquareLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent" >
    
        <LinearLayout
            android:id="@+id/edit_ll"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:background="@color/transparent"
            android:orientation="vertical" >
    
            <ImageView
                android:id="@+id/icon_iv"
                android:layout_width="@dimen/layout_30"
                android:layout_height="@dimen/layout_30"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="10dp"
                android:contentDescription="@string/hello_world"
                android:scaleType="fitXY"
                android:src="@mipmap/tubiao_20" />
    
            <TextView
                android:id="@+id/name_tv"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="5dp"
                android:gravity="center"
                android:paddingBottom="10dp"
                android:text="@string/hello_world"
                android:textColor="@color/white"
                android:textSize="15sp" />
        </LinearLayout>
    
        <ImageView
            android:id="@+id/delet_iv"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignRight="@id/edit_ll"
            android:layout_alignTop="@id/edit_ll"
            android:contentDescription="@string/hello_world"
            android:padding="8dp"
            android:scaleType="centerInside"
            android:src="@mipmap/edit_flag"
            android:visibility="gone" />
    
    </com.roch.water_source_zwb.view.SquareLayout>

    大家注意下,我这里是重写了RelativeLayout布局,为了让item是一个正方形,这个布局文件也很简单,大家一看都明白了,不明白的可以私信我

    我们再来看一下适配器吧,这里需要在适配器里面做一些操作

    package com.roch.water_source_zwb.adapter;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import com.lidroid.xutils.DbUtils;
    import com.lidroid.xutils.exception.DbException;
    import com.roch.water_source_zwb.R;
    import com.roch.water_source_zwb.app.MyApplication;
    import com.roch.water_source_zwb.entity.DragView;
    import com.roch.water_source_zwb.entity.More;
    import com.roch.water_source_zwb.utils.Common;
    import com.roch.water_source_zwb.view.DragGrid;
    
    import android.content.Context;
    import android.os.Handler;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.view.ViewGroup.LayoutParams;
    import android.view.View.OnClickListener;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class DragAdapter extends BaseAdapter {
        /** TAG*/
        private final static String TAG = "DragAdapter";
        /** 是否显示底部的ITEM */
        private boolean isItemShow = false;
        private Context context;
        /** 控制的postion */
        private int holdPosition;
        /** 是否改变 */
        private boolean isChanged = false;
        /** 是否可见 */
        boolean isVisible = true;
        /** 可以拖动的列表(即用户选择的频道列表) */
        public List<DragView> channelList;
        /** TextView 频道内容 */
        class HolderView
        {
            private TextView item_text;
            private ImageView iv_icon;
            private ImageView iv_delete;
        }
        private boolean isDelete = false;
        /** 要删除的position */
        public int remove_position = -1;
    
        private Handler mHandler = new Handler();
    
        private DragGrid grid;
    
        /**
         * 指定隐藏的position
         */
        private int hideposition = -1;
    
        public DragAdapter(Context context, List<DragView> channelList,DragGrid grid) {
            this.context = context;
            this.channelList = channelList;
            this.grid = grid;
        }
    
        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return channelList == null ? 0 : channelList.size();
        }
    
        @Override
        public DragView getItem(int position) {
            // TODO Auto-generated method stub
            if (channelList != null && channelList.size() != 0) {
                return channelList.get(position);
            }
            return null;
        }
    
        @Override
        public long getItemId(int position) {
            // TODO Auto-generated method stub
            return position;
        }
    
        @Override
        public View getView(final int position, View convertView, ViewGroup parent) {
            HolderView holderView = null;
            View view = null;
            if (view == null) {
                holderView = new HolderView();
                view = LayoutInflater.from(context).inflate(R.layout.gridview_itemview, parent,false);
                holderView.iv_icon = (ImageView) view.findViewById(R.id.icon_iv);
                holderView.item_text = (TextView) view.findViewById(R.id.name_tv);
                holderView.iv_delete = (ImageView) view.findViewById(R.id.delet_iv);
    
                LayoutParams mLayoutParams = holderView.iv_icon.getLayoutParams();
                mLayoutParams.width = (int) (Common.Width / 7);
                mLayoutParams.height = (int) (Common.Width / 7);
                holderView.iv_icon.setLayoutParams(mLayoutParams);
    
                view.setTag(holderView);
            }
            holderView = (HolderView)view.getTag();
            final DragView iconInfo = getItem(position);
            holderView.iv_icon.setImageResource(iconInfo.getResid());
            holderView.item_text.setText(iconInfo.getName());
            holderView.iv_delete.setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    mHandler.post(new Runnable() {
    
                        @Override
                        public void run() {
                            // TODO Auto-generated method stub
                            //判断动画是否结束,没有结束则不能进行下一次的删除
                            if (!Common.isAnimaEnd) {
                                return;
                            }
                            notifyDataSetChanged();
                            grid.deleteInfo(position);
                        }
                    });
                }
            });
    
            if (position == getCount()-1){
                if (convertView == null) {
                    convertView = view;
                }
                convertView.setEnabled(false);
                convertView.setFocusable(false);
                convertView.setClickable(false);
            }
            //      DragView channel = getItem(position);
            //      
            //      item_text.setText(channel.getName());
            if (isChanged && (position == holdPosition) && !isItemShow) {
                holderView.item_text.setText("");
                holderView.item_text.setSelected(true);
                holderView.item_text.setEnabled(true);
                isChanged = false;
            }
            if (!isVisible && (position == -1 + channelList.size())) {
    
                holderView.item_text.setText("");
                holderView.item_text.setSelected(true);
                holderView.item_text.setEnabled(true);
            }
            if(remove_position == position){
                deletInfo(position);
            }
            if (!isDelete) {
                holderView.iv_delete.setVisibility(View.GONE);
            }
            else
            {
                if (!iconInfo.getName().equals("更多")) {
                    holderView.iv_delete.setVisibility(View.VISIBLE);
                }
            }
            if (hideposition == position) {
                view.setVisibility(View.INVISIBLE);
            }else {
                view.setVisibility(View.VISIBLE);
            }
            return view;
        }
    
        public void setisDelete(boolean isDelete)
        {
            this.isDelete = isDelete;
        }
    
        /**
         * 删除某个position
         * @param position
         */
        public void deletInfo(int position)
        {
            channelList.remove(position);
            hideposition = -1;
            notifyDataSetChanged();
        }
    
    
        /** 添加频道列表 */
        public void addItem(DragView channel) {
            channelList.add(channel);
            notifyDataSetChanged();
        }
    
        /** 拖动变更频道排序,在这里可以将变更后的顺序保存带数据库 */
        public void exchange(int dragPostion, int dropPostion) {
            holdPosition = dropPostion;
            DragView dragItem = getItem(dragPostion);
            Log.d(TAG, "startPostion=" + dragPostion + ";endPosition=" + dropPostion);
            if (dragPostion < dropPostion) {
                channelList.add(dropPostion + 1, dragItem);
                channelList.remove(dragPostion);
            } else {
                channelList.add(dropPostion, dragItem);
                channelList.remove(dragPostion + 1);
            }
            isChanged = true;
            notifyDataSetChanged();
        }
    
        /** 获取频道列表 */
        public List<DragView> getChannnelLst() {
            return channelList;
        }
    
        /** 设置删除的position */
        public void setRemove(int position) {
            remove_position = position;
            notifyDataSetChanged();
        }
    
        /** 删除频道列表 */
        public void remove() {
            channelList.remove(remove_position);
            remove_position = -1;
            notifyDataSetChanged();
        }
    
        /** 设置频道列表 */
        public void setListDate(List<DragView> list) {
            channelList = list;
        }
    
        /** 获取是否可见 */
        public boolean isVisible() {
            return isVisible;
        }
    
        /** 设置是否可见 */
        public void setVisible(boolean visible) {
            isVisible = visible;
        }
        /** 显示放下的ITEM */
        public void setShowDropItem(boolean show) {
            isItemShow = show;
        }
    
        public void setHidePosition(int position) {
            // TODO Auto-generated method stub
            this.hideposition = position;
            notifyDataSetChanged();
        }
    }

    适配器的注释也写得很明白了,我这里没有添加保存到数据库的代码,大家可以自行添加这里主要的就是exchange()方法,然后我们看下效果图:
    这里写图片描述

    怎么样,效果还不错吧,喜欢的话可以自己来实现一下,测试在Android5.1的模拟器上面,如果大家有什么不懂得,或者对此文章有疑问的欢迎留言讨论

    最后附上Demo下载地址

    如果大家觉得本文对您有所帮助,请投上您宝贵的一票,转载请注明出处http://blog.csdn.net/u010151514/article/details/51273485

    展开全文
  • 在ListView的xml文件中添加这个属性: android:cacheColorHint="#00000000" 转载于:https://www.cnblogs.com/wuyou/p/3516011.html
  • 无法添加原因:屏幕横向跨度不匹配,...在com.android.launcher3.widget.WidgetsPagedView类的onClick方法中点击添加小部件: @Override public void onClick(View v) { if (!mLauncher.isWidgetsViewVi...
  • Android GridView 中如何实现item合并,类似于桌面的应用图标效果,长按拖动,加入到另外一个已经存在的文件夹中,或者是压在另一个图标上,行成新的文件夹,并且两个图标都存在于新建的这个文件夹中。各位大神求...
  • 转载请声明出处,谢谢!...ok,现在简单说下我上面的图片被做了什么操作,长按“休闲场所”,然后代码实现 震动,告诉用户,现在可以移动了,然后我把它和“海滨沿岸” 互换位置,注意此时的 图片是 半透明的,这...
  • Android应用自动化过程中,会遇见需要长按拖动的场景,例如类似UC浏览器中,长按某个导航中的图标,使其处于可移动状态,然后再将其移动到另一个地方,与其它导航图标换个位置,在robotium中有个drag(float fromX,...
  • 一个Android库为提供长按并可拖动刷新recycler view items的功能
  • android自动化测试中实现长按拖动

    千次阅读 2013-10-28 20:26:02
    android应用自动化过程中,会遇见需要长按拖动的场景,例如类似UC浏览器中,长按某个导航中的图标,使其处于可移动状态,然后再将其移动到另一个地方,与其它导航图标换个位置,在robotium中有个drag(float fromX,...
  • 长按拖动item来布局的功能,源码PagedDragDropGrid,源码主要实现了我们可以长按界面上某个item时,item就会抖动,同时还可以拖动item来进行布局,以及重新排列位置等功能。 An Android ViewGroup that ...
  • 转载自:https://blog.csdn.net/yangbo437993234/article/details/51506895项目中遇见一个奇怪的需求就是搜索栏不可点击,可长按和拉着拖拽点拖动。一开始想着很简单嘛。屏蔽吊点击事件,搜索栏自带长按事件,差不多...
  • GridView 实现长按item拖动后,其它item自动移位效果 GridView 实现长按item拖动后,其它item自动移位效果 GridView 实现长按item拖动后,其它item自动移位效果 ...

空空如也

空空如也

1 2 3 4 5 ... 13
收藏数 257
精华内容 102
关键字:

android长按拖动