Android View各种尺寸位置相关的方法探究

 

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)是可以得到绝对坐标的。

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;
        }
    }

 

 

  所以屏幕位置是在窗口位置上加了一个偏置。

 

 

 

posted @ 2013-08-21 19:39  圣骑士wind  阅读(13566)  评论(2编辑  收藏  举报