Android View的各种尺寸相关函数查看,写了一个Demo并输出结果。
Android View各种尺寸位置相关的方法探究
本来想做一个View间的碰撞检测之类的。
动手做了才发现不是想象的那么简单。
首先,写好了碰撞检测的工具类如下:
package com.mengdd.utils;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.View;
public class Collision2DUtils {
public static boolean isPointInRect(int x, int y, Rect rect) {
boolean isIn = false;
isIn = rect.contains(x, y);
return isIn;
}
public static boolean isPointInRectF(float x, float y, RectF rectF) {
boolean isIn = false;
isIn = rectF.contains(x, y);
return isIn;
}
public static boolean isPointInView(float x, float y, View view) {
if (null == view) {
throw new IllegalArgumentException("view is null!");
}
boolean isIn = false;
int top = view.getTop();
int left = view.getLeft();
int right = view.getRight();
int bottom = view.getBottom();
int width = view.getWidth();
int height = view.getHeight();
if (x >= left && x <= right && y >= top && y <= bottom) {
isIn = true;
}
Log.i("View", ", x: " + x + ", left: " + left + ", right: " + right
+ ", y: " + y + ", top: " + top + ", bottom: " + bottom
+ ", width: " + width + ", height: " + height + ", result: "
+ isIn);
return isIn;
}
}
三个方法,分别用于判断点是否在一个矩形中(整形,浮点型),还有判断一个点是否在一个View显示的范围中。
然后测试了一下前两个方法,因为矩形对象都是自己直接用数字构建的,所以没有问题。
测试判断点是否在View中的方法
思路是:在布局中写两个TextView,在Activity中重写onTouchEvent方法,判断点击的点是否在View中。
@Override
public boolean onTouchEvent(MotionEvent event) {
if (MotionEvent.ACTION_DOWN == event.getAction()) {
float x = event.getX();
float y = event.getY();
detectCollision(x, y);
}
return super.onTouchEvent(event);
}
其中判断的方法:
结果显示在另一个TextView里
private void detectCollision(float x, float y) {
boolean result1 = Collision2DUtils.isPointInView(x, y, block1);
boolean result2 = Collision2DUtils.isPointInView(x, y, block2);
mResult1.setText("result1: " + result1 + ", result2: " + result2);
}
一试就发现问题了:判断结果并不对。
判断错误的原因:
onTouchEvent()回调中获取的坐标值,是点击位置,是在绝对坐标中的。
碰撞检测方法中View的getTop()、getBottom()、getLeft()、getRight()获取的都是当前View相对于它的父类容器的顶部、底部、左边和右边的位置。
因为一个是绝对位置一个是相对位置,所以无法比较。
当然也有可以比较的情况,就是这个View的父类充满了整个屏幕,然后窗口的设置是无标题并且全屏的。
在onCreate()方法中添加如下语句设置无标题全屏:
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
那如何获取View的绝对位置呢?
逐一看了看View中与位置相关的几个get方法:
getTranslationX()
getTranslationY()
getLocalVisibleRect(Rect r)
getGlobalVisibleRect(Rect r)
getGlobalVisibleRect(Rect r, Point globalOffset)
getLocationInWindow(int[] location)
getLocationOnScreen(int[] location)
完整Demo如下:
布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".CollisionDetectionActivity" >
<TextView
android:id="@+id/touch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp" />
<TextView
android:id="@+id/result1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#FFBBBBFF"
android:textSize="20sp" />
<TextView
android:id="@+id/block1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/touch"
android:background="#FFFFBBFF"
android:padding="10dp"
android:text="Touch Block1"
android:textSize="20sp" />
<TextView
android:id="@+id/block2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/block1"
android:background="#FFBBFFBB"
android:padding="10dp"
android:text="Touch Block2"
android:textSize="20sp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="@id/result1"
android:layout_below="@id/block2"
android:id="@+id/myScrollView">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/info1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="20sp" />
<TextView
android:id="@+id/info2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:textSize="20sp" />
</LinearLayout>
</ScrollView>
</RelativeLayout>
activity_collision_detection.xml
Activity:

