原本想作一個View間的碰撞檢測之類的。html
動手作了才發現不是想象的那麼簡單。java
首先,寫好了碰撞檢測的工具類以下:android
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顯示的範圍中。web
而後測試了一下前兩個方法,由於矩形對象都是本身直接用數字構建的,因此沒有問題。app
思路是:在佈局中寫兩個TextView,在Activity中重寫onTouchEvent方法,判斷點擊的點是否在View中。ide
@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()回調中獲取的座標值,是點擊位置,是在絕對座標中的。this
碰撞檢測方法中View的getTop()、getBottom()、getLeft()、getRight()獲取的都是當前View相對於它的父類容器的頂部、底部、左邊和右邊的位置。
由於一個是絕對位置一個是相對位置,因此沒法比較。
固然也有能夠比較的狀況,就是這個View的父類充滿了整個屏幕,而後窗口的設置是無標題而且全屏的。
在onCreate()方法中添加以下語句設置無標題全屏:
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
逐一看了看View中與位置相關的幾個get方法:
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:
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(); } }
將兩個有背景色的TextView的信息都獲取並顯示出來,注意此時是有標題欄的非全屏狀況:
運行截圖以下:
TextView1的信息:(在截圖上加了紅色的圈)
TextView2的信息:
getGlobalVisibleRect(Rect r)是能夠獲得絕對座標的。
getLocationInWindow(int[] location)
getLocationOnScreen(int[] location)
這兩個方法能夠返回View左上角的絕對座標。目前還不出有什麼差異。
查看源碼發現:
public void getLocationOnScreen(int[] location) { getLocationInWindow(location); final AttachInfo info = mAttachInfo; if (info != null) { location[0] += info.mWindowLeft; location[1] += info.mWindowTop; } }
因此屏幕位置是在窗口位置上加了一個偏置。