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