1.背景

学习自定义控件,了解Android各种坐标系及一些API的坐标含义绝对算一个小而不可忽视的技能;所谓Android自定义View那几大主要onXXX()方法的重写实质大多数都是在处理坐标逻辑运算。所以,我们一起学习下Android坐标系。

2.Android坐标系

2.1Android屏幕区域划分

自定义View学习笔记(二)-Android坐标系简介_第1张图片 device-2016-12-09-142518.png

通过上图我们可以很直观的看到Android对于屏幕的划分定义。下面我们就给出这些区域里常用区域的一些坐标或者度量方式。如下:

//获取手机屏幕区域高度public int getWindowArea() {    DisplayMetrics metrics = new DisplayMetrics();    getWindowManager().getDefaultDisplay().getMetrics(metrics);    int widthPixels = metrics.widthPixels;    int heightPixels = metrics.heightPixels;    return heightPixels;}//获取应用区域高度public int getApplicationArea() {    Rect rect = new Rect();    getWindow().getDecorView().            getWindowVisibleDisplayFrame(rect);    int width = rect.width();    int height = rect.height();    return height;}//获取状态栏高度public int getstatusBarArea() {    Rect rect = new Rect();    getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);    int statusBarHeight = rect.top;    return statusBarHeight;}//获取view绘制区域高度public int getDrawArea() {    Rect rect = new Rect();    getWindow().findViewById(Window.ID_ANDROID_CONTENT)            .getDrawingRect(rect);    int width = rect.width();    int height = rect.height();    return height;}//获取标题栏高度public int gettitleBarArea() {    return getApplicationArea()-getDrawArea();}@Overridepublic void onWindowFocusChanged(boolean hasFocus) {    super.onWindowFocusChanged(hasFocus);    android.util.Log.i("TAG","onWindowFocusChanged");    if (hasFocus) {        //获取view绘制区域(即不包括标titlebar的内容区域)距离手机屏幕顶部的距离        android.util.Log.i("TAG","屏幕区域高度:"+getWindowArea());        android.util.Log.i("TAG","应用区域高度:"+getApplicationArea());        android.util.Log.i("TAG","view绘制区域高度:"+getDrawArea());        android.util.Log.i("TAG","状态栏高度:"+getstatusBarArea());        android.util.Log.i("TAG","标题栏高度:"+gettitleBarArea());    }}

...
onWindowFocusChanged():当Activity的当前Window获得或失去焦点时会被回调此方法。当回调了这个方法时表示Activity是完全对用户可见的(只是可见,还一片黑呼呼的,有待draw..)。当对话框弹起/消失及Activity新创建及回退等都会调用此方法。此时能获取屏幕区域的准确信息。

2.2Android View绝对相对坐标系

2.2.1View的坐标

自定义View学习笔记(二)-Android坐标系简介_第2张图片 view的位置坐标和父容器的关系.jpg(图片来源于网络)
自定义View学习笔记(二)-Android坐标系简介_第3张图片 view.getX和view.getTranslationX区别.png(图片来源于网络)

通过上图我们可以很直观的给出View一些坐标相关的方法解释,不过必须要明确的是上面这些方法必须要在layout之后才有效,如下:

|View的静态坐标方法 | 解释
|-------------
|getLeft() |返回View自身左边到父布局左边的距离
|getTop() |返回View自身顶边到父布局顶边的距离
|getRight()| 返回View自身右边到父布局左边的距离
|getBottom()| 返回View自身底边到父布局顶边的距离
|getX()| 返回值为getLeft()+getTranslationX(),当setTranslationX()时getLeft()不变,getX()变。
|getY() |返回值为getTop()+getTranslationY(),当setTranslationY()时getTop()不变,getY()变。

2.2.2MotionEvent坐标

自定义View学习笔记(二)-Android坐标系简介_第4张图片 MotionEvent坐标.jpg(图片来源于网络)

手指触摸屏幕时MotionEvent提供的一些方法解释,如下:

|MotionEvent坐标方法| 解释
|-------------
|getX() |当前触摸事件距离当前View左边的距离
|getY() |当前触摸事件距离当前View顶边的距离
|getRawX() |当前触摸事件距离整个屏幕左边的距离
|getRawY() |当前触摸事件距离整个屏幕顶边的距离

2.3Android View滑动相关坐标系

|View的滑动方法 | 效果及描述
|-------------
|offsetLeftAndRight(int offset) |水平方向挪动View,offset为正则x轴正向移动,移动的是整个View,getLeft()会变的。
|offsetTopAndBottom(int offset)|垂直方向挪动View,offset为正则y轴正向移动,移动的是整个View,getTop()会变的。
|scrollTo(int x, int y)|将View中内容(不是整个View)滑动到相应的位置,参考坐标原点为ParentView左上角,x,y为正则向x,y轴反方向移动,反之同理。
|scrollBy(int x, int y)|在scrollTo()的基础上继续滑动xy。
|setScrollX(int value)|实质为scrollTo(),只是只改变Y轴滑动。
|setScrollY(int value)|实质为scrollTo(),只是只改变X轴滑动。
|getScrollX()/getScrollY()|获取当前滑动位置偏移量。

