自定义View学习笔记(二)-Android坐标系简介

1.背景

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

2.Android坐标系

2.1Android屏幕区域划分

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();
}

@Override
public 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的位置坐标和父容器的关系.jpg(图片来源于网络)

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坐标

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;
}
mScrollX和mScrollY的变换规律.png(图片来源于网络)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,602评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,442评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,878评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,306评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,330评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,071评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,382评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,006评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,512评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,965评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,094评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,732评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,283评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,286评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,512评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,536评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,828评论 2 345

推荐阅读更多精彩内容