zl程序教程

您现在的位置是:首页 >  其它

当前栏目

gradeview可拖动效果实现

实现 效果 拖动
2023-09-14 08:58:19 时间

刚开始接触这个的时候,不知道要如何实现,去网上翻了一大堆资料,懂了个大概,就是目前可以找到的都是拖拽的时候,不带移动动画的,和线上的客户端交互效果相差甚远,在反反复复的尝试查看相关东西,大致的做了出来,目前在模拟器上似乎有一点小BUG,真机测试没有问题,就先放上来,如果发现问题在修改优化。代码反面,没有好好的修改调整,可能会有点乱,请见谅哈。


二、开发前的准备

1.了解重写View的相关知识,并且知道GridView的一些内部方法,如:怎么通过触摸的坐标获取对应的position等(这里我采用的是继承GridView控件)

2.了解屏幕触摸动作传递原理    这里我以前转载的一篇或许会有帮助:Android事件分发机制完全解析,带你从源码的角度彻底理解(全)

3.了解位移动画Animation,本DEMO中主要用到:TranslateAnimation  平移动画

4.了解WindowManager的窗口机制,这里的item拖拽等都要设计到这个。

5.了解SQLiteDatabase 以及SQLiteOpenHelper等数据库操作相关的类,本DEMO中主要用到数据库进行存储频道信息,如果你要用文档进行存储读取也可以。


三、开发思路

1.  获取数据库中频道的列表,如果为空,赋予默认列表,并存入数据库,之后通过对应的适配器赋给对应的GridView


2.  2个GridView--(1.DragGrid   2. OtherGridView)

DragGrid 用于显示我的频道,带有长按拖拽效果

OtherGridView用于显示更多频道,不带推拽效果

注:由于屏幕大小不一定,外层使用ScrollView,所以2者都要重写计算高度


3.  点击2个GridView的时候,根据点击的Item对应的position,获取position对应的view,进行创建一层移动的动画层

起始位置:点击的positiongetLocationInWindow()获取。终点位置:另一个GridView的最后个ITEM 的position + 1的位置。

并赋予移动动画,等动画结束后对2者对应的频道列表进行数据的remove和add操作。


4.  设置点击和拖动的限制条件,如  推荐  这个ITEM是不允许用户操作的。


5.  拖动的DragGrid的操作:

(1)长按获取长按的ITEM的position  -- dragPosition 以及对应的view ,手指触摸屏幕的时候,调用onInterceptTouchEvent来获取MotionEvent.ACTION_DOWN事件,获取对应的数据。由于这里是继承了GridView,所以长按时间可以通过setOnItemLongClickListener监听来执行,或则你也可以通过计算点击时间来监听是否长按。

(2)通过onTouchEvent(MotionEvent ev)来监听手指的移动和抬起动作。当它移动到 其它的item下面,并且下方的item对应的position  不等于 dragPosition,进行数据交换,并且2者之间的所有item进行移动动画,动画结束后,数据更替刷新界面。

(3) 抬起手后,清除掉拖动时候创建的view,让GridView中的数据显示。


6.  退出时候,将改变后的频道列表存入数据库。


四、流程图

