原文地址:http://souly.cn/%E6%8A%80%E6%9C%AF%E5%8D%9A%E6%96%87/2015/07/31/Android%E4%B8%AD%E7%9A%84%E8%8E%B7%E5%8F%96%E6%8E%A7%E4%BB%B6%E7%9F%A9%E9%98%B5getHitRect%E6%96%B9%E6%B3%95/
getHitRect的作用和用法
public void getHitRect(Rect outRect)这个方法用来找到控件占据的矩形区域的矩形坐标。 参数outRect表示控件占据的矩形区域。
测试代码如下,根据代码可以很直观的知道这个函数的用法:
public class MainActivity extends Activity {
private TextView textView = null;
private Button button = null;
Rect Trect = new Rect();
Rect Brect = new Rect();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.tv01);
button = (Button) findViewById(R.id.btn);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
textView.getHitRect(Trect);
button.getHitRect(Brect);
Log.e("TAG", "---------TextView_left-------" + String.valueOf(Trect.left));
Log.e("TAG", "---------TextView_top-------" + String.valueOf(Trect.top));
Log.e("TAG", "---------TextView_right-------" + String.valueOf(Trect.right));
Log.e("TAG", "---------TextView_bottom-------" + String.valueOf(Trect.bottom));
Log.e("TAG", "---------Button_left-------" + String.valueOf(Brect.left));
Log.e("TAG", "---------Button_top-------" + String.valueOf(Brect.top));
Log.e("TAG", "---------Button_right-------" + String.valueOf(Brect.right));
Log.e("TAG", "---------Button_bottom-------" + String.valueOf(Brect.bottom));
}
});
}
}
查看log如下:
06-17 18:07:55.534: E/hailong(5797): ---------TextView_left-------281
06-17 18:07:55.534: E/hailong(5797): ---------TextView_top-------475
06-17 18:07:55.534: E/hailong(5797): ---------TextView_right-------438
06-17 18:07:55.534: E/hailong(5797): ---------TextView_bottom-------513
06-17 18:07:55.534: E/hailong(5797): ---------Button_left-------281
06-17 18:07:55.534: E/hailong(5797): ---------Button_top-------513
06-17 18:07:55.534: E/hailong(5797): ---------Button_right-------439
06-17 18:07:55.534: E/hailong(5797): ---------Button_bottom-------609
这样我们就可以知道控件的顶点坐标了。
可以用类似的方法判断是否点击到该控件上boolean isHit = Trect.contains((int)event.getX(), (int)event.getY());
注意:getHitRect作为获取控件所在的矩阵范围函数,我们平常调用时候如果是在控件的监听器里调用就没事, 但是如果主动的在onCreate 或者 onResume中,拿到的矩阵坐标全是0.因为getHitRect方法不能直接在onCreate中调用 ,原因是该控件还未在这个界面框架中得以测量布局,不知道到底是多少,所以我们要寻找一个时机去做这件事,两个方法:
1.运用该控件执行post(就把下面的那个parent当成你要获取getHitRect的方法)
final View parent = (View) delegate.getParent();
parent.post( new Runnable() {
// Post in the parent's message queue to make sure the parent
// lays out its children before we call getHitRect()
public void run() {
final Rect r = new Rect();
delegate.getHitRect(r);
r.top -= 4;
r.bottom += 4;
parent.setTouchDelegate( new TouchDelegate( r , delegate));
}
});
2.自定义该控件,覆写onDraw,调用getHitRect.
扩大view点击范围
Android4.0设计规定的有效可触摸的UI元素标准是48dp,转化为一个物理尺寸约为9毫米。7~10毫米,这是一个用户手指能准确并且舒适触摸的区域。
如下图所示,你的UI元素可能小于48dp,图标仅有32dp,按钮仅有40dp,但是他们的实际可操作焦点区域最好都应达到48dp的大小。
为使小的UI区域获得良好的触摸交互,根据View的特性,目前碰到了两种情况:
1.如ImageView,设置其padding值,可触摸区域将向外扩展;
2.如Button,设置其padding值,可触摸区域不变,其内内容显示区域向内压缩;
情况1的控件,可直接设置其padding值达到目的,如 android:padding=”10dp”
情况2的控件,可使用TouchDelegate动态修改其触摸区域,达到扩大点击范围的效果.
这里大致说一下setTouchDelegate的作用:假设有两个View,分别是v1,v2,我们可以通过v1的setTouchDelegate(bounds, v2)来委派触摸事件, 其中bounds是一个Rect。v1中,落在这个范围的TouchEvent都会传给v2。
既然是这样,那我们可以通过设置某个view的parent的touchDelegate来达到扩大这个view触摸范围的目的。 关键是什么时候去执行parent.setTouchDelegate()方法呢?要设置这个委派,必须得知道当前view大小以及它在parent的位置。 而这些数据都是在onLayout才能确定(注:如果不是自定义View,只是在Activity中设置,请将这些操作置于onWindowFocusChanged()方法中)。
代码如下:
/**
* 扩大View的触摸和点击响应范围,最大不超过其父View范围
*
* @param view
* @param top
* @param bottom
* @param left
* @param right
*/
public static void expandViewTouchDelegate(final View view, final int top,
final int bottom, final int left, final int right) {
((View) view.getParent()).post(new Runnable() {
@Override
public void run() {
Rect bounds = new Rect();
view.setEnabled(true);
view.getHitRect(bounds);
bounds.top -= top;
bounds.bottom += bottom;
bounds.left -= left;
bounds.right += right;
TouchDelegate touchDelegate = new TouchDelegate(bounds, view);
if (View.class.isInstance(view.getParent())) {
((View) view.getParent()).setTouchDelegate(touchDelegate);
}
}
});
}
采取此种方法的两点注意:
1、若View的自定义触摸范围超出Parent的大小,则超出的那部分无效。 2、一个Parent只能设置一个View的TouchDelegate,设置多个时只有最后设置的生效。
若需要恢复该View的触摸范围:
/**
* 还原View的触摸和点击响应范围,最小不小于View自身范围
*
* @param view
*/
public static void restoreViewTouchDelegate(final View view) {
((View) view.getParent()).post(new Runnable() {
@Override
public void run() {
Rect bounds = new Rect();
bounds.setEmpty();
TouchDelegate touchDelegate = new TouchDelegate(bounds, view);
if (View.class.isInstance(view.getParent())) {
((View) view.getParent()).setTouchDelegate(touchDelegate);
}
}
});
}
使用TouchDelegate扩大View的触摸响应范围是一种比较灵活的方法,有时可与设置padding的方式结合使用。
注意:将此法应用在ListView的getView()中绘制每个ItemView时,则Delegate的设置将部分失效,原因是ListView的绘制较特殊,可能无法获取到部分还未绘制出的View的正确坐标。