簡介
當處理列表時(如RecyclerView or AdapterView)時,你想要的元素並不在屏幕上,因爲列表的特性。
Espresso提供了特殊的方法來處理兩類列表:
- RecyclerView
- AdapterView
AdapterView
繼承AdapterView的類有很多,如ListView,GridView,Spinner,等。
而尋找元素使用onData().
例子
例子使用ListView。採用兩個Adapter:SimpleAdapter和自定義Adapter。
SimpleAdapter
代碼
public class ListViewActivity extends AppCompatActivity{
private ListView lt1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
List<Map<String, String>> listems = new ArrayList<>();
for (int i = 0; i < 50; i++) {
Map<String, String> listem = new HashMap<>();
listem.put("STR", String.format(Locale.getDefault(), "item %d", i));
listem.put("LEN", String.valueOf(i));
listems.add(listem);
}
SimpleAdapter adapter = new SimpleAdapter(this, listems,
R.layout.adapter_simple, new String[] { "STR", "LEN"},
new int[] {R.id.simple_item,R.id.simple_len});
lt1=(ListView)findViewById(R.id.list);
lt1.setAdapter(adapter);
}
}
測試代碼
@RunWith(AndroidJUnit4.class)
public class ListViewActivityTest {
@Rule
public ActivityTestRule<ListViewActivity> mActivityRule = new ActivityTestRule<>(
ListViewActivity.class);
@Test
public void findItemAndClick(){
onData(allOf(is(Matchers.instanceOf(Map.class)),
hasEntry(equalTo("STR"), is("item 40")))).perform(click());
}
@Test
public void findItemAndClickCustom() {
onData(withItemContent("item 40")).perform(click());
}
@Test
public void findItemAndClickChild() {
onData(withItemContent("item 40")).onChildView(withId(R.id.simple_len)).perform(click());
}
public static Matcher<Object> withItemContent(final Matcher<String> itemTextMatcher) {
return new BoundedMatcher<Object, Map>(Map.class) {
@Override
public boolean matchesSafely(Map map) {
return hasEntry(equalTo("STR"), itemTextMatcher).matches(map);
}
@Override
public void describeTo(Description description) {
description.appendText("with item content: ");
itemTextMatcher.describeTo(description);
}
};
}
public static Matcher<Object> withItemContent(String expectedText) {
return withItemContent(equalTo(expectedText));
}
}
自定義Adapter
public class ListViewActivity2 extends AppCompatActivity {
private ListView lt1;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listview);
List<SearchItem> listems = new ArrayList<>();
for (int i = 0; i < 50; i++) {
SearchItem item = new SearchItem();
item.setKeyword(String.format(Locale.getDefault(), "item %d", i));
listems.add(item);
}
MyAdapter adapter = new MyAdapter(this, listems);
lt1=(ListView)findViewById(R.id.list);
lt1.setAdapter(adapter);
}
public class ViewHolder {
TextView mText;
}
public class MyAdapter extends BaseAdapter {
private LayoutInflater mInflater;
private List<SearchItem> mItems;
public MyAdapter(Context context, List<SearchItem> items) {
this.mInflater = LayoutInflater.from(context);
mItems = items;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mItems.size();
}
@Override
public SearchItem getItem(int arg0) {
// TODO Auto-generated method stub
return mItems.get(arg0);
}
@Override
public long getItemId(int arg0) {
// TODO Auto-generated method stub
return arg0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// 顯示優化(只要之前顯示過的就可以不再再次從佈局文件讀取,直接從緩存中讀取——ViewHolder的作用)
// 其實是setTag和getTag中Tag的作用
ViewHolder holder = null;
if (convertView == null) {// 如果是第一次顯示該頁面(要記得保存到viewholder中供下次直接從緩存中調用)
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.adapter_simple2, null);
// 以下爲保存這一屏的內容,供下次回到這一屏的時候直接refresh,而不用重讀佈局文件
holder.mText = (TextView) convertView.findViewById(R.id.simple2_text);
convertView.setTag(holder);
} else {// 如果之前已經顯示過該頁面,則用viewholder中的緩存直接刷屏
holder = (ViewHolder) convertView.getTag();
}
SearchItem item = mItems.get(position);
holder.mText.setText(item.getKeyword());
return convertView;
}
}
}
public class SearchItem {
private String keyword;
public SearchItem() {}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public String getKeyword() {
return keyword;
}
}
測試代碼
@RunWith(AndroidJUnit4.class)
public class ListViewActivity2Test {
@Rule
public ActivityTestRule<ListViewActivity2> mActivityRule = new ActivityTestRule<>(
ListViewActivity2.class);
@Test
public void findItemAndClick(){
onData(allOf(is(instanceOf(SearchItem.class)),
teacherSearchItemWithName("item 40"))).perform(click());
onData(allOf(is(instanceOf(SearchItem.class)), teacherSearchItemWithName("item 40")))
.inAdapterView(withId(R.id.list)).perform(click());
}
public static Matcher<Object> teacherSearchItemWithName(final String name) {
return new BoundedMatcher<Object, SearchItem>(SearchItem.class) {
@Override
protected boolean matchesSafely(SearchItem item) {
return item != null
&& !TextUtils.isEmpty(item.getKeyword())
&& item.getKeyword().equals(name);
}
@Override
public void describeTo(Description description) {
description.appendText("SearchItem has Name: " + name);
}
};
}
}
區別
兩個adapter一個採用map,一個採用SearchItem,在測試代碼中都是用 instance(map.class)和instance(SearchItem)來尋找需要的對應的adapter,進而找到元素。即時通過數據匹配找元素。
onData尋找元素時會自動滾動去顯示匹配元素。
RecyclerView
對RecycleView採用onView先得到RecycleView這個元素,然後通過perform執行動作更新子元素。