Frage Gibt es ein Beispiel für die Verwendung eines TouchDelegate in Android, um das Klickziel einer Ansicht zu vergrößern?


Mein Verständnis ist, dass Sie, wenn Sie eine Ansicht haben, die zu klein ist, um sie einfach zu berühren, ein TouchDelegate verwenden sollten, um die anklickbare Region für diese Ansicht zu vergrößern.

Bei der Suche nach Verwendungsbeispielen bei Google werden jedoch mehrere Personen mit der Frage konfrontiert, aber nur wenige Antworten.

Kennt jemand den richtigen Weg, um einen Touch-Delegaten für eine Ansicht einzurichten, um beispielsweise seinen anklickbaren Bereich um 4 Pixel in jede Richtung zu vergrößern?


75
2017-08-27 19:13


Ursprung


Antworten:


Ich fragte einen Freund bei Google und sie konnten mir helfen, herauszufinden, wie man TouchDelegate verwendet. Folgendes haben wir uns ausgedacht:

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));
    }
});

93
2017-08-27 21:21



Ich konnte dies mit mehreren Ansichten (Checkboxen) auf einem Bildschirm erreichen, die größtenteils von dieser Blogbeitrag. Im Grunde nehmen Sie die Lösung von Emmby und wenden sie individuell auf jede Schaltfläche und deren Eltern an.

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));
        }
   });
}

In meinem Fall hatte ich eine Rasteransicht von Bildansichten mit überlagerten Kontrollkästchen und nannte die Methode wie folgt:

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);

Super für mich.


20
2018-03-04 03:01



Diese Lösung wurde von @ BrendanWeinstein in Kommentaren gepostet.

Anstatt eine zu senden TouchDelegate Sie können überschreiben getHitRect(Rect) Methode deines View (falls Sie eine verlängern).

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;
    }
}

9
2017-11-07 13:33



Der Ansatz von Emmby hat bei mir nicht funktioniert, aber nach ein paar Änderungen hat es geklappt:

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));         
        }
    });
}

Vielleicht kann es jemandem Zeit sparen


6
2018-06-13 13:16



Ist es nicht die bessere Idee, Padding für diese bestimmte Komponente (Button) zu geben.


1
2017-07-05 08:19



Ein bisschen spät auf die Party, aber nach viel Recherche verwende ich jetzt:

/**
 * 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);
            }
        });
}

Dies ist besser als die akzeptierte Lösung, da es auch ermöglicht, dass ein TouchDelegate für jede Voransicht festgelegt wird, nicht nur für die übergeordnete Ansicht.

Im Gegensatz zur akzeptierten Lösung aktualisiert es das TouchDelegate auch dann, wenn sich das Layout des übergeordneten Views ändert.


1
2017-09-08 15:45



Wenn Sie es nicht programmatisch machen wollen, erstellen Sie einfach einen transparenten Bereich um das Bild, wenn Sie das Bild als Hintergrund für die Schaltfläche (Ansicht) verwenden.

Der graue Bereich kann transparent sein, um den Berührungsbereich zu vergrößern.

enter image description here


0
2017-11-07 13:25



In den meisten Fällen können Sie die Ansicht, die einen größeren Berührungsbereich erfordert, in eine andere Ansicht ohne Kopf einfügen (künstliche transparente Ansicht) und der Wrapper-Ansicht Padding / Rand hinzufügen und den Klick / Touch sogar an die Wrapper-Ansicht anstatt an die ursprüngliche Ansicht anhängen muss einen größeren Touch-Bereich haben.


0
2018-02-26 03:52



Laut @ Mason Lee Kommentar, löste dies mein Problem. Mein Projekt hatte ein relatives Layout und einen Knopf. Also Eltern ist -> Layout und Kind ist -> eine Schaltfläche.

Hier ist ein Google Link-Beispiel Google-Code

Im Falle der Löschung seiner sehr wertvollen Antwort habe ich hier seine Antwort gegeben.

Ich wurde kürzlich gefragt, wie man ein TouchDelegate benutzt. Ich war selbst ein wenig eingerostet und konnte keine gute Dokumentation finden.   Hier ist der Code, den ich nach ein wenig Versuch und Irrtum geschrieben habe.   touch_delegate_view ist ein einfaches RelativeLayout mit der ID   touch_delegate_root. Ich habe mit einem einzigen Kind des Layouts definiert, das   Schaltfläche delegierter_Button. In diesem Beispiel erweitere ich den anklickbaren Bereich   der Schaltfläche auf 200 Pixel über dem oberen Rand meiner Schaltfläche.

Öffentliche Klasse TouchDelegateSample erweitert Aktivität {

Schaltfläche mButton; @Override geschützter void onCreate (Bundle   savedInstanceState) {       super.onCreate (savedInstanceState);       setContentView (R.layout.touch_delegate_view);       mButton = (Button) findViewById (R.id.delegated_button);       View 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);
    }
  }
});   } }

Prost, Justin Android Team @ Google

Wenige Worte von mir: Wenn du die linke Seite erweitern willst, gibst du Wert mit Minus, und wenn du die rechte Seite des Objekts erweitern willst, gibst du Wert mit Plus. Dies funktioniert gleich mit oben und unten.


0
2018-04-05 10:29



Um den Touchbereich generisch mit relativ wenigen Einschränkungen zu erweitern, verwenden Sie den folgenden Code.

Sie können den Touchbereich des gegebenen erweitern view innerhalb des gegebenen ancestor Blick durch das Gegebene expansion in Pixel. Sie können einen beliebigen Vorfahren auswählen, solange sich die angegebene Ansicht in der Ahnenstruktur befindet.

    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;
        }
    });
}

Einschränkungen, die gelten:

  • Der Berührungsbereich darf die Grenzen des Vorfahren der Ansicht nicht überschreiten, da der Vorfahr das Berührungsereignis abfangen muss, um es an die Ansicht weiterzuleiten.
  • Einziger TouchDelegate kann auf a eingestellt werden ViewGroup. Wenn Sie mit mehreren Touch-Delegaten arbeiten möchten, wählen Sie andere Vorfahren oder verwenden Sie einen Delegierten-Berührungs-Delegaten, wie in erklärt So verwenden Sie mehrere TouchDelegate.

0
2018-05-18 15:25