Подтвердить что ты не робот

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

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

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

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

4b9b3361

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