2015-03-31 9 views
26

Avere gridView con alcune immagini. La cella di gridView esce dallo stesso layout predefinito, che ha lo stesso id e desc.in Espresso, come scegliere una vista che ha lo stesso ID per evitare AmbiguousViewMatcherException

R.id.item_image == 2131493330

onView(withId(is(R.id.item_image))).perform(click()); 

Poiché tutte le celle della griglia hanno lo stesso id, esso ottenuto AmbiguousViewMatcherException. Come prendere solo il primo o nessuno dei due? Grazie!

android.support.test.espresso.AmbiguousViewMatcherException: 'con id: è < 2.131.493,33 mille>' corrisponde a più viste nella gerarchia. Le viste dei problemi sono contrassegnate con '**** MATCHES ****' di seguito.

+ -------------> ImageView {id = 2131493330, res-name = item_image, desc = Immagine, visibilità = VISIBILE, larghezza = 262, altezza = 262, has-focus = falso, has-focusable = false, has-window-focus = true, is-clickable = false, is-enabled = true, is-focused = false, is-focusable = false, is-layout-requested = false, is -selected = false, root-is-layout-requested = false, has-input-connection = false, x = 0.0, y = 0.0} **** MATCHES ****

+ ----- --------> ImageView {id = 2131493330, res-name = item_image, desc = Immagine, visibilità = VISIBILE, larghezza = 262, altezza = 262, has-focus = false, has-focusable = false, ha -window-focus = true, is-clickable = false, is-enabled = true, is-focused = false, is-focusable = false, is-layout-requested = false, is-selected = false, root-is-layout -requested = false, has-input-connection = false, x = 0.0, y = 0 .0} **** MATCHES **** |

risposta

16

Si dovrebbe usare onData() di operare su GridView:

onData(withId(R.id.item_image)) 
     .inAdapterView(withId(R.id.grid_adapter_id)) 
     .atPosition(0) 
     .perform(click()); 

Questo codice cliccare sull'immagine interna prima voce GridView

+0

grazie! lo proverò presto Penso che sia la risposta. – lannyf

+0

Ciao, è 'R.id.grid_adapter_id' l'effettivo' RecyclerView'/'ListView'? Grazie! –

+2

@NeonWarge, yes, id di GridView o può anche essere ListView. Ma non Recycler View. Non puoi usare 'onData()' con esso. – denys

12

ho creato un ViewMatcher che corrisponde al primo punto di vista che trova. Forse è utile per qualcuno. E.g. quando non si dispone di un AdapterView per l'uso di onData().

/** 
* Created by stost on 15.05.14. 
* Matches any view. But only on first match()-call. 
*/ 
public class FirstViewMatcher extends BaseMatcher<View> { 


    public static boolean matchedBefore = false; 

    public FirstViewMatcher() { 
     matchedBefore = false; 
    } 

    @Override 
    public boolean matches(Object o) { 
     if (matchedBefore) { 
      return false; 
     } else { 
      matchedBefore = true; 
      return true; 
     } 
    } 

    @Override 
    public void describeTo(Description description) { 
     description.appendText(" is the first view that comes along "); 
    } 

    @Factory 
    public static <T> Matcher<View> firstView() { 
     return new FirstViewMatcher(); 
    } 
} 

usare in questo modo:

onView(FirstViewMatcher.firstView()).perform(click()); 
+0

Si sta provando a modificarlo per restituire il primo di un particolare tipo di visualizzazione. Pensieri? – wapples

+2

@wapples Depends ... se si desidera abbinare un tipo di vista e tutti i tipi che ne ereditano si può usare qualcosa come "if (o instanceof ClassToMatch.class)" nel metodo matches() - methode. Se vuoi abbinare solo una classe specifica potresti fare qualcosa come 'if (" ClassToMatchAsString ".equals (o.getClass(). GetName()))' o 'if (" ClassToMatchAsString ".equals (o.getClass() .getSimpleName())) '. O se non si desidera modificare il codice, dovrebbe funzionare anche: 'onView (allOf (withClassName (endsWith (" ClassToMatchAsString ")), FirstViewMatcher.firstView())). Perform (click());' – StefanTo

