Есть ли пример использования TouchDelegate в Android для увеличения размера цели клика?

Я понимаю, что, когда у вас есть представление, которое слишком мало для легкого касания, вы должны использовать TouchDelegate, чтобы увеличить область с кликами для этого вида.

Тем не менее, поиск примеров использования в Google включает нескольких людей, задающих вопрос, но ответов мало.

Кто-нибудь знает, как правильно настроить сенсорный делегат для просмотра, скажем, для увеличения его кликабельной области на 4 пикселя в каждом направлении?

Я спросил друга у 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)); } }); } 

В моем случае у меня было 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)); } }); } 

Может быть, это может спасти чье-то время

Разве это не лучшая идея дать Padding этому конкретному компоненту (Button).

Немного поздно для вечеринки, но после долгих исследований я сейчас использую:

 /** * 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 всякий раз, когда происходит изменение макета представления предка.

Если вы не хотите делать это программно, просто создайте прозрачную область вокруг изображения. Если вы используете изображение в качестве фона для кнопки (вид).

Серая область может быть прозрачной, чтобы увеличить зону касания.

Введите описание изображения здесь

В большинстве случаев вы можете обернуть представление, для которого требуется более большая область касания в другом представлении без головы (искусственный прозрачный вид), и добавьте отступы / поля в представление обертки и прикрепите клик / касание даже к представлению обертки вместо исходного вида, который Должен иметь большую зону касания.

Согласно комментарию @Mason Lee, это решило мою проблему. Мой проект имел относительный макет и одну кнопку. Таким образом, parent -> layout, а child – – кнопка.

Вот пример ссылки google google code

В случае удаления его очень ценного ответа я поставил здесь свой ответ.

Недавно меня спросили о том, как использовать TouchDelegate. Я был немного ржавчив на этом, и я не мог найти никакой хорошей документации. Вот код, который я написал после небольшого проб и ошибок. Touch_delegate_view – это простой RelativeLayout с id touch_delegate_root. Я определил с одним, дочерним элементом макета, кнопкой 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'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); } } }); } } 

Cheers, Justin Android Team @ Google

Несколько слов от меня: если вы хотите развернуть левую сторону, вы даете значение с минусом, и если вы хотите развернуть правую сторону объекта, вы даете значение с плюсом. Это работает так же, как сверху и снизу.

Чтобы расширить зону касания в целом, с довольно небольшими ограничениями используйте следующий код.

Это позволяет вам расширить область касания данного 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 (может быть любой дочерний вид) и пересылать все события касания, зафиксированные в его обратном направлении к дочернему виду. Он работает для кликов, прядильщик открывается, даже если его выходят за его пределы, не уверен, какие последствия для других более сложных случаев.

Intereting Posts