奇怪的View.getHitRect()行为

13

我有一个简单的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

<Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button" />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:onClick="animate"
    android:text="animate" />

</LinearLayout>

在我的Activity中,我更改按钮的y位置后打印出其点击区域的矩形:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
}

public void animate(View view) {
    printHitRect();
    findViewById(R.id.button1).setY(50);
    printHitRect();
}
private void printHitRect() { 
    Rect rect = new Rect();
    findViewById(R.id.button1).getHitRect(rect);
    Log.d(">>button1 hit rect", rect.flattenToString()); 
} 
}

预期输出

按钮1命中区域: 0 0 116 72

按钮1命中区域: 0 50 116 122

实际输出

按钮1命中区域: 0 0 116 72

按钮1命中区域: -58 14 58 86

有人能解释一下这个输出吗?我是做错了什么还是这是一个bug?基本上,我正在使用自定义的ViewGroup中的getHitRect()来检测用户触摸了哪个子视图。是否有更好的方法来获取特定点处的子视图,也许有类似getChildAt(x, y)的函数吗?

我尝试过使用setTranslateY()替换setY(),也使用了NineOldAndroid库和内置的动画框架。如果我使用findViewById(R.id.button1).animate().y(50)而不是setY(),就会看到相同的行为。

更新:

我最终使用NineOldAndroid库编写了实用程序方法,现在它可以工作:

private static void getHitRect(View v, Rect rect) {
    rect.left = (int) com.nineoldandroids.view.ViewHelper.getX(v);
    rect.top = (int) com.nineoldandroids.view.ViewHelper.getY(v);
    rect.right = rect.left + v.getWidth();
    rect.bottom = rect.top + v.getHeight();
}
3个回答

11

getHitRect()存在一个错误,会导致无法正确应用转换。我们已经在内部修复了此问题,并且将在 Android 的下一个公共版本中提供修复。


8
既然这个答案是在2013年7月19日发布的,我假设你所指的“下一个版本”是Android 4.3。这是否意味着getHitRect()方法在之前的版本中不可靠? - Saket
@Saket,我认为这是一个合理的假设,因为我这里有一部手机卡在4.1.2(Xperia S)上,我得到的y坐标没有太多意义,即底部和顶部-很遗憾,因为那个矩形函数本来可以帮我省去很多麻烦... - AgentKnopf
@Saket 谢谢你的提示,我最终采取了那种方法 :) ! - AgentKnopf
如果能将此内容添加到文档中就太好了,因为在低于4.3的版本中仍存在此错误。 - Marc

2
你可以通过以下方式获取hit rect:
public Rect getHitRect(View child){
   Rect frame = new Rect();
   frame.left = child.getLeft();
   frame.right = child.getRight();
   frame.top = child.getTop();
   frame.bottom = child.getBottom();
   return frame;
}

这个不起作用,因为child.getLeft()返回了错误的值,提问者使用nineoldandroid编写的解决方案有效。 - 2cupsOfTech

1

这里有一个不依赖于NineOldAndroids的解决方案。

如果您跟随NineOldAndroids代码路径,最终会在post honeycomb设备上到达此处: Github

private static void getHitRect(View v, Rect rect) {
    rect.left = (int) (v.getLeft() + v.getTranslationX());
    rect.top = (int) (v.getTop() + v.getTranslationY());
    rect.right = rect.left + v.getWidth();
    rect.bottom = rect.top + v.getHeight();
}

好的解决方案,我只需要添加小的校准 rect.top -= dividerValue/2; rect.bottom +=dividerValue/2; 当在listview中使用更宽的分隔符时,会有很多触摸检测失误。 - LadyWoodi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接