13

Non del tutto correlata alla griglia situazione vista ma è possibile utilizzare hamcrest allOf matchers per combinare più condizioni:

import static org.hamcrest.CoreMatchers.allOf; 

onView(allOf(withId(R.id.login_password), 
      withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE))) 
     .check(matches(isCompletelyDisplayed())) 
     .check(matches(withHint(R.string.password_placeholder))); 
48

Ero stupito che io non è stato possibile trovare una soluzione semplicemente fornendo un indice insieme a un matcher (es. withText, withId). La risposta accettata risolve il problema solo quando hai a che fare con onData e ListViews.

Se si dispone di più di una vista sullo schermo con lo stesso resid/text/contentDesc, è possibile scegliere quale quello che si desidera, senza causare un AmbiguousViewMatcherException utilizzando questo matcher personalizzato:

public static Matcher<View> withIndex(final Matcher<View> matcher, final int index) { 
    return new TypeSafeMatcher<View>() { 
     int currentIndex = 0; 

     @Override 
     public void describeTo(Description description) { 
      description.appendText("with index: "); 
      description.appendValue(index); 
      matcher.describeTo(description); 
     } 

     @Override 
     public boolean matchesSafely(View view) { 
      return matcher.matches(view) && currentIndex++ == index; 
     } 
    }; 
} 

Ad esempio:

onView(withIndex(withId(R.id.my_view), 2)).perform(click()); 

eseguirà un'azione di clic sulla terza istanza di R.id.my_view.

+0

funziona bene. Grazie. – rpattabi

+0

Questa è una grande soluzione, stavo cercando questo perché la mia vista non è una vista dell'adattatore, stiamo usando la visualizzazione lista appiccicosa che implementa la lista senza una vista elenco. –

+0

brillante. Ho anche avvolto questa soluzione in versioni come first() e second(). – Ivan

2

Casi:

onView(withId(R.id.songListView)).perform(RealmRecyclerViewActions.scrollTo(Matchers.first(Matchers.withTextLabeled("Love Song")))); 
onView(Matchers.first(withText("Love Song"))).perform(click()); 

dentro la mia Matchers.class

public static Matcher<View> first(Matcher<View> expected){ 

    return new TypeSafeMatcher<View>() { 
     private boolean first = false; 

     @Override 
     protected boolean matchesSafely(View item) { 

      if(expected.matches(item) && !first){ 
       return first = true; 
      } 

      return false; 
     } 

     @Override 
     public void describeTo(Description description) { 
      description.appendText("Matcher.first(" + expected.toString() + ")"); 
     } 
    }; 
} 
7

provato risposta @FrostRocket come era alla ricerca più promettente, ma aveva bisogno di aggiungere alcune personalizzazioni:

public static Matcher<View> withIndex(final Matcher<View> matcher, final int index) { 
    return new TypeSafeMatcher<View>() { 
     int currentIndex; 
     int viewObjHash; 

     @SuppressLint("DefaultLocale") @Override 
     public void describeTo(Description description) { 
      description.appendText(String.format("with index: %d ", index)); 
      matcher.describeTo(description); 
     } 

     @Override 
     public boolean matchesSafely(View view) { 
      if (matcher.matches(view) && currentIndex++ == index) { 
       viewObjHash = view.hashCode(); 
      } 
      return view.hashCode() == viewObjHash; 
     } 
    }; 
} 
+0

ciao, perché hai dovuto aggiungere il controllo del codice hash? Questa versione funziona per me, ma non ero sicuro del motivo per cui è necessario farlo. Grazie! – rfodge

+0

Ciao, devo dire che non ricordo esattamente. Penso che fosse qualcosa a che fare con la vista di essere mutato mentre era sotto test ma non ne sono sicuro ora. – fada21

+0

Penso che il motivo sia che il matcher potrebbe essere chiamato più volte sullo stesso componente. Mantenere un riferimento alla vista (invece del codice hash) dovrebbe funzionare anche in questo caso. –

Problemi correlati