我的理解是,当视图太小而无法轻松触摸时,应该使用TouchDelegate来增加该视图的可点击区域。
但是,在Google上搜索用法示例会使很多人问这个问题,但答案却很少。
有谁知道为视图设置触摸代表的正确方法,例如,将视图的可点击区域在每个方向上增加4个像素?
我的理解是,当视图太小而无法轻松触摸时,应该使用TouchDelegate来增加该视图的可点击区域。
但是,在Google上搜索用法示例会使很多人问这个问题,但答案却很少。
有谁知道为视图设置触摸代表的正确方法,例如,将视图的可点击区域在每个方向上增加4个像素?
Answers:
我问Google的一个朋友,他们能够帮助我弄清楚如何使用TouchDelegate。这是我们想到的:
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));
}
});
我基本上可以通过此博客文章中的一个屏幕绘图上的多个视图(复选框)来完成此操作。基本上,您采用emmby的解决方案并将其分别应用于每个按钮及其父按钮。
public static void expandTouchArea(final View bigView, final View smallView, final int extraPadding) {
bigView.post(new Runnable() {
@Override
public void run() {
Rect rect = new Rect();
smallView.getHitRect(rect);
rect.top -= extraPadding;
rect.left -= extraPadding;
rect.right += extraPadding;
rect.bottom += extraPadding;
bigView.setTouchDelegate(new TouchDelegate(rect, smallView));
}
});
}
在我的情况下,我有一个imageviews的gridview,上面有复选框,并按如下方式调用该方法:
CheckBox mCheckBox = (CheckBox) convertView.findViewById(R.id.checkBox1);
final ImageView imageView = (ImageView) convertView.findViewById(R.id.imageView1);
// Increase checkbox clickable area
expandTouchArea(imageView, mCheckBox, 100);
对我来说很棒。
此解决方案由@BrendanWeinstein发表在评论中。
TouchDelegate您可以覆盖getHitRect(Rect)您的方法View(而不是发送一个方法)(以防您扩展一个方法)。
public class MyView extends View { //NOTE: any other View can be used here
/* a lot of required methods */
@Override
public void getHitRect(Rect r) {
super.getHitRect(r); //get hit Rect of current View
if(r == null) {
return;
}
/* Manipulate with rect as you wish */
r.top -= 10;
}
}
emmby的方法对我不起作用,但是经过一些更改,它确实起作用了:
private void initApplyButtonOnClick() {
mApplyButton.setOnClickListener(onApplyClickListener);
final View parent = (View)mApplyButton.getParent();
parent.post(new Runnable() {
@Override
public void run() {
final Rect hitRect = new Rect();
parent.getHitRect(hitRect);
hitRect.right = hitRect.right - hitRect.left;
hitRect.bottom = hitRect.bottom - hitRect.top;
hitRect.top = 0;
hitRect.left = 0;
parent.setTouchDelegate(new TouchDelegate(hitRect , mApplyButton));
}
});
}
也许可以节省别人的时间
根据@Mason Lee的评论,这解决了我的问题。我的项目有相对布局和一个按钮。因此父级是->布局,子级是->一个按钮。
这是一个谷歌链接示例谷歌代码
如果删除他非常有价值的答案,我将他的答案放在这里。
最近有人问我如何使用TouchDelegate。我对此有些生疏,找不到任何好的文档。这是我经过反复尝试后编写的代码。touch_delegate_view是一个简单的RelativeLayout,其ID为touch_delegate_root。我用一个单独的布局子级定义了按钮委托_按钮。在此示例中,我将按钮的可单击区域扩展到按钮顶部上方200像素。
public class TouchDelegateSample extends Activity { Button mButton; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.touch_delegate_view); mButton = (Button)findViewById(R.id.delegated_button); View parent = findViewById(R.id.touch_delegate_root); // post a runnable to the parent view's message queue so its run after // the view is drawn parent.post(new Runnable() { @Override public void run() { Rect delegateArea = new Rect(); Button delegate = TouchDelegateSample.this.mButton; delegate.getHitRect(delegateArea); delegateArea.top -= 200; TouchDelegate expandedArea = new TouchDelegate(delegateArea, delegate); // give the delegate to an ancestor of the view we're delegating the // area to if (View.class.isInstance(delegate.getParent())) { ((View)delegate.getParent()).setTouchDelegate(expandedArea); } } }); } }Google贾斯汀Android团队欢呼声@ Google
我的话很少:如果要扩展左侧,则用减号给出值,如果要扩展对象右侧,则用加号给出值。顶部和底部的工作原理相同。
将Padding赋予该特定组件(按钮)不是更好的主意。
参加聚会有点晚,但是经过大量研究,我现在使用:
/**
* Expand the given child View's touchable area by the given padding, by
* setting a TouchDelegate on the given ancestor View whenever its layout
* changes.
*/*emphasized text*
public static void expandTouchArea(final View ancestorView,
final View childView, final Rect padding) {
ancestorView.getViewTreeObserver().addOnGlobalLayoutListener(
new OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
TouchDelegate delegate = null;
if (childView.isShown()) {
// Get hitRect in parent's coordinates
Rect hitRect = new Rect();
childView.getHitRect(hitRect);
// Translate to ancestor's coordinates
int ancestorLoc[] = new int[2];
ancestorView.getLocationInWindow(ancestorLoc);
int parentLoc[] = new int[2];
((View)childView.getParent()).getLocationInWindow(
parentLoc);
int xOffset = parentLoc[0] - ancestorLoc[0];
hitRect.left += xOffset;
hitRect.right += xOffset;
int yOffset = parentLoc[1] - ancestorLoc[1];
hitRect.top += yOffset;
hitRect.bottom += yOffset;
// Add padding
hitRect.top -= padding.top;
hitRect.bottom += padding.bottom;
hitRect.left -= padding.left;
hitRect.right += padding.right;
delegate = new TouchDelegate(hitRect, childView);
}
ancestorView.setTouchDelegate(delegate);
}
});
}
这比公认的解决方案要好,因为它还允许在任何祖先视图(不仅是父视图)上设置TouchDelegate。
与公认的解决方案不同,只要祖先视图的布局发生更改,它也会更新TouchDelegate。
如果不想以编程方式进行操作,则只需在图像周围创建透明区域即可。如果您将图像用作按钮(视图)的背景。
灰色区域可以是透明的以增加触摸区域。