下面是大体的流程图:





    @Override       public void onItemClick(AdapterView ?  parent, final View view, final int position,long id) {           //如果点击的时候,之前动画还没结束,那么就让点击事件无效           if(isMove){               return;           }           switch (parent.getId()) {           case R.id.userGridView:               //position为 0,1 的不可以进行任何操作               if (position != 0   position != 1) {                   final ImageView moveImageView = getView(view);                   if (moveImageView != null) {                       TextView newTextView = (TextView) view.findViewById(R.id.text_item);                       final int[] startLocation = new int[2];                       newTextView.getLocationInWindow(startLocation);                       final ChannelItem channel = ((DragAdapter) parent.getAdapter()).getItem(position);//获取点击的频道内容                       otherAdapter.setVisible(false);                       //添加到最后一个                       otherAdapter.addItem(channel);                       new Handler().postDelayed(new Runnable() {                           public void run() {                               try {                                   int[] endLocation = new int[2];                                   //获取终点的坐标                                   otherGridView.getChildAt(otherGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);                                   MoveAnim(moveImageView, startLocation , endLocation, channel,userGridView);                                   userAdapter.setRemove(position);                               } catch (Exception localException) {                               }                           }                       }, 50L);                   }               }               break;           case R.id.otherGridView:               final ImageView moveImageView = getView(view);               if (moveImageView != null){                   TextView newTextView = (TextView) view.findViewById(R.id.text_item);                   final int[] startLocation = new int[2];                   newTextView.getLocationInWindow(startLocation);                   final ChannelItem channel = ((OtherAdapter) parent.getAdapter()).getItem(position);                   userAdapter.setVisible(false);                   //添加到最后一个                   userAdapter.addItem(channel);                   new Handler().postDelayed(new Runnable() {                       public void run() {                           try {                               int[] endLocation = new int[2];                               //获取终点的坐标                               userGridView.getChildAt(userGridView.getLastVisiblePosition()).getLocationInWindow(endLocation);                               MoveAnim(moveImageView, startLocation , endLocation, channel,otherGridView);                               otherAdapter.setRemove(position);                           } catch (Exception localException) {                           }                       }                   }, 50L);               }               break;           default:               break;           }       }  
span  private void MoveAnim(View moveView, int[] startLocation,int[] endLocation, final ChannelItem moveChannel,               final GridView clickGridView) {           int[] initLocation = new int[2];           //获取传递过来的VIEW的坐标           moveView.getLocationInWindow(initLocation);           //得到要移动的VIEW,并放入对应的容器中           final ViewGroup moveViewGroup = getMoveViewGroup();           final View mMoveView = getMoveView(moveViewGroup, moveView, initLocation);           //创建移动动画           TranslateAnimation moveAnimation = new TranslateAnimation(                   startLocation[0], endLocation[0], startLocation[1],                   endLocation[1]);           moveAnimation.setDuration(300L);//动画时间           //动画配置           AnimationSet moveAnimationSet = new AnimationSet(true);           moveAnimationSet.setFillAfter(false);//动画效果执行完毕后,View对象不保留在终止的位置           moveAnimationSet.addAnimation(moveAnimation);           mMoveView.startAnimation(moveAnimationSet);           moveAnimationSet.setAnimationListener(new AnimationListener() {                              @Override               public void onAnimationStart(Animation animation) {                   isMove = true;               }                              @Override               public void onAnimationRepeat(Animation animation) {               }                              @Override               public void onAnimationEnd(Animation animation) {                   moveViewGroup.removeView(mMoveView);                   // instanceof 方法判断2边实例是不是一样,判断点击的是DragGrid还是OtherGridView                   if (clickGridView instanceof DragGrid) {                       otherAdapter.setVisible(true);                       otherAdapter.notifyDataSetChanged();                       userAdapter.remove();                   }else{                       userAdapter.setVisible(true);                       userAdapter.notifyDataSetChanged();                       otherAdapter.remove();                   }                   isMove = false;               }           });       } /span   
    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) {           mVibrator = (Vibrator) context                   .getSystemService(Context.VIBRATOR_SERVICE);           // 将布局文件中设置的间距dip转为px           mHorizontalSpacing = DataTools.dip2px(context, mHorizontalSpacing);       }       @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();               setOnItemClickListener(ev);           }           return super.onInterceptTouchEvent(ev);       }       @Override       public boolean onTouchEvent(MotionEvent ev) {           // TODO Auto-generated method stub           boolean bool = true;           if (dragImageView != null                     dragPosition != AdapterView.INVALID_POSITION) {               // 移动时候的对应x,y位置               bool = super.onTouchEvent(ev);               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);                   }                   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);       }       /** 在拖动的情况 */       private void onDrag(int x, int y, int rawx, int rawy) {           if (dragImageView != null) {               windowParams.alpha = 0.6f;               windowParams.x = rawx - win_view_x;               windowParams.y = rawy - win_view_y;               windowManager.updateViewLayout(dragImageView, windowParams);           }       }       /** 在松手下放的情况 */       private void onDrop(int x, int y) {           // 根据拖动到的x,y坐标获取拖动位置下方的ITEM对应的POSTION           int tempPostion = pointToPosition(x, y);           dropPosition = tempPostion;           DragAdapter mDragAdapter = (DragAdapter) getAdapter();           // 显示刚拖动的ITEM           mDragAdapter.setShowDropItem(true);           // 刷新适配器,让对应的ITEM显示           mDragAdapter.notifyDataSetChanged();       }       /**       * 长按点击监听       * @param ev       */       public void setOnItemClickListener(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位置                   startPosition = position;// 第一次点击的postion                   dragPosition = position;                   if (startPosition  = 1) {                       return false;                   }                   ViewGroup dragViewGroup = (ViewGroup) getChildAt(dragPosition                           - getFirstVisiblePosition());                   TextView dragTextView = (TextView) dragViewGroup                           .findViewById(R.id.text_item);                   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;           // 得到preview左上角相对于屏幕的坐标           windowParams.x = x - win_view_x;           windowParams.y = y - win_view_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   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 % 4 == 0) {                               to_x = 3 * 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) % 4 == 0) {                               to_x = -3 * 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;                               }                           }                       });                   }               }           }       }  
public class SQLHelper extends SQLiteOpenHelper {       public static final String DB_NAME = "database.db";// 数据库名称       public static final int VERSION = 1;              public static final String TABLE_CHANNEL = "channel";//数据表        public static final String ID = "id";//       public static final String NAME = "name";       public static final String ORDERID = "orderId";       public static final String SELECTED = "selected";       private Context context;       public SQLHelper(Context context) {           super(context, DB_NAME, null, VERSION);           this.context = context;       }       public Context getContext(){           return context;       }              @Override       public void onCreate(SQLiteDatabase db) {           // TODO 创建数据库后,对数据库的操作           String sql = "create table if not exists "+TABLE_CHANNEL +                   "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +                   ID + " INTEGER , " +                   NAME + " TEXT , " +                   ORDERID + " INTEGER , " +                   SELECTED + " SELECTED)";           db.execSQL(sql);       }       @Override       public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {           // TODO 更改数据库版本的操作           onCreate(db);       }  
!-- 在SDCard中创建与删除文件权限 --    uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /    !-- 往SDCard写入数据权限 --    uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /    !-- 震动权限 --    uses-permission android:name="android.permission.VIBRATE"/