尊重原创作者,转载请注明出处:
http://blog.csdn.net/gemmem/article/details/9967295
ViewRootImpl是一个和系统相关的类,一般程序开发可能不需要使用它,但是有时候为了实现一些高级功能,我们可以考虑使用ViewRootImpl这个类。
举一个例子:
在一般程序开发过程中,如果想得到当前View的touch事件的坐标x,y,我们一般会借助onTouch等回调函数,因为这些函数里面会带有系统传上来的MotionEvent参数,但是有些情况下,我们是无法依赖这种onTouch函数的,比如下面这种情况。
我们想实现View的拖拽功能,希望ImageView随着手指移动而移动,android给程序员提供了drag and drop框架,我们可以直接使用android的drag and drop API来实现这个功能。
框架已经帮我们实现了drag shadow的移动和事件分发,只需要我们调用startDrag即可。
而drag shadow的UI需要我们自己定义,关键函数如下:
onProvideShadowMetrics()
startDrag()
. Use it to send to the system the dimensions and touch point of the drag shadow. The method has two arguments:
Point
object. The drag shadow width goes in
x
and its height goes in
y
.
Point
object. The touch point is the location within the drag shadow that should be under the user's finger during the drag. Its X position goes in
x
and its Y position goes in
y
第一个是用来指定drag shadow的宽和高, point.x表示宽,point.y表示高;
第二个Point参数是指定drag shadow的位置,这里解释一下API定义shadow位置的方法:point.x表示在拖到过程中shadow的左上角在水平方向上到手指触摸点的距离,point.y表示在拖动过程中shadow的左上角在竖直方向上到手指触摸点的距离。
考虑一个实际问题,我们的ImageView是有大小的,它的尺寸是不能忽略的,我们可以长按ImageView的正中心来拖拽它,也可能长按ImageView左上角、右上角或者下边缘等地方来发起拖拽行为,很明显,在这些情形下,我们的手指触摸点到ImageView的左上角距离是不一样的,但是,我们希望在拖拽的过程中drag shadow的左上角和手指触摸点保持这种距离,因为这样显得比较自然,那么在onProvideShadowMetrics里面,我们的touch_point参数就需要动态计算,而不能写死,所以,我们需要得到手指触摸点坐标和ImageView左上角坐标,ImageView的位置坐标非常容易获得(view.getLocationOnScreen),难点在于我们怎么得到手指触摸点坐标,这里ViewRootImpl就派上用场了,ViewRootImpl有 个函数getLastTouchPoint(),它可以获得手机屏幕上最近一次触摸行为的触摸点坐标,有了ImageView左上角坐标和手指触摸点坐标,我就可以计算出手指触摸点和ImageView左上角的相对位置。
那么如何获得ViewRootImpl实例,这个很关键,请看代码:
View root = getRootView(); //getRootView是View.java中的public函数 if (root == null) return; final ViewRootImpl viewRoot = (ViewRootImpl)root.getParent();
下面是构建drag shadow UI的完整代码:
private class SwitchDragShadowBuilder extends View.DragShadowBuilder { public SwitchDragShadowBuilder(View v) { super(v); } @Override public void onProvideShadowMetrics (Point size, Point touch) { int width; int height; width = getView().getWidth(); height = getView().getHeight(); size.set(width, height); View root = getRootView(); if (root == null) return; final ViewRootImpl viewRoot = (ViewRootImpl)root.getParent(); Point lastPoint = new Point(); viewRoot.getLastTouchPoint(lastPoint); //获得触摸点的坐标,相对于屏幕左上角 int pos = new int[2]; mImage.getLocationOnScreen(pos); //获得ImageView的坐标,相对于屏幕左上角 mTouchOffsetX = lastPoint.x - pos[0]; mTouchOffsetY = lastPoint.y - pos[1] + DRAG_OFFSET; //y方向加一个DRAG_OFFSET,表示长按后,我们希望drag shadow往上跳一段距离 touch.set(mTouchOffsetX, mTouchOffsetY); //set position between drag shadow and finger touch point } @Override public void onDrawShadow(Canvas canvas) { getView().draw(canvas); } }