2009-08-27 5 views
73

La mia comprensione è che quando si dispone di una vista troppo piccola per essere facilmente toccata, si suppone di utilizzare un TouchDelegate per aumentare la regione selezionabile per quella vista.Esiste un esempio di come utilizzare TouchDelegate in Android per aumentare le dimensioni del target di clic di una vista?

Tuttavia, la ricerca di esempi di utilizzo su Google presenta più persone che fanno la domanda, ma poche risposte.

Qualcuno conosce il modo corretto di impostare un delegato tocco per una vista, ad esempio, aumentare la regione selezionabile di 4 pixel in ogni direzione?

+3

Ecco un altro post utile su come utilizzare TouchDelegate: http://groups.google.com/group/android-developers/browse_thread/thread/178af525ab84b371 –

+0

Buona esercitazione: [Ampie aree estendibili] (http: // android.cyrilmottier.com/?p=574) –

risposta

85

Ho chiesto a un amico di Google e sono stati in grado di aiutarmi a capire come utilizzare TouchDelegate. Ecco cosa siamo arrivati ​​a:

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

Cosa succede se ci sono due pulsanti su uno schermo in cui vorresti farlo? Se si impostaTouchDelegate nuovamente, viene cancellato il delegato tocco precedente. – cottonBallPaws

+1

Questo non ha funzionato per me ... presumo che il delegato sia la vista a cui sto cercando di aggiungere il TouchDelegate. –

+0

Qualcuno ha una soluzione per il problema di @ littleFluffyKitty? Sto incontrando lo stesso problema! –

0

Se non si vuole farlo programatically poi semplicemente creare un'area trasparente attorno all'immagine, se si utilizza un'immagine come sfondo per il pulsante (vista).

L'area grigia può essere trasparente per aumentare l'area di tocco.

enter image description here

+1

Non creerebbe uno spazio vuoto attorno a quella vista che non può essere usato per nient'altro? Cosa fare se si desidera visualizzare il testo informativo vicino a un pulsante e utilizzare l'area di testo per migliorare l'area di clic del pulsante? – Martin

+0

Non dico sia il migliore e l'ultimo approccio. Ma si adatterà a molti scenari in cui si desidera mostrare un'immagine di pulsante minuscola ma l'area di tocco da estendere. Nel tuo caso è difficile dare la precedenza al pulsante quando fai clic su altre viste (vista testo) è tutto insieme è una sfida diversa. –

9

Questa soluzione è stato pubblicato da @BrendanWeinstein nei commenti.

Invece di inviare un TouchDelegate è possibile sostituire il metodo getHitRect(Rect) del proprio View (nel caso in cui si stia estendendo 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; 
    } 
} 
+0

Idea interessante. Ma non significa che la vista non può ** essere creata da un layout? No, aspetta - se progetto una nuova classe vista e la uso nel layout. Potrebbe funzionare. Vale la pena provare. – Martin

+6

@ Martin è l'idea giusta. Sfortunatamente, getHitRect non viene più chiamato come ICS da dispatchTouchEvent http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.4_r1/android/view/ViewGroup.java # 1834 Override di getHitRect funziona solo con il pan di zenzero e sotto. –

0

Nella maggior parte dei casi, si può avvolgere la vista che richiede una più grande area di contatto in un'altra vista senza testa (artificiale trasparente vista) e aggiungere padding/margine alla vista involucro e fissare il click/touch anche per l'involucro guarda invece della vista originale che deve avere un'area di tocco più ampia.

+0

Non creerebbe uno spazio vuoto attorno a quella vista che non può essere usato per nient'altro?Cosa fare se si desidera visualizzare il testo informativo vicino a un pulsante e utilizzare l'area di testo per migliorare l'area di clic del pulsante? – Martin

+0

@ Martin, dipende da come lo fai. E dovrebbe essere facile fare tutto quello che vuoi - avvolgere tutto ciò che vuoi (ma molte viste vuoi) in una vista trasparente sopra tutte queste viste e assegnare un clic anche a quell'area. – inteist

18

Sono riuscito a farlo con più viste (caselle di controllo) su uno schermo in gran parte disegno da this blog post. Fondamentalmente si prende la soluzione di emmby e si applica a ciascun pulsante e ai suoi genitori 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)); 
     } 
    }); 
} 

Nel mio caso ho avuto un GridView di imageviews con caselle di controllo sovrapposti sulla parte superiore, e chiamato il metodo come segue:

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

