尊重原创作者,转载请注明出处:
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()
The system calls this method immediately after you call
startDrag()
. Use it to send to the system the dimensions and touch point of the drag shadow. The method has two arguments:
- dimensions
-
A
Point
object. The drag shadow width goes inx
and its height goes iny
. - touch_point
-
A
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 inx
and its Y position goes iny
第一个是用来指定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();
上面的代码即可获得ViewRootImpl实例。
下面是构建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);
}
}
ViewRootImpl这个类里面有很多高级的public API,大家可以自己去研究,上面的这个例子只是抛砖引玉,期待大家的更多发现。