在大多数情况下,您可以将需要较大触摸区域的视图包装到另一个无头视图(人工透明视图)中,并在包装器视图中添加填充/边距,并将单击/触摸甚至附加到包装器视图,而不是原始视图必须具有更大的触摸区域。
一般而言,要以很少的限制扩展触摸区域,请使用以下代码。
它使您可以将给view定ancestor视图内给定的触摸区域扩展给定expansion的像素数。您可以选择任何祖先,只要给定视图在祖先布局树中即可。
public static void expandTouchArea(final View view, final ViewGroup ancestor, final int expansion) {
ancestor.post(new Runnable() {
public void run() {
Rect bounds = getRelativeBounds(view, ancestor);
Rect expandedBounds = expand(bounds, expansion);
// LOG.debug("Expanding touch area of {} within {} from {} by {}px to {}", view, ancestor, bounds, expansion, expandedBounds);
ancestor.setTouchDelegate(new TouchDelegate(expandedBounds, view));
}
private Rect getRelativeBounds(View view, ViewGroup ancestor) {
Point relativeLocation = getRelativeLocation(view, ancestor);
return new Rect(relativeLocation.x, relativeLocation.y,
relativeLocation.x + view.getWidth(),
relativeLocation.y + view.getHeight());
}
private Point getRelativeLocation(View view, ViewGroup ancestor) {
Point absoluteAncestorLocation = getAbsoluteLocation(ancestor);
Point absoluteViewLocation = getAbsoluteLocation(view);
return new Point(absoluteViewLocation.x - absoluteAncestorLocation.x,
absoluteViewLocation.y - absoluteAncestorLocation.y);
}
private Point getAbsoluteLocation(View view) {
int[] absoluteLocation = new int[2];
view.getLocationOnScreen(absoluteLocation);
return new Point(absoluteLocation[0], absoluteLocation[1]);
}
private Rect expand(Rect rect, int by) {
Rect expandedRect = new Rect(rect);
expandedRect.left -= by;
expandedRect.top -= by;
expandedRect.right += by;
expandedRect.bottom += by;
return expandedRect;
}
});
}
适用的限制:
TouchDelegate设置为ViewGroup。如果要使用多个触摸代表,请选择不同的祖先或使用如如何使用多个TouchDelegate中所述的组成的触摸代表。因为我不喜欢仅仅为了获得TouchDelegate矩形的新大小而等待布局通过的想法,所以我选择了另一种解决方案:
public class TouchSizeIncreaser extends FrameLayout {
public TouchSizeIncreaser(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
final View child = getChildAt(0);
if(child != null) {
child.onTouchEvent(event);
}
return true;
}
}
然后,在布局中:
<ch.tutti.ui.util.TouchSizeIncreaser
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp">
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</ch.tutti.ui.util.TouchSizeIncreaser>
这个想法是TouchSizeIncreaser FrameLayout将包装Spinner(可以是任何子视图),并将在命中时捕获的所有触摸事件转发到子视图。它适用于单击,即使在其边界之外单击,微调器也会打开,不确定其他更复杂情况的含义是什么。