Android4.4-Launcher源码分析系列之关键的类和接口之DragLayer

原创 2016年05月31日 19:36:44

一、DragLayer布局

上一篇文章分析过Launcher的布局,它是最外层的布局

<!-- Full screen view projects under the status bar and contains the background -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:launcher="http://schemas.android.com/apk/res-auto/com.android.launcher3"
    android:id="@+id/launcher"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/workspace_bg" >

    <com.android.launcher3.DragLayer
        android:id="@+id/drag_layer"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <!-- The workspace contains 5 screens of cells -->
它继承自FrameLayout,它对应的类是DragLayer.

二、DragLayer代码分析

官方解释为:A ViewGroup that coordinates dragging across its descendants.意思是说它是一个协调处理它的子view拖动的容器.那么很明显,我们需要关注它对屏幕触摸事件的处理.它有三个方法onTouchEvent,onInterceptTouchEvent和onInterceptHoverEvent.在分析这三个方法之前,先看下DragLayer的构造函数,它重写了FrameLayout的一些属性.

 public DragLayer(Context context, AttributeSet attrs) {
        super(context, attrs);

        // 禁止多点触控
        setMotionEventSplittingEnabled(false);
        // 按指定顺序渲染子控件
        setChildrenDrawingOrderEnabled(true);
        // 侦听子view的add和remove事件
        setOnHierarchyChangeListener(this);
        //拖动图标到屏幕左边缘的图片
        mLeftHoverDrawable = getResources().getDrawable(R.drawable.page_hover_left_holo);
        //拖动图标到屏幕右边缘的图片
        mRightHoverDrawable = getResources().getDrawable(R.drawable.page_hover_right_holo);
    }

我已经在代码写上了注释,mLeftHoverDrawable是拖动桌面上的图标到屏幕左侧边缘时显示的图片,默认的图片实现的是高亮的效果,这里我把它替换为手指的图片,便于观看效果,如下图

不了解android屏幕事件传递机制的可以先去了解下.接下来讲上面提到的三个方法.

当点击屏幕时,会先进入onInterceptTouchEvent方法

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();

          if (action == MotionEvent.ACTION_DOWN) {
            if (handleTouchDown(ev, true)) {
            	 
            	return true;
            }
        } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            if (mTouchCompleteListener != null) {
                mTouchCompleteListener.onTouchComplete();
            }
            mTouchCompleteListener = null;
        }
        clearAllResizeFrames();
        return mDragController.onInterceptTouchEvent(ev);
    }
先判断action是不是down,点击事件必然会执行down,那么继续判断handleTouchDown(ev, true)是否为true,为true则拦截,这个方法是当正在缩放插件、打开文件夹、编辑文件夹名称的时候返回true.

 private boolean handleTouchDown(MotionEvent ev, boolean intercept) {
        Rect hitRect = new Rect();
        int x = (int) ev.getX();
        int y = (int) ev.getY();

        for (AppWidgetResizeFrame child: mResizeFrames) {
        	// 获得widget缩放框占据的矩形区域的矩形坐标
        	child.getHitRect(hitRect);
        	// 动作如果碰到了子控件。检查如果坐标处于缩放框可以对widget进行缩放操作的区域中,则禁止父控件再响应触控事件
        	if (hitRect.contains(x, y)) {
                if (child.beginResizeIfPointInRegion(x - child.getLeft(), y - child.getTop())) {
                    mCurrentResizeFrame = child;
                    mXDown = x;
                    mYDown = y;
                    requestDisallowInterceptTouchEvent(true);
                    return true;
                }
            }
        }
        // 获取当前打开的文件夹。
         Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
         if (currentFolder != null && !mLauncher.isFolderClingVisible() && intercept) {
        	 // 如果点击的是文件夹内区域并且正处于重命名状态,则取消重命名
        	if (currentFolder.isEditingName()) {
                if (!isEventOverFolderTextRegion(currentFolder, ev)) {
                    currentFolder.dismissEditingName();
                    return true;
                }
            }
        	getDescendantRectRelativeToSelf(currentFolder, hitRect);
            // 如果文件夹正在打开状态。那么如果点击的是文件夹以外区域则关闭文件夹。
            if (!isEventOverFolder(currentFolder, ev)) {
                mLauncher.closeFolder();
                return true;
            }
        }
        return false;
    }
