自定义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();}@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的位置坐标和父容器的关系.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(图片来源于网络)
更多相关文章
- Android的View体系(三):View坐标以及方法说明
- Android中PopupWindow自定义坐标实现
- Android获取GPS坐标:
- Android获取基站坐标代码
- Android GPS坐标距离计算
- android 坐标布局 AbsoluteLayout
- Android 屏幕(View)坐标系统
- gridview第一行与顶端有一定的距离