zoukankan      html  css  js  c++  java
  • Launcher 拖拽 流程小结『android 2.3 2.2』

    问:尼玛Android 4.1Jelly Bean都发布了,你还bb2.3,坑爹呢,这是?

    答:这个真不好意思了,屌丝的特点就是后知后觉。

    问:那有何用?

    答:可以很不负责任的说,从2.2以后launcher 拖拽流程基本没变化。

    问:基本?那还是有变化,到底还是坑爹。

    答:“好吧,你赢了”。


    -------------------------------------------------------------------------------------------------------------我是风格线--------

    回归正题,要是做launcher的话,那拖拽事件处理、响应是必须得撸清楚的:

    先来一张Launcher拖拽的时序图,以便对整个流程有个大概的把握(看不明白也没关系,跳过看下面先,回头再看就so easy了):


    下面分步骤详细说明:

    一,事件产生

    拖拽事件的产生基于用户长按操作,分两种情况:

    • 长按桌面(workspace视图)上的子视图(应用图标,文件夹,widget)事件响应是launcher.java

     public boolean onLongClick(View v)
        {
          ...........
                else 
                {
                    if (!(cellInfo.cell instanceof Folder)) 
                    {
                        // 用户长按某一组件时调用
                        mWorkspace.startDrag(cellInfo);
                    }
                }
            }
            return true;
        }

    F2跳转之:

       void startDrag(CellLayout.CellInfo cellInfo)
        {
            View child = cellInfo.cell;
    
            // Make sure the drag was started by a long press as opposed to a long click.
            // Note that Search takes focus when clicked rather than entering touch mode
          ..........
            mDragger.startDrag(child, this, child.getTag(), DragController.DRAG_ACTION_MOVE);
            invalidate();
            clearVacantCache();
        }

    • 长按应用主菜单(AllAppGridView视图)上的子视图(应用图标)事件响应是AllAppGridView.java

    public boolean onItemLongClick(AdapterView<?> parent, View view,
    			int position, long id) 
    	{
    		if (!view.isInTouchMode()) 
    		{
    			return false;
    		}
    		ApplicationInfo app = (ApplicationInfo) parent
    				.getItemAtPosition(position);
    		app = new ApplicationInfo(app);
                   //用户长按应用主菜单应用图标时调用
    		mDragger.startDrag(view, this, app, DragController.DRAG_ACTION_COPY);	
    	}

    该步骤的事件产生最终都定位mDragger.startDrag方法,顺着它往下摸就行了。

    二,mDragger为何物?

    mDragger是DragController实例,我们F3可以看到DragController其实就是一个接口,里面有几个很眼熟的抽象方法

    F4一下可以知道该接口的唯一子类是DragLayer.java,必须进去一探究竟,尼玛这玩意竟然继承了frameLayout,脑子里有东西闪过,soga,原来是这样:

    <com.android.launcher2.DragLayer
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:launcher="http://schemas.android.com/apk/res/com.android.launcher"
    
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <!-- Keep these behind the workspace so that they are not visible when
             we go into AllApps -->
        <include
    ...........

    上面的代码就是launcher的主布局文件。DragLayer作为整个launcher的RootView负责所有拖拽事件的统一分发处理。因为launcher上能看到的组件都布局在它之上

    为啥它是frameLayout,我就不罗嗦了。

    当用户长按某一launcher组件时,DragLayer.startDrag会扯开嗓子通知那些相关组件做好接受拖拽事件的准备,并顺便悄悄的隐藏被拖拽图标的原始view。

    三,既然已经跟到frameLayout视图,那么接下来应该是顺着触屏时序处理来看比较直观。

     public boolean onInterceptTouchEvent(MotionEvent ev) 
        {
          ..........
                case MotionEvent.ACTION_UP:
                    if (mShouldDrop && drop(x, y)) 
                    {
                        mShouldDrop = false;
                    }
                    endDrag();
                    break;
            }
    
            return mDragging;
        }
    onInterceptTouchEvent作为触屏事件的节奏“打断者”在这里并没啥作为,只是在up时调用endDrag,用来在合适的时候显示刚才隐藏的view。

    老大不管事,那自然小弟抗大梁了。看看onTouchEvent是如何有条不紊的处理这些拖拽事件的。

     public boolean onTouchEvent(MotionEvent ev) 
        {
             .........
            switch (action) 
            {
            case MotionEvent.ACTION_DOWN:
    
                // 初始化mScrollState状态。
                    mScrollState = SCROLL_OUTSIDE_ZONE;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                // 不断更新当前icon的位置。
                rect.union(left - 1, top - 1, left + width + 1, top + height + 1);
                final int[] coordinates = mDropCoordinates;
                //调用findDropTarget来寻找落脚点。比如是deleteZone区域,就触发相关的事件响应。
                DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
               ........
                        dropTarget.onDragOver(mDragSource, coordinates[0], coordinates[1],        
                        mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
                        dropTarget.onDragEnter(mDragSource, coordinates[0], coordinates[1],
                        mLastDropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
               ........
                if (!inDragRegion && x < SCROLL_ZONE) 
                {
                    if (mScrollState == SCROLL_OUTSIDE_ZONE)
                    {
                        mScrollState = SCROLL_WAITING_IN_ZONE;
                        mScrollRunnable.setDirection(SCROLL_LEFT);
                //拖着图标往左边换屏幕。
                        postDelayed(mScrollRunnable, SCROLL_DELAY);
                    }
                } 
                else if (!inDragRegion && x > getWidth() - SCROLL_ZONE)
                {
                    if (mScrollState == SCROLL_OUTSIDE_ZONE) 
                    {
                        mScrollState = SCROLL_WAITING_IN_ZONE;
                        mScrollRunnable.setDirection(SCROLL_RIGHT);
               //拖着图标往右边换屏幕。
    	            postDelayed(mScrollRunnable, SCROLL_DELAY); 
                   }
               }
               .......
            case MotionEvent.ACTION_UP:
                removeCallbacks(mScrollRunnable);
                if (mShouldDrop)
                 {
              //秋后算总账了,目标view是否接受该source view,还需drop(x,y)作最后定夺。
                    drop(x, y);
                    mShouldDrop = false;
                }
                endDrag();
                break;
            case MotionEvent.ACTION_CANCEL:
                endDrag();
            }
            return true;
        }

    Action_move事件里要特别注意一下findDropTarget方法的实现,该事件会不断调用dropTarget的那几个抽象方法,只要是实现了该接口的view(userFolder,DeleteZone,workspace)都会根据特定的条件调用相应的重载方法,从而作出像文件夹开合,deleteZone变色等响应。

    Action_up事件中咱们很熟练的F3,进了drop方法(太重要了,它决定了该拖拽事件最终的结果,是目标view接受了它还是拒绝都在这里都到裁决)。但代码其实很简短:

     private boolean drop(float x, float y)
        {
            invalidate();
            final int[] coordinates = mDropCoordinates;
            //如果上面findDropTarget是寻找落脚点,这里就是准备托付终身了。
            DropTarget dropTarget = findDropTarget((int) x, (int) y, coordinates);
            if (dropTarget != null) 
            {
            	if(shouldAccept == false && dropTarget.toString() == "ActionButton")
            	{
            		return false;
            	}
                dropTarget.onDragExit(mDragSource, coordinates[0], coordinates[1],
                        (int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
                //如果这里的acceptDrop返回ture,那就意味着得到了男方父母的首肯,终于找到依附的view了。
                if (dropTarget.acceptDrop(mDragSource, coordinates[0], coordinates[1],
                        (int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo)) {
                    dropTarget.onDrop(mDragSource, coordinates[0], coordinates[1],
                            (int) mTouchOffsetX, (int) mTouchOffsetY, mDragInfo);
                    mDragSource.onDropCompleted((View) dropTarget, true);
                    return true;
                } 
                else 
                {
                    mDragSource.onDropCompleted((View) dropTarget, false);
                    return true;
                }
            }       
            return false;
        }


    其实去年已经看这块了,怎奈人搓且奇懒无比,今天总算做个了结。










  • 相关阅读:
    mdx 根据维度Hierarchy节点的名字来filter节点,搜索节点
    学习C++.Primer.Plus 8 函数探幽
    学习C++.Primer.Plus 7 函数
    学习C++.Primer.Plus 6 分支语句和逻辑操作符
    学习C++.Primer.Plus 5 循环和关系表达式
    学习C++.Primer.Plus 4 复合类型
    NYoj_171聪明的kk
    NYoj_104最大和
    希尔排序
    NYoj_49开心的小明
  • 原文地址:https://www.cnblogs.com/aaa2832/p/2594034.html
Copyright © 2011-2022 走看看