android中如何使用ViewRootImpl这个类

时间:2021-10-03 05:29:13

尊重原创作者,转载请注明出处:

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
Point object. The drag shadow width goes in  x and its height goes in  y.
touch_point
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
这个函数有2个参数,都是Point类型。

第一个是用来指定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,大家可以自己去研究,上面的这个例子只是抛砖引玉,期待大家的更多发现。