Android4.4-Launcher源码分析系列之关键的类和接口之DragLayer
2016-05-31 19:36
465 查看
一、DragLayer布局
上一篇文章分析过Launcher的布局,它是最外层的布局
二、DragLayer代码分析
官方解释为:A ViewGroup that coordinates dragging across its descendants.意思是说它是一个协调处理它的子view拖动的容器.那么很明显,我们需要关注它对屏幕触摸事件的处理.它有三个方法onTouchEvent,onInterceptTouchEvent和onInterceptHoverEvent.在分析这三个方法之前,先看下DragLayer的构造函数,它重写了FrameLayout的一些属性.
我已经在代码写上了注释,mLeftHoverDrawable是拖动桌面上的图标到屏幕左侧边缘时显示的图片,默认的图片实现的是高亮的效果,这里我把它替换为手指的图片,便于观看效果,如下图
不了解android屏幕事件传递机制的可以先去了解下.接下来讲上面提到的三个方法.
当点击屏幕时,会先进入onInterceptTouchEvent方法
你们可以在里面加上打印或者Toast验证下,想出现文件夹,就把一个图标移动到另一个图标上面,自动出现文件夹.
在down下之后,如果handleTouchDown(ev, true)返回为false,则进入到clearAllResizeFrames方法,这个方法是清除widget的所有缩放框,
widget不是可以调整大小吗,当点击下去的时候就重置widget大小,该方法如下
当你点击下图标就松开,那么只执行onInterceptTouchEvent方法.当你点击图标之后拖动,那么接下来会执行onTouchEvent方法
最后是这个onInterceptHoverEvent方法,意思是如果没有正在缩放插件、没有打开文件夹、没有编辑文件夹名称。 DragLayer将处理触控动作,
并取消一切widget的Resize动作(如果有)
上一篇文章分析过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源码的.
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories