Tengo entendido que cuando tiene una vista que es demasiado pequeña para tocarla fácilmente, se supone que debe usar un TouchDelegate para aumentar la región en la que se puede hacer clic para esa vista.
Sin embargo, al buscar ejemplos de uso en Google, aparecen varias personas que hacen la pregunta, pero pocas respuestas.
¿Alguien sabe la forma correcta de configurar un delegado táctil para una vista que, por ejemplo, aumente su región en la que se puede hacer clic en 4 píxeles en todas las direcciones?
Respuestas:
Le pregunté a un amigo de Google y pudieron ayudarme a descubrir cómo usar TouchDelegate. Esto es lo que se nos ocurrió:
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)); } });
fuente
Pude lograr esto con múltiples vistas (casillas de verificación) en una pantalla dibujando en gran parte de esta publicación de blog . Básicamente, toma la solución de emmby y la aplica a cada botón y a su padre individualmente.
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)); } }); }
En mi caso, tenía una vista de cuadrícula de vistas de imágenes con casillas de verificación superpuestas en la parte superior y llamé al método de la siguiente manera:
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);
Funciona muy bien para mí.
fuente
Esta solución fue publicada por @BrendanWeinstein en los comentarios.
En lugar de enviar un
TouchDelegate
, puede anular elgetHitRect(Rect)
método de suView
(en caso de que esté ampliando uno).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; } }
fuente
El enfoque de emmby no funcionó para mí, pero después de algunos cambios, sí lo hizo:
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)); } }); }
Tal vez pueda ahorrarle tiempo a alguien
fuente
Según el comentario de @Mason Lee, esto resolvió mi problema. Mi proyecto tenía un diseño relativo y un botón. Entonces el padre es -> diseño y el hijo es -> un botón.
Aquí hay un código de google de ejemplo de enlace de google
En caso de borrar su muy valiosa respuesta pongo aquí su respuesta.
Algunas palabras mías: si quieres expandir el lado izquierdo, le das valor con menos, y si quieres expandir el lado derecho del objeto, le das valor con más. Esto funciona igual con la parte superior e inferior.
fuente
¿No es la mejor idea darle Padding a ese componente en particular (botón)?
fuente
Un poco tarde para la fiesta, pero después de mucha investigación, ahora estoy usando:
/** * 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); } }); }
Esto es mejor que la solución aceptada porque también permite que un TouchDelegate se configure en cualquier Vista anterior, no solo en la Vista principal.
A diferencia de la solución aceptada, también actualiza TouchDelegate cada vez que hay un cambio en el diseño de la vista anterior.
fuente
Si no quiere hacerlo programáticamente, simplemente cree un área transparente alrededor de la imagen, si está usando una imagen como fondo para el botón (ver).
El área gris puede ser transparente para aumentar el área táctil.
fuente
En la mayoría de los casos, puede envolver la vista que requiere un área táctil más grande en otra vista sin cabeza (vista transparente artificial) y agregar relleno / margen a la vista de envoltura y adjuntar el clic / toque incluso a la vista de envoltura en lugar de la vista original que tiene que tener un área táctil más grande.
fuente
Para expandir el área táctil genéricamente con muy pocas restricciones, use el siguiente código.
Le permite expandir el área táctil de lo dado
view
dentro de laancestor
vista dada por lo dadoexpansion
en píxeles. Puede elegir cualquier antepasado siempre que la vista dada esté en el árbol de diseño de antepasados.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; } }); }
Restricciones que aplican:
TouchDelegate
se puede establecer en aViewGroup
. Si desea trabajar con múltiples delegados táctiles, elija diferentes antepasados o use un delegado táctil de composición como se explica en Cómo usar múltiples TouchDelegate .fuente
Como no me gustó la idea de esperar el pase de diseño solo para obtener el nuevo tamaño del rectángulo de TouchDelegate, busqué una solución diferente:
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; } }
Y luego, en un diseño:
<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>
La idea es que TouchSizeIncreaser FrameLayout envolverá el Spinner (podría ser cualquier Vista secundaria) y reenviará todos los eventos táctiles capturados en su derecha a la Vista secundaria. Funciona para clics, la ruleta se abre incluso si se hace clic fuera de sus límites, no estoy seguro de cuáles son las implicaciones para otros casos más complejos.
fuente