判断触控事件是否经过了文件夹图标的文字区域的方法

private boolean isEventOverFolderTextRegion(Folder folder, MotionEvent ev) {
        getDescendantRectRelativeToSelf(folder.getEditTextRegion(), mHitRect);
        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
        	
        	return true;
        }
        return false;
    }
判断触控事件是否经过了文件夹图标区域的方法

private boolean isEventOverFolder(Folder folder, MotionEvent ev) {
        getDescendantRectRelativeToSelf(folder, mHitRect);
        if (mHitRect.contains((int) ev.getX(), (int) ev.getY())) {
            
        	return true;
        }
        return false;
    }

你们可以在里面加上打印或者Toast验证下,想出现文件夹,就把一个图标移动到另一个图标上面,自动出现文件夹.

在down下之后,如果handleTouchDown(ev, true)返回为false,则进入到clearAllResizeFrames方法,这个方法是清除widget的所有缩放框,

widget不是可以调整大小吗,当点击下去的时候就重置widget大小,该方法如下

         /**
	 * 清除widget的所有缩放框
	 */
    public void clearAllResizeFrames() {
        if (mResizeFrames.size() > 0) {
            for (AppWidgetResizeFrame frame: mResizeFrames) {
                frame.commitResize();
                removeView(frame);
            }
            mResizeFrames.clear();
        }
    }
执行完这个方法后会返回DragController的onInterceptTouchEvent值

return mDragController.onInterceptTouchEvent(ev);
如果你点击查看会发现,如果正在拖动图标则返回true,否则返回false.
当你点击下图标就松开,那么只执行onInterceptTouchEvent方法.当你点击图标之后拖动,那么接下来会执行onTouchEvent方法

@Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        int action = ev.getAction();
        int x = (int) ev.getX();
        int y = (int) ev.getY();

        if (action == MotionEvent.ACTION_DOWN) {
            if (handleTouchDown(ev, false)) {
            	return true;
            }
          } else if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
        	System.out.println("307");
        	if (mTouchCompleteListener != null) {
                mTouchCompleteListener.onTouchComplete();
            }
            mTouchCompleteListener = null;
            
        }

        if (mCurrentResizeFrame != null) {
            handled = true;
            switch (action) {
                case MotionEvent.ACTION_MOVE:
                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
                     break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    mCurrentResizeFrame.visualizeResizeForDelta(x - mXDown, y - mYDown);
                    mCurrentResizeFrame.onTouchUp();
                    mCurrentResizeFrame = null;
                   
            }
        }
        // 如果没有正在调节widget大小也没有打开文件夹,则进入正题,开始拖动
        if (handled) return true;
        return mDragController.onTouchEvent(ev);
    }
它会调用DragController的onTouchEvent方法.交给子view处理.

最后是这个onInterceptHoverEvent方法,意思是如果没有正在缩放插件、没有打开文件夹、没有编辑文件夹名称。 DragLayer将处理触控动作,

并取消一切widget的Resize动作(如果有)

    @Override
    public boolean onInterceptHoverEvent(MotionEvent ev) {
        if (mLauncher == null || mLauncher.getWorkspace() == null) {
            return false;
        }
        Folder currentFolder = mLauncher.getWorkspace().getOpenFolder();
        if (currentFolder == null) {
            return false;
        } else {
                AccessibilityManager accessibilityManager = (AccessibilityManager)
                        getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
            if (accessibilityManager.isTouchExplorationEnabled()) {
                final int action = ev.getAction();
                boolean isOverFolder;
                switch (action) {
                    case MotionEvent.ACTION_HOVER_ENTER:
                        isOverFolder = isEventOverFolder(currentFolder, ev);
                        if (!isOverFolder) {
                            sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                            mHoverPointClosesFolder = true;
                            return true;
                        } else if (isOverFolder) {
                            mHoverPointClosesFolder = false;
                        } else {
                            return true;
                        }
                    case MotionEvent.ACTION_HOVER_MOVE:
                        isOverFolder = isEventOverFolder(currentFolder, ev);
                        if (!isOverFolder && !mHoverPointClosesFolder) {
                            sendTapOutsideFolderAccessibilityEvent(currentFolder.isEditingName());
                            mHoverPointClosesFolder = true;
                            return true;
                        } else if (isOverFolder) {
                            mHoverPointClosesFolder = false;
                        } else {
                            return true;
                        }
                }
            }
        }
        return false;
    }
