天天看點

Android View各種尺寸位置相關的方法探究

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如下:

  布局:

Android View各種尺寸位置相關的方法探究
Android View各種尺寸位置相關的方法探究
<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:

Android View各種尺寸位置相關的方法探究
Android View各種尺寸位置相關的方法探究
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的資訊:(在截圖上加了紅色的圈)

Android View各種尺寸位置相關的方法探究

  TextView2的資訊:

Android View各種尺寸位置相關的方法探究

說明:

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

Android View各種尺寸位置相關的方法探究