当前位置: 代码迷 >> Android >> android Launcher——拖放效能深入研究
  详细解决方案

android Launcher——拖放效能深入研究

热度:45   发布时间:2016-05-01 20:38:14.0
android Launcher——拖放功能深入研究

Luancher有一个相对比较复杂的功能就是拖放功能,要深入了解launcher,深入理解拖放功能是有必要的,这篇blog,我将对launcher的拖放功能做深入的了解
1.首先直观感受什么时候开始拖放?我们长按桌面一个应用图标或者控件的时候拖放就开始了,包括在all app view中长按应用图标,下面就是我截取的拖放开始的代码调用堆栈
at com.android.launcher2.DragController.startDrag(DragController.java:170)
at com.android.launcher2.Workspace.startDrag(Workspace.java:1068)
at com.android.launcher2.Launcher.onLongClick(Launcher.java:1683)
at android.view.View.performLongClick(View.java:2427)
at android.widget.TextView.performLongClick(TextView.java:7286)
at android.view.View$CheckForLongPress.run(View.java:8792)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
桌面应用图标由Launcher.onLongClick负责监听处理,插入断点debug进入onLongclick函数
? ?? ?? ?if (!(v instanceof CellLayout)) {
? ?? ?? ?? ?v = (View) v.getParent();
? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//获取桌面CellLayout上一个被拖动的对象
? ?? ?? ?CellLayout.CellInfo cellInfo = (CellLayout.CellInfo) v.getTag();
? ?? ?? ?? ?? ?...
? ?? ???if (mWorkspace.allowLongPress()) {
? ?? ?? ?? ?if (cellInfo.cell == null) {
? ?? ?? ?? ?? ? ...
? ?? ?? ?? ?} else {
? ?? ?? ?? ?? ? if (!(cellInfo.cell instanceof Folder)) {
? ?? ?? ?? ?? ?? ???...
? ?? ?? ?? ?? ?? ???//调用Workspace.startDrag处理拖动
? ?? ?? ?? ?? ?? ???mWorkspace.startDrag(cellInfo);
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?}
? ?? ???}
我上面只写出关键代码,首先是获取被拖动的对象v.getTag(),Tag什么时候被设置进去的了
? ?public boolean onInterceptTouchEvent(MotionEvent ev) {
? ?? ???...
? ?? ???if (action == MotionEvent.ACTION_DOWN) {
? ?? ?? ?? ?? ?? ?? ?? ?...
? ?? ?? ?? ?boolean found = false;
? ?? ?? ?? ?for (int i = count - 1; i >= 0; i--) {
? ?? ?? ?? ?? ? final View child = getChildAt(i);

? ?? ?? ?? ?? ? if ((child.getVisibility()) == VISIBLE || child.getAnimation() != null) {
? ?? ?? ?? ?? ?? ???child.getHitRect(frame);
? ?? ?? ?? ?? ?? ???//判断区域是否在这个子控件的区间,如果有把child信息赋给mCellInfo
? ?? ?? ?? ?? ?? ???if (frame.contains(x, y)) {
? ?? ?? ?? ?? ?? ?? ?? ?final LayoutParams lp = (LayoutParams) child.getLayoutParams();
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.cell = child;
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.cellX = lp.cellX;
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.cellY = lp.cellY;
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.spanX = lp.cellHSpan;
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.spanY = lp.cellVSpan;
? ?? ?? ?? ?? ?? ?? ?? ?cellInfo.valid = true;
? ?? ?? ?? ?? ?? ?? ?? ?found = true;
? ?? ?? ?? ?? ?? ?? ?? ?mDirtyTag = false;
? ?? ?? ?? ?? ?? ?? ?? ?break;
? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?}
? ?? ?? ?? ?
? ?? ?? ?? ?mLastDownOnOccupiedCell = found;

? ?? ?? ?? ?if (!found) {
? ?? ?? ?? ?? ?? ?? ?? ?? ? ...
? ?? ?? ?? ?? ?? ?? ?? ?? ? //没有child view 说明没有点击桌面图标项
? ?? ?? ?? ?? ? cellInfo.cell = null;? ?? ?? ?? ?? ?
? ?? ?? ?? ?}
? ?? ?? ?? ?setTag(cellInfo);
? ?? ???}
看了上面代码知道,当开始点击桌面时,celllayout就会根据点击区域去查找在该区域是否有child存在,若有把它设置为tag.cell,没有,tag.cell设置为null,后面在开始拖放时launcher.onlongclick中对tag进行处理,
这个理顺了,再深入到workspace.startDrag函数,workspace.startDrag调用DragController.startDrag去处理拖放
mDragController.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
再分析一下上面调用的几个参数
child = tag.cell
this = workspace
child.getTag()是什么呢?在什么时候被设置?再仔细回顾原来launcher加载过程代码,在launcher.createShortcut中它被设置了:注意下面我代码中的注释
? ? View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
? ?? ???TextView favorite = (TextView) mInflater.inflate(layoutResId, parent, false);

? ?? ???favorite.setCompoundDrawablesWithIntrinsicBounds(null,
? ?? ?? ?? ?? ? new FastBitmapDrawable(info.getIcon(mIconCache)),
? ?? ?? ?? ?? ? null, null);
? ?? ???favorite.setText(info.title);
? ?? ???//设置favorite(一个桌面Shortcut类型的图标)的tag
? ?? ???favorite.setTag(info);
? ?? ???favorite.setOnClickListener(this);

? ?? ???return favorite;
? ? }
继续深入解读DragController.startDrag函数
? ? public void startDrag(View v, DragSource source, Object dragInfo, int dragAction) {
? ?? ?? ?? ?//设置拖放源view
? ?? ?? ?? ?mOriginator = v;
? ?? ???//获取view的bitmap
? ?? ???Bitmap b = getViewBitmap(v);

? ?? ???if (b == null) {
? ?? ?? ?? ?// out of memory?
? ?? ?? ?? ?return;
? ?? ???}
? ?? ???//获取源view在整个屏幕的坐标
? ?? ???int[] loc = mCoordinatesTemp;
? ?? ???v.getLocationOnScreen(loc);
? ?? ???int screenX = loc[0];
? ?? ???int screenY = loc[1];
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//该函数功能解读请继续往下看
? ?? ???startDrag(b, screenX, screenY, 0, 0, b.getWidth(), b.getHeight(),
? ?? ?? ?? ?? ? source, dragInfo, dragAction);

? ?? ???b.recycle();
? ?? ???//设置原来view不可见
? ?? ???if (dragAction == DRAG_ACTION_MOVE) {
? ?? ?? ?? ?v.setVisibility(View.GONE);
? ?? ???}
? ? }

////////////////////////////////////////////////////////////
? ? public void startDrag(Bitmap b, int screenX, int screenY,
? ?? ?? ?? ?int textureLeft, int textureTop, int textureWidth, int textureHeight,
? ?? ?? ?? ?DragSource source, Object dragInfo, int dragAction) {
? ?? ???//隐藏软键盘
? ?? ???if (mInputMethodManager == null) {
? ?? ?? ?? ?mInputMethodManager = (InputMethodManager)
? ?? ?? ?? ?? ?? ???mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
? ?? ???}
? ?? ???mInputMethodManager.hideSoftInputFromWindow(mWindowToken, 0);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//mListener = deletezone,在blog laucher ui框架中有说明该函数,主要就是现实deletezone
? ?? ???if (mListener != null) {
? ?? ?? ?? ?mListener.onDragStart(source, dragInfo, dragAction);
? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//记住手指点击位置与屏幕左上角位置偏差
? ?? ???int registrationX = ((int)mMotionDownX) - screenX;
? ?? ???int registrationY = ((int)mMotionDownY) - screenY;

? ?? ???mTouchOffsetX = mMotionDownX - screenX;
? ?? ???mTouchOffsetY = mMotionDownY - screenY;

? ?? ???mDragging = true;
? ?? ???mDragSource = source;
? ?? ???mDragInfo = dragInfo;

? ?? ???mVibrator.vibrate(VIBRATE_DURATION);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//创建DragView对象
? ?? ???DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
? ?? ?? ?? ?? ? textureLeft, textureTop, textureWidth, textureHeight);
? ?? ???//显示Dragview对象
? ?? ???dragView.show(mWindowToken, (int)mMotionDownX, (int)mMotionDownY);
? ? }
到这里,拖放开始处理的框框基本清楚,但是DragView的创建和显示还有必要进一步深究
? ?? ???DragView dragView = mDragView = new DragView(mContext, b, registrationX, registrationY,
? ?? ?? ?? ?? ? textureLeft, textureTop, textureWidth, textureHeight);
//函数参数说明:
mContext = launcher
b = 根据拖放源view创建的大小一致的bitmap对象
registrationX = 手指点击位置与拖放源view 坐标x方向的偏移? ?? ???
registrationY = 手指点击位置与拖放源view 坐标y方向的偏移? ?? ???
textureLeft = 0
textureTop = 0
textureWidth = b.getWidth()
textureHeight =??b.getHeight()
//函数体
? ?? ???super(context);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//获取window管理器
? ?? ???mWindowManager = WindowManagerImpl.getDefault();
? ?? ???//一个动画,开始拖放时显示
? ?? ???mTween = new SymmetricalLinearTween(false, 110 /*ms duration*/, this);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//对源b 做一个缩放产生一个新的bitmap对象
? ?? ???Matrix scale = new Matrix();
? ?? ???float scaleFactor = width;
? ?? ???scaleFactor = mScale = (scaleFactor + DRAG_SCALE) / scaleFactor;
? ?? ???scale.setScale(scaleFactor, scaleFactor);

? ?? ???mBitmap = Bitmap.createBitmap(bitmap, left, top, width, height, scale, true);

? ?? ???// The point in our scaled bitmap that the touch events are located
? ?? ???mRegistrationX = registrationX + (DRAG_SCALE / 2);
? ?? ???mRegistrationY = registrationY + (DRAG_SCALE / 2);
其实函数很简单,就是记录一些参数,然后对view图片做一个缩放处理,并且准备一个tween动画,在长按桌面图标后图标跳跃到手指上显示该动画,了解这些,有助于理解函数dragView.show
//windowToken来自与workspace.onattchtowindow时候获取的view 所有attch的window标识,有这个参数,可以把dragview添加到
workspace所属的同一个window对象
//touchX,手指点击在屏幕的位置x
//touchy,手指点击在屏幕的位置y
? ? public void show(IBinder windowToken, int touchX, int touchY) {
? ?? ???WindowManager.LayoutParams lp;
? ?? ???int pixelFormat;

? ?? ???pixelFormat = PixelFormat.TRANSLUCENT;
? ?? ???//布局参数值的注意的是view位置参数,
? ?? ???//x=touchX-mRegistrationX=touchX-(registrationX + (DRAG_SCALE / 2))=手指点击位置-view坐标与手指点击位置偏差加上缩放值
? ?? ???lp = new WindowManager.LayoutParams(
? ?? ?? ?? ?? ? ViewGroup.LayoutParams.WRAP_CONTENT,
? ?? ?? ?? ?? ? ViewGroup.LayoutParams.WRAP_CONTENT,
? ?? ?? ?? ?? ? touchX-mRegistrationX, touchY-mRegistrationY,
? ?? ?? ?? ?? ? WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
? ?? ?? ?? ?? ? WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
? ?? ?? ?? ?? ?? ???| WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
? ?? ?? ?? ?? ?? ???/*| WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM*/,
? ?? ?? ?? ?? ? pixelFormat);
//? ?? ???lp.token = mStatusBarView.getWindowToken();
? ?? ???lp.gravity = Gravity.LEFT | Gravity.TOP;
? ?? ???lp.token = windowToken;
? ?? ???lp.setTitle("DragView");
? ?? ???mLayoutParams = lp;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//dragview的父类是Window,也就是说dragview可以拖放到屏幕的任意位置
? ?? ???mWindowManager.addView(this, lp);

? ?? ???mAnimationScale = 1.0f/mScale;
? ?? ???//播放开始拖动动画(直观感觉是图标变大了)
? ?? ???mTween.start(true);
? ? }

2,拖放过程
拖放过程的处理需要深入了解DragController.onTouchEvent(MotionEvent ev)函数的实现,我下面列出关键的MotionEvent.ACTION_MOVE部分代码并作出注释说明
? ?? ?? ?? ?? ?? ?? ?? ?case MotionEvent.ACTION_MOVE:
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???// 根据手指坐标移动dragview
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???mDragView.move((int) ev.getRawX(), (int) ev.getRawY());

? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???// 根据手指所在屏幕坐标获取目前所在的拖放目的view
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???final int[] coordinates = mCoordinatesTemp;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???DropTarget dropTarget = findDropTarget(screenX, screenY, coordinates);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???// 根据不同状态调用DropTarget的生命周期处理函数
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (dropTarget != null) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (mLastDropTarget == dropTarget) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? } else {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?if (mLastDropTarget != null) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???} else {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (mLastDropTarget != null) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1], (int) mTouchOffsetX,
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???mLastDropTarget = dropTarget;

? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//判断是否在delete区域
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???boolean inDeleteRegion = false;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (mDeleteRegion != null) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? inDeleteRegion = mDeleteRegion.contains(screenX, screenY);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?//不在delete区域,在左边切换区
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (!inDeleteRegion && screenX < SCROLL_ZONE) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (mScrollState == SCROLL_OUTSIDE_ZONE) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollState = SCROLL_WAITING_IN_ZONE;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollRunnable.setDirection(SCROLL_LEFT);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//不在delete区,在右边切换区
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???else if (!inDeleteRegion && screenX > scrollView.getWidth() - SCROLL_ZONE) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (mScrollState == SCROLL_OUTSIDE_ZONE) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollState = SCROLL_WAITING_IN_ZONE;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollRunnable.setDirection(SCROLL_RIGHT);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//在delete区域
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???else {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? if (mScrollState == SCROLL_WAITING_IN_ZONE) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollState = SCROLL_OUTSIDE_ZONE;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mScrollRunnable.setDirection(SCROLL_RIGHT);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?mHandler.removeCallbacks(mScrollRunnable);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}

? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???break;
拖放过程总的处理思路就是根据当前坐标位置获取dropTarget的目标位置,然后又根据相关状态和坐标位置调用dropTarget的对应生命周期函数,这里面有两个点需要进一步深入了解,一是查找dropTarget:findDropTarget(screenX, screenY, coordinates),二是mHandler.postDelayed(mScrollRunnable, SCROLL_DELAY);
--1.findDropTarget
? ? private DropTarget findDropTarget(int x, int y, int[] dropCoordinates) {
? ?? ???final Rect r = mRectTemp;
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???//mDropTargets是一个拖放目标view别表,在laucher初始化等被添加
? ?? ???final ArrayList<DropTarget> dropTargets = mDropTargets;
? ?? ???final int count = dropTargets.size();
? ?? ???//遍历dropTargets列表,查看{x,y}是否落在dropTarget坐标区域,若是,返回dropTarget。
? ?? ???for (int i=count-1; i>=0; i--) {
? ?? ?? ?? ?final DropTarget target = dropTargets.get(i);
? ?? ?? ?? ?target.getHitRect(r);
? ?? ?? ?? ?//获取target左上角屏幕坐标
? ?? ?? ?? ?target.getLocationOnScreen(dropCoordinates);
? ?? ?? ?? ?r.offset(dropCoordinates[0] - target.getLeft(), dropCoordinates[1] - target.getTop());
? ?? ?? ?? ?if (r.contains(x, y)) {
? ?? ?? ?? ?? ? dropCoordinates[0] = x - dropCoordinates[0];
? ?? ?? ?? ?? ? dropCoordinates[1] = y - dropCoordinates[1];
? ?? ?? ?? ?? ? return target;
? ?? ?? ?? ?}
? ?? ???}
? ?? ???return null;
? ? }
--2.mScrollRunnable
//看mScrollRunnable对象的构造类,通过setDirection设置滚动方向,然后通过一步调用DragScroller.scrollLeft/scrollRight来对桌面进行向左向右滚动,想深入了解如何实现的,敬请阅读我相关blogauncher——桌面移动详解
? ? private class ScrollRunnable implements Runnable {
? ?? ???private int mDirection;

? ?? ???ScrollRunnable() {
? ?? ???}

? ?? ???public void run() {
? ?? ?? ?? ?if (mDragScroller != null) {
? ?? ?? ?? ?? ? if (mDirection == SCROLL_LEFT) {
? ?? ?? ?? ?? ?? ???mDragScroller.scrollLeft();
? ?? ?? ?? ?? ? } else {
? ?? ?? ?? ?? ?? ???mDragScroller.scrollRight();
? ?? ?? ?? ?? ? }
? ?? ?? ?? ?? ? mScrollState = SCROLL_OUTSIDE_ZONE;
? ?? ?? ?? ?}
? ?? ???}

? ?? ???void setDirection(int direction) {
? ?? ?? ?? ?mDirection = direction;
? ?? ???}
? ? }
3.拖放结束,入口还是在DragController.onTouchEvent(MotionEvent ev)
? ?? ???先看调用堆栈:
at com.android.launcher2.DragController.endDrag(DragController.java:315)
at com.android.launcher2.DragController.onTouchEvent(DragController.java:471)
at com.android.launcher2.DragLayer.onTouchEvent(DragLayer.java:64)
at android.view.View.dispatchTouchEvent(View.java:3766)
? ?? ???onTouchEvent关键代码:
? ?? ?? ?? ?? ?? ?? ?? ?case MotionEvent.ACTION_UP:
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???mHandler.removeCallbacks(mScrollRunnable);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???if (mDragging) {
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? // 拖动过程手指离开屏幕
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ? drop(screenX, screenY);
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???}
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???endDrag();
? ?? ?? ?? ?? ?? ?? ?? ?? ?? ???break;
--1.drop(screenX, screenY);
? ?? ???final int[] coordinates = mCoordinatesTemp;
? ?? ???//获取dropTarget对象
? ?? ???DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
? ?? ???//coordinates=点触点在dropTarget 中的xy坐标

? ?? ???if (dropTarget != null) {
? ?? ?? ?? ?dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
? ?? ?? ?? ?? ?? ???(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ?? ???//根据相关参数判断是否可dropTarget是否接受该drag view
? ?? ?? ?? ?if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1],
? ?? ?? ?? ?? ?? ???(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo)) {
? ?? ?? ?? ?? ? dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1],
? ?? ?? ?? ?? ?? ?? ?? ?(int) mTouchOffsetX, (int) mTouchOffsetY, mDragView, mDragInfo);
? ?? ?? ?? ?? ? mDragSource.onDropCompleted((View) dropTarget, true);
? ?? ?? ?? ?? ? return true;
? ?? ?? ?? ?} else {
? ?? ?? ?? ?? ? mDragSource.onDropCompleted((View) dropTarget, false);
? ?? ?? ?? ?? ? return true;
? ?? ?? ?? ?}
? ?? ???}
? ?? ???return false

  相关解决方案