Есть ли пример использования TouchDelegate в Android для увеличения размера цели клика?
Я понимаю, что, когда у вас есть представление, которое слишком мало для легкого касания, вы должны использовать TouchDelegate, чтобы увеличить область для просмотра для этого вида.
Однако поиск примеров использования в Google включает нескольких людей, задающих вопрос, но ответов мало.
Кто-нибудь знает, как правильно настроить сенсорный делегат для просмотра, скажем, увеличить его область с возможностью кликов на 4 пикселя в каждом направлении?
Ответы
Ответ 1
Я спросил друга в Google, и они смогли помочь мне разобраться, как использовать TouchDelegate. Вот что мы придумали:
final View parent = (View) delegate.getParent();
parent.post( new Runnable() {
// Post in the parent 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
Мне удалось выполнить это с несколькими видами (флажками) на одном экране, главным образом из этого сообщения в блоге. В основном вы принимаете решение 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));
}
});
}
В моем случае у меня было 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);
Отлично работает для меня.
Ответ 3
Это решение было опубликовано @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;
}
}
Ответ 4
emmby approch не работал у меня, но после небольших изменений он сделал:
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));
}
});
}
Возможно, это может сэкономить время
Ответ 5
Разве это не лучшая идея дать Padding этому конкретному компоненту (Button).
Ответ 6
Немного поздно для вечеринки, но после долгих исследований я теперь использую:
/**
* Expand the given child View 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 coordinates
Rect hitRect = new Rect();
childView.getHitRect(hitRect);
// Translate to ancestor 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 всякий раз, когда есть изменение в макете представления предка.
Ответ 7
Если вы не хотите делать это программно, просто создайте прозрачную область вокруг изображения. Если вы используете изображение в качестве фона для кнопки (вид).
Серая область может быть прозрачной, чтобы увеличить зону касания.
![enter image description here]()
Ответ 8
В большинстве случаев вы можете обернуть представление, для которого требуется более большая область касания в другом представлении без головы (искусственный прозрачный вид), и добавьте отступы/поля в представление обертки и прикрепите клик/касание даже к обертке, а не к оригинальный вид, который должен иметь большую область касания.
Ответ 9
Согласно комментарию @Mason Lee, это решило мою проблему.
Мой проект имел относительный макет и одну кнопку. Таким образом, родительский элемент → layout, а child - - кнопка.
Вот пример ссылки google код google
В случае удаления его очень ценного ответа я ставлю здесь свой ответ.
Недавно меня спросили о том, как использовать TouchDelegate. Я был немного ржавчив на этом, и я не мог найти никакой хорошей документации. Вот код, который я написал после небольшого проб и ошибок. touch_delegate_view - это простой RelativeLayout с идентификатором touch_delegate_root. Я определил с одним ребенком макета, button delegated_button. В этом примере я разверну от кнопки до 200 пикселей над верхней частью моей кнопки.
открытый класс TouchDelegateSample расширяет действие {
Кнопка mButton; @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView (R.layout.touch_delegate_view); mButton = (кнопка) findViewById (R.id.delegated_button); Просмотреть parent = findViewById (R.id.touch_delegate_root);
// post a runnable to the parent view 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);
}
}
}); } }
Cheers, Justin Android Team @Google
Несколько слов от меня: если вы хотите развернуть левую сторону, вы даете значение с минусом, и если вы хотите развернуть правую сторону объекта, вы даете значение с плюсом. Это работает так же, как сверху и снизу.
Ответ 10
Чтобы расширить область касания в целом с помощью довольно небольшого количества ограничений, используйте следующий код.
Он позволяет вам расширить область касания заданного 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.
Ответ 11
Поскольку мне не понравилась идея ожидания перехода макета только для получения нового размера прямоугольника 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 (может быть любым дочерним представлением) и пересылать все записанные в нем события касания, попадая прямо в дочерний вид. Он работает для кликов, прядильщик открывается, даже если его выходят за его пределы, не уверен, какие последствия для других более сложных случаев.