Lavorare grande per me.

0

Secondo il commento di @Mason Lee, questo ha risolto il mio problema. Il mio progetto aveva un layout relativo e un pulsante. Quindi genitore è -> layout e child is -> un pulsante.

Ecco un esempio di collegamento del google google code

In caso di cancellazione la sua risposta molto prezioso ho messo qui la sua risposta.

Mi è stato chiesto di recente come utilizzare un TouchDelegate. Ero un po 'arrugginito su questo e non ho trovato alcuna buona documentazione su di esso. Ecco il codice che ho scritto dopo un po 'di tentativi ed errori. touch_delegate_view è un RelativeLayout semplice con l'id touch_delegate_root.Ho definito con un singolo, figlio del layout, il pulsante delegated_button. In questo esempio, espongo l'area cliccabile del pulsante a 200 pixel sopra la parte superiore del mio pulsante.

public class TouchDelegateSample estende Activity {

Pulsante MButton; @Override protected void onCreate (Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView (R.layout.touch_delegate_view); mButton = (Button) findViewById (R.id.delegated_button); Visualizza genitore = 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, squadra Justin Android @ Google

Poche parole da me: se si desidera espandere la sinistra si dà valore con meno, e se si vuole ampliare lato destro di oggetto, si dà valore con più. Funziona allo stesso modo in alto e in basso. approch

+0

Il suggerimento magico qui sembra essere * un pulsante * - In qualche modo ho la sensazione che TouchDelegate sia inutilizzabile per più pulsanti. Puoi confermarlo? – Martin

6

di emmby non ha funzionato per me, ma dopo un po 'di cambiamenti che ha fatto:

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

forse può risparmiare tempo di qualcuno

+0

questo è quello che ha funzionato per me. in realtà rende l'intero genitore di inviare eventi di clic al figlio. non deve nemmeno essere il genitore diretto (può essere un genitore di un genitore). –

+0

Probabilmente funziona se si ha una vista da delegare. cosa succede se hai, ad esempio, 50 visualizzazioni per migliorare l'area tattile? - Come in questo Screenshot: https://plus.google.com/u/0/b/112127498414857833804/112127498414857833804/posts - È un TouchDelegate quindi ancora una soluzione appropriata? – Martin

1

non è la migliore idea di dare imbottitura per quel particolare componente (Button).

+1

Ciò renderebbe più grande. Che cosa succede se si dispone di testo non cliccabile sopra un pulsante e si desidera utilizzare l'area di tale testo per migliorare la precisione del clic per il pulsante. - Come in questo Screenshot: https://plus.google.com/u/0/b/112127498414857833804/112127498414857833804/posts – Martin

1

Un po 'in ritardo alla festa, ma dopo molte ricerche, ora sto 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); 
      } 
     }); 
} 

Questo è meglio che la soluzione accettata perché permette anche una TouchDelegate da impostare su qualsiasi antenato View, non solo la vista genitore.

A differenza della soluzione accettata, aggiorna anche il TouchDelegate ogni volta che c'è un cambiamento nel layout della vista dell'antenato.

0

Per espandere genericamente l'area di tocco con poche restrizioni, utilizzare il seguente codice.

Consente di espandere l'area di tocco del dato view all'interno della vista ancestor in base allo expansion indicato in pixel. È possibile scegliere qualsiasi antenato purché la vista data si trovi nell'albero di layout degli antenati.

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

restrizioni che si applicano:

  • L'area tocco non può superare i limiti di antenato della vista in quanto l'antenato deve essere in grado di catturare l'evento di tocco per inoltrare alla vista.
  • Solo uno TouchDelegate può essere impostato su un ViewGroup. Se vuoi lavorare con delegati a più contatti, scegli antenati diversi o usa un delegato per il tocco di composizione come spiegato in How To Use Multiple TouchDelegate.
0

Perché non mi piaceva l'idea di aspettare il passaggio di layout solo per ottenere la nuova dimensione del rettangolo del TouchDelegate, sono andato per una soluzione diversa:

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

E poi, in un Layout:

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

l'idea è che TouchSizeIncreaser FrameLayout sarà avvolgere lo Spinner (potrebbe essere qualsiasi bambino View) e in avanti tutti gli eventi touch catturati in è colpito rect al bambino View. Funziona per i clic, lo spinner si apre anche se cliccato fuori dai limiti, non è sicuro quali sono le implicazioni per altri casi più complessi.

Problemi correlati