可拖拽排序的GridView

来源:互联网 发布:张忻铃 知乎 编辑:程序博客网 时间:2024/05/17 08:36

根据两位大神的代码整理了下

         1)可以拖动排序,Adapter里面复用了convertView,但是拖动过程中的动画不是很理想

         2)找不到在哪了,但是拖动中的动画还不错

直接上代码了

1、DragView

public class DragGridView extends GridView {/** * DragGridView的item长按响应的时间, 默认是1000毫秒,也可以自行设置 */private long dragResponseMS = 700;/** * 是否可以拖拽,默认不可以 */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 = 80;/** * item发生变化回调的接口 */private OnChanageListener onChanageListener;private int mNumColumns = 3;private boolean mAnimationEnd = true;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); // 获取状态栏的高度}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);}};/** * 设置回调接口 *  * @param onChanageListener */public void setOnChangeListener(OnChanageListener onChanageListener) {this.onChanageListener = onChanageListener;}/** * 设置响应拖拽的毫秒数,默认是1000毫秒 *  * @param dragResponseMS */public void setDragResponseMS(long dragResponseMS) {this.dragResponseMS = dragResponseMS;}@Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {switch (ev.getAction()) {case MotionEvent.ACTION_DOWN:// 使用Handler延迟dragResponseMS执行mLongClickRunnablemHandler.postDelayed(mLongClickRunnable, dragResponseMS);mDownX = (int) ev.getX();mDownY = (int) ev.getY();// 根据按下的X,Y坐标获取所点击item的positionmDragPosition = pointToPosition(mDownX, mDownY);if (mDragPosition == AdapterView.INVALID_POSITION) {return super.dispatchTouchEvent(ev);}// 根据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() / 4;// 获取DragGridView自动向下滚动的偏移量,大于这个值,DragGridView向上滚动mUpScrollBorder = getHeight() * 3 / 4;// 开启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);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 (moveY > mUpScrollBorder) {scrollY = -speed;mHandler.postDelayed(mScrollRunnable, 25);} else if (moveY < mDownScrollBorder) {scrollY = speed;mHandler.postDelayed(mScrollRunnable, 25);} else {scrollY = 0;mHandler.removeCallbacks(mScrollRunnable);}// 当我们的手指到达GridView向上或者向下滚动的偏移量的时候,可能我们手指没有移动,但是DragGridView在自动的滚动// 所以我们在这里调用下onSwapItem()方法来交换itemonSwapItem(moveX, moveY);View view = getChildAt(mDragPosition - getFirstVisiblePosition());if (view != null) {// 实现GridView的自动滚动smoothScrollToPositionFromTop(mDragPosition, view.getTop()+ scrollY);}}};/** * 交换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) {getChildAt(tempPosition - getFirstVisiblePosition()).setVisibility(View.INVISIBLE);// 拖动到了新的item,新的item隐藏掉getChildAt(mDragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);// 之前的item显示出来if (onChanageListener != null) {onChanageListener.onChange(mDragPosition, tempPosition);}// mDragPosition = tempPosition;animateReorder(mDragPosition, tempPosition);mDragPosition = 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() {@Overridepublic void onAnimationStart(Animator animation) {mAnimationEnd = false;}@Overridepublic void onAnimationEnd(Animator animation) {mAnimationEnd = true;}});resultSet.start();}/** * 停止拖拽我们将之前隐藏的item显示出来,并将镜像移除 */private void onStopDrag() {getChildAt(mDragPosition - getFirstVisiblePosition()).setVisibility(View.VISIBLE);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 interface OnChanageListener {/** * 当item交换位置的时候回调的方法,我们只需要在该方法中实现数据的交换即可 *  * @param form *            开始的position * @param to *            拖拽到的position */public void onChange(int form, int to);}}

2、MainActivity,注意红色部分

public class MainActivity extends Activity {private List<HashMap<String, Object>> dataSourceList = new ArrayList<HashMap<String, Object>>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);DragGridView mDragGridView = (DragGridView) findViewById(R.id.dragGridView);for (int i = 0; i < 30; i++) {HashMap<String, Object> itemHashMap = new HashMap<String, Object>();itemHashMap.put("item_image",R.drawable.com_tencent_open_notice_msg_icon_big);itemHashMap.put("item_text", "拖拽 " + Integer.toString(i));dataSourceList.add(itemHashMap);}final SimpleAdapter mSimpleAdapter = new SimpleAdapter(this, dataSourceList,R.layout.grid_item, new String[] { "item_image", "item_text" },new int[] { R.id.item_image, R.id.item_text });final DragAdapter dragAdapter = new DragAdapter(this, dataSourceList);mDragGridView.setAdapter(dragAdapter);<span style="color:#ff0000;">mDragGridView.setOnChangeListener(new OnChanageListener() {@Overridepublic void onChange(int from, int to) {HashMap<String, Object> temp = dataSourceList.get(from);//直接交互item//dataSourceList.set(from, dataSourceList.get(to));//dataSourceList.set(to, temp);//这里的处理需要注意下if(from < to){for(int i=from; i<to; i++){Collections.swap(dataSourceList, i, i+1);}}else if(from > to){for(int i=from; i>to; i--){Collections.swap(dataSourceList, i, i-1);}}dataSourceList.set(to, temp);dragAdapter.notifyDataSetChanged();}});</span>}}

3、Adapter 和普通的Adapter类似

public class DragAdapter extends BaseAdapter{    private final List<HashMap<String, Object>> list;    private final LayoutInflater mInflater;    private int mHidePosition = -1;    public DragAdapter(Context context, List<HashMap<String, Object>> list) {        this.list = list;        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;    }        @Override    public View getView(int position, View convertView, ViewGroup parent) {    ViewHolder holder = null;    if(convertView == null){    convertView = mInflater.inflate(R.layout.grid_item, null);    holder = new ViewHolder();    holder.mImageView = (ImageView) convertView    .findViewById(R.id.item_image);    holder.mTextView = (TextView) convertView    .findViewById(R.id.item_text);    convertView.setTag(holder);    }else{    holder = (ViewHolder) convertView.getTag();    }    holder.mImageView.setImageResource((Integer) list.get(position).get(                "item_image"));    holder.mTextView.setText((CharSequence) list.get(position).get("item_text"));        if (position == mHidePosition) {            convertView.setVisibility(View.INVISIBLE);        }        return convertView;    }        private class ViewHolder{    ImageView mImageView;TextView mTextView;    }}

源码下载






0 0
原创粉丝点击