- 浏览: 105652 次
- 性别:
- 来自: 贵州
最新评论
-
ldci3gandroid:
受用,3Q。
如果程序员没了手! -
love_you:
,很好的代码
android GPS获取地理信息(弥补Geocoder不足之处) -
li172219244:
大哥,我下载了,为啥包很多的错啊,android.conten ...
android静默安装应用程序 -
kaikai7918:
大神,求android.content.pm.IPackage ...
android静默安装应用程序 -
kaikai7918:
大神,求android.content.pm.IPackage ...
android静默安装应用程序
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
发表评论
-
特效的一些基本参数
2013-02-04 12:38 1088角度和弧度 今天写程序遇到圆的参数方程,什么角度和弧度 ... -
Android Camera使用Matrix进行滑动特效变换
2013-01-19 13:05 6443以下所自行找到资料,如有抄作请尽量 Android 使用 ... -
TextView 小结
2013-01-03 22:20 1697Android的TextView使用Html来处理图片显示、字 ... -
java
2012-09-13 13:28 4由于本人发觉很多群的人很少、技术问题不够及时回答、本人现创建群 ... -
android添加桌面快捷方式
2012-07-30 15:19 1098封装类、直接放到项目中去就可以用 记在主配置文件添加权限 ... -
屏蔽launcher桌面应用
2012-07-30 14:14 3322今天接到一个需求、需求如下、 默认开机以后屏蔽所有界面,待机 ... -
android GPS获取地理信息(弥补Geocoder不足之处)
2012-07-19 15:57 7394最近做一个社交软件、需要用到GPS、但是过程中有反向编码、也 ... -
(转)MeasureSpec
2012-05-12 12:54 927android的布局分两个阶段,先measure()后re ... -
android上一些方法的区别和用法的注意事项
2012-05-09 16:59 916onMeasure()和onLayout() 1. onMea ... -
android:修改preference中view属性
2012-05-09 12:54 963android开发包preference提供了我们一些方面的设 ... -
android动画之最总总结
2012-04-19 19:59 1145动画总结:不管什么动画、透明还是大小、位置、四个动画可以改变的 ... -
如何在Ubuntu编译Android2.3源代码以及制作自己的SDK
2012-04-17 17:40 2224经过2两周断续的搜索网上编译的东西、得到了有用的编译之道 ... -
Android 怎么样改变图片颜色
2012-03-20 22:50 6503先说一下调色的一些基 ... -
android静默安装应用程序
2012-03-15 15:51 5711SharedPreferences fr = getShare ... -
Android 4.0源码下载
2012-02-23 09:23 1406经过3.0+ 版本不开源,终于等到4.0的开源,也可以看看4. ... -
十四个Uri
2012-01-03 20:52 1141Android 常用到的Intent的URI用法 以下是 ... -
EditText操作
2011-12-26 20:26 2186Android开发:自由选择TextView的文字 20 ... -
Ubuntu 将adb加入环境变量 及adb shell 无法启动
2011-11-22 19:40 4315客分类: Android开发 android ubuntu.在 ...
相关推荐
安卓Android源码——安卓Android Launcher 桌面分页滑动代码.rar
安卓Android源码——Android Launcher 源码修改可编译.zip
安卓Android源码——Launcher2.rar
安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.zip
安卓Android源码——安卓Android Launcher 桌面分页滑动代码.zip
安卓Android源码——Launcher源码修改可编译.zip
安卓Android源码——Launcher桌面分页滑动代码.zip
Androidlauncher开发.pdf
本版本基于Launcher3最新版进行开发,开发包括:双层改单层,特效,菜单,点击卸载等功能。
Android中实现Launcher功能之添加快捷方式
Android Launcher源码下载,PagedView,PagedViewIcon
安卓Android源码——高仿小米launcher(ZAKER)跨屏拖动item.rar
android laucnher3 源码,仅供学习使用。
Android Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid Launcher研究.pdfAndroid ...
android的launcher是一款非常强大的系统应用,值得去好好的研究一番,这里对 Launcher2文件夾功能作详细的分析介绍。
Android 6.0 Launcher3 增加屏幕切换动画的资源和源码包
Android Launcher 源码修改可编译.zip源码资源下载Android Launcher 源码修改可编译.zip源码资源下载
更改官方的Launcher3使得可以在Android Studio编译 原始地址,从5892520提交开始 最小sdk版本为16 Android 5.0 版本及以上可能会出现因为相同权限声明而不能安装的问题 如果你想要可以在Eclipse编译的版本,可以看...
Android系统桌面Launcher3源码,可直接在Android Studio中编译。
这个是android Launcher2.2 源码,我最近在学习,有人想跟我交流就到我的博客:http://blog.csdn.net/gqdy365