Насколько я понимаю, когда у вас есть представление, которое слишком мало, чтобы его можно было легко коснуться, вы должны использовать 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)); } }); }
В моем случае у меня была сетка просмотра изображений с наложенными сверху флажками, и я вызвал метод следующим образом:
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, это решило мою проблему. В моем проекте была относительная раскладка и одна кнопка. Итак, родительский элемент -> макет, а дочерний элемент -> кнопка.
Вот пример ссылки Google код Google
В случае удаления его очень ценного ответа я помещаю здесь его ответ.
Несколько слов от меня: если вы хотите развернуть левую часть, вы даете значение с минусом, а если вы хотите расширить правую часть объекта, вы даете значение с плюсом. То же самое с верхом и низом.
источник
Разве это не лучшая идея дать 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 всякий раз, когда происходит изменение макета предка View.
источник
Если вы не хотите делать это программно, просто создайте прозрачную область вокруг изображения. Если вы используете изображение в качестве фона для кнопки (просмотра).
Серая область может быть прозрачной для увеличения сенсорной области.
источник
В большинстве случаев вы можете обернуть представление, которое требует большей сенсорной области, в другое представление без головы (искусственный прозрачный вид) и добавить отступ / поле к представлению оболочки и прикрепить щелчок / касание даже к представлению оболочки вместо исходного представления, которое должна иметь большую сенсорную область.
источник
Чтобы расширить сенсорную область в целом с небольшими ограничениями, используйте следующий код.
Это позволяет вам расширять сенсорную область данного
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, я выбрал другое решение:
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 (может быть любым дочерним представлением) и пересылать все события касания, захваченные в его прямоугольнике, дочернему представлению. Он работает для щелчков, счетчик открывается, даже если щелкнуть за его пределами, не уверен, каковы последствия для других более сложных случаев.
источник