2.3.1为什么scrollTo和scrollBy滚动的是View的内容而非View本身?

/** * Set the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the x position to scroll to * @param y the y position to scroll to */public void scrollTo(int x, int y) {    if (mScrollX != x || mScrollY != y) {        int oldX = mScrollX;        int oldY = mScrollY;        mScrollX = x;        mScrollY = y;        invalidateParentCaches();        onScrollChanged(mScrollX, mScrollY, oldX, oldY);        if (!awakenScrollBars()) {            postInvalidateOnAnimation();        }    }}/** * Move the scrolled position of your view. This will cause a call to * {@link #onScrollChanged(int, int, int, int)} and the view will be * invalidated. * @param x the amount of pixels to scroll by horizontally * @param y the amount of pixels to scroll by vertically */public void scrollBy(int x, int y) {    scrollTo(mScrollX + x, mScrollY + y);}

因为View在ViewGroup中的位置是由LayoutParams的margin等参数决定的,要想滚动View或者说要想改变View的位置只需要改变LayoutParams的相关参数就可以。但是scrollTo和scrollBy改变的只是mScrollX和mScrollY的值,这两个值对于改变View在ViewGroup里面的位置是毫无关系的;这就排除了scrollTo或者scrollBy滚动的是View本身了。
为什么说View滚动的是内容?

/** * Draws the background onto the specified canvas. * @param canvas Canvas on which to draw the background */private void drawBackground(Canvas canvas) {    final Drawable background = mBackground;    if (background == null) {        return;    }    setBackgroundBounds();    // Attempt to use a display list if requested.    if (canvas.isHardwareAccelerated() && mAttachInfo != null            && mAttachInfo.mHardwareRenderer != null) {        mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);        final RenderNode renderNode = mBackgroundRenderNode;        if (renderNode != null && renderNode.isValid()) {            setBackgroundRenderNodeProperties(renderNode);            ((DisplayListCanvas) canvas).drawRenderNode(renderNode);            return;        }    }    final int scrollX = mScrollX;    final int scrollY = mScrollY;    if ((scrollX | scrollY) == 0) {        background.draw(canvas);    } else {        canvas.translate(scrollX, scrollY);        background.draw(canvas);        canvas.translate(-scrollX, -scrollY);    }}

上面的代码可以看出mScrollX和mScrollY这两个变量正式交给显示View内容的Canvas来操作的!所以我们说scrollTo或者scrollBy滚动的是View的内容,而不是改变View在parentView显示的位置关系!

2.3.2 mScrollX和mScrollY的变换规律

  /** * The offset, in pixels, by which the content of this view is scrolled * horizontally. * {@hide} */@ViewDebug.ExportedProperty(category = "scrolling")protected int mScrollX;/** * The offset, in pixels, by which the content of this view is scrolled * vertically. * {@hide} */@ViewDebug.ExportedProperty(category = "scrolling")protected int mScrollY;  /** * Return the scrolled left position of this view. This is the left edge of * the displayed part of your view. You do not need to draw any pixels * farther left, since those are outside of the frame of your view on * screen. * * @return The left edge of the displayed part of your view, in pixels. */public final int getScrollX() {    return mScrollX;}/** * Return the scrolled top position of this view. This is the top edge of * the displayed part of your view. You do not need to draw any pixels above * it, since those are outside of the frame of your view on screen. * * @return The top edge of the displayed part of your view, in pixels. */public final int getScrollY() {    return mScrollY;}
自定义View学习笔记(二)-Android坐标系简介_第5张图片 mScrollX和mScrollY的变换规律.png(图片来源于网络)

更多相关文章

  1. Android的View体系(三):View坐标以及方法说明
  2. Android中PopupWindow自定义坐标实现
  3. Android获取GPS坐标:
  4. Android获取基站坐标代码
  5. Android GPS坐标距离计算
  6. android 坐标布局 AbsoluteLayout
  7. Android 屏幕(View)坐标系统
  8. gridview第一行与顶端有一定的距离

随机推荐

  1. Android改变标题栏背景和文字颜色
  2. Android新手入门实例之Android漂亮时钟的
  3. Android ViewPager和PagerAdapter简单代
  4. Android Service简单示例
  5. android Bitmap和Drawable转换
  6. Android 触摸消息处理
  7. Android环境搭建问题的解决:Failed to fet
  8. android 通过pull解析xml文件
  9. Android so库编译错误 java.lang.Unsatis
  10. Android UTC和Local 时间互转