好了,关于DragLayer的介绍就这么多了,关于其它的一些细节,我会在后面上传注释后的Launcher源码的.


版权声明:本文为博主原创文章,转载请注明出处:邓志勇博客 http://blog.csdn.net/qq_31530015/

android Launcher——ui框架

1.先看图这是我正在研究的一个800*480的launcher的ui框架图,对应的桌面显示效果如下:下面一行应用是我修改桌面后的结果,在前面我的ui框架图中没有。闲话不多说,下面就一一做出讲解1.Dr...
  • stonecao
  • stonecao
  • 2011年06月02日 21:38
  • 26179

自定义控件三部曲之动画篇(五)——ValueAnimator高级进阶(一)

前言:唯有脚踏实地,才能厚积薄发,未来只属于为梦想而奋斗的人们,今天的你决定未来的自己。上一篇给大家介绍了ValueAnimator的大部分函数的用法,不过还都是些简单的用法,这篇我们带大家来看看有关...

Android动画学习(三)之使用ValueAnimator和ObjectAnimator实现动画实例

转载请注明出处:         上一篇博文我们初步地了解了Property Animation的工作方式及流程,并通过实验加深了对对象属性计算过程的理解,同时,我在文章的最后罗列一些实现P...

Android4.4-Launcher源码分析系列之关键的类和接口之DragScroller、DragController

一、DragScroller DragScroller是一个接口,顾名思义是控制滑动的接口,它定义了4个方法 public interface DragScroller { /** * 左...

Android4.4-Launcher源码分析系列之关键的类和接口之DropTarget、DragSource

一、DropTarget DropTarget是一个接口,它定义了一个可以接收被拖动对象的对象,意思是说实现了这个接口的对象是一个可以放置被拖动对象的容器. 实现了这个接口的有ButtonDr...

Android4.4-Launcher源码分析系列之WorkSpace及屏幕滑动

一.WorkSpace是什么 前面已经介绍了一个WorkSpace包含了多个CellLayout,再回忆下之前画过的图 WorkSpace是一个ViewGroup,它的布局如下 ...

Android4.4-Launcher源码分析系列之概述

这段时间研究了安卓4.4的Launcher源码,决定把自己的收获分享一下,欢迎各位留言. 把4.4的Launcher源码导入到eclipse中时会报一些错误,这是因为涉及到一些隐藏的api和资源.需要...

Android4.4-Launcher源码分析系列之搜索框/删除框

一、搜索框/删除框简介 搜索框在手机桌面上方,当拖动一个快捷方式图标时就会隐藏搜索框并且显示删除框.如下图所示 二、搜索框/删除框布局   屏幕上方的搜索和删除框是在一个布局里,名称为qsb...

Android4.4-Launcher源码分析系列之CellLayout

一.CellLayout是什么 在前面的 Android4.4-Launcher源码分析系列之Launcher介绍分析了Launcher的布局,CellLayout继承自ViewGroup, 一个Wo...

Android4.4-Launcher源码分析系列之Launcher启动简介

一、Launcher的启动 首先启动LauncherApplication,这里面只有两行代码,初始化了LauncherAppState,LauncherAppState主要是初始化一些对象,注册广播...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Android4.4-Launcher源码分析系列之关键的类和接口之DragLayer
举报原因:
原因补充:

(最多只允许输入30个字)