package com.example.hellocollisiondetection;
import com.mengdd.utils.Collision2DUtils;
import android.os.Bundle;
import android.app.Activity;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ScrollView;
import android.widget.TextView;
public class CollisionDetectionActivity extends Activity {
private TextView mTouchTextView = null;
private TextView mResult1 = null;
private View block1 = null;
private View block2 = null;
private TextView mInfo1 = null;
private TextView mInfo2 = null;
private ScrollView mScrollView = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
// requestWindowFeature(Window.FEATURE_NO_TITLE);
// getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_collision_detection);
mTouchTextView = (TextView) findViewById(R.id.touch);
mResult1 = (TextView) findViewById(R.id.result1);
block1 = findViewById(R.id.block1);
block2 = findViewById(R.id.block2);
mInfo1 = (TextView) findViewById(R.id.info1);
mInfo2 = (TextView) findViewById(R.id.info2);
mScrollView = (ScrollView) findViewById(R.id.myScrollView);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.collision_detection, menu);
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (MotionEvent.ACTION_DOWN == event.getAction()) {
float x = event.getX();
float y = event.getY();
mTouchTextView.setText("x: " + x + ", y: " + y);
Log.i("Touch", "x: " + x + ", y: " + y);
detectCollision(x, y);
getInfos();
}
return super.onTouchEvent(event);
}
private void detectCollision(float x, float y) {
// Rect rect1 = new Rect(0, 0, 200, 200);
// boolean result1 = Collision2DUtils.isPointInRect((int)x, (int)y,
// rect1);
//
// RectF rect2 = new RectF(0,200,600,1000);
// boolean result2 = Collision2DUtils.isPointInRectF(x, y, rect2);
boolean result1 = Collision2DUtils.isPointInView(x, y, block1);
boolean result2 = Collision2DUtils.isPointInView(x, y, block2);
Log.i("Touch", "result1: " + result1 + ", result2: " + result2);
mResult1.setText("result1: " + result1 + ", result2: " + result2);
}
private void getInfos() {
mInfo1.setText(getViewInfo(block1));
mInfo2.setText(getViewInfo(block2));
}
private String getViewInfo(View view) {
StringBuffer stringBuffer = new StringBuffer();
int top = view.getTop();
int left = view.getLeft();
int right = view.getRight();
int bottom = view.getBottom();
int width = view.getWidth();
int height = view.getHeight();
stringBuffer.append("Info relative to Parent: " + "left: " + left
+ ", right: " + right + ", top: " + top + ", bottom: " + bottom
+ ", width: " + width + ", height: " + height);
// API Level 11
stringBuffer.append("\n view.getTranslationX(): "
+ view.getTranslationX());
stringBuffer.append("\n view.getTranslationY(): "
+ view.getTranslationY());
Rect rect = new Rect();
view.getLocalVisibleRect(rect);
stringBuffer.append("\ngetLocalVisibleRect(): ");
stringBuffer.append("\n " + rect.toString());
Rect globalRect = new Rect();
view.getGlobalVisibleRect(globalRect);
stringBuffer.append("\ngetGlobalVisibleRect(): ");
stringBuffer.append("\n " + globalRect.toString());
int[] locationInWindow = new int[2];
view.getLocationInWindow(locationInWindow);
stringBuffer.append("\ngetLocationInWindow(): ");
stringBuffer.append("\n " + locationInWindow[0] + ", "
+ locationInWindow[1]);
int[] locationOnScreen = new int[2];
view.getLocationOnScreen(locationOnScreen);
stringBuffer.append("\ngetLocationOnScreen(): ");
stringBuffer.append("\n " + locationOnScreen[0] + ", "
+ locationOnScreen[1]);
return stringBuffer.toString();
}
}
CollisionDetectionActivity.java
将两个有背景色的TextView的信息都获取并显示出来,注意此时是有标题栏的非全屏情况:
运行截图如下:
TextView1的信息:(在截图上加了红色的圈)
TextView2的信息:
说明:
getGlobalVisibleRect(Rect r)是可以得到绝对坐标的。
这两个方法可以返回View左上角的绝对坐标。目前还不出有什么差别。
查看源码发现:
public void getLocationOnScreen(int[] location) {
getLocationInWindow(location);
final AttachInfo info = mAttachInfo;
if (info != null) {
location[0] += info.mWindowLeft;
location[1] += info.mWindowTop;
}
}
所以屏幕位置是在窗口位置上加了一个偏置。
作者: 圣骑士Wind
出处: 博客园: 圣骑士Wind
Github: https://github.com/mengdd
微信公众号: 圣骑士Wind