Oke, saya rasa saya telah menemukan solusi pamungkas untuk ini. Karena AppCompat dan teman-teman, drawable yang disediakan terkadang digelembungkan dalam berbagai bentuk sehingga tidak cukup untuk dilakukan getResources().getBitmap(R.drawable.my_awesome_drawable)
.
Jadi, untuk mendapatkan instance drawable dari jenis dan bentuk yang sama seperti yang disediakan oleh tampilan, seseorang dapat melakukan ini:
public static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
Context context = view.getContext();
try {
View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
dummyView.setBackgroundResource(drawableId);
return dummyView.getBackground();
} catch (Exception e) {
return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
}
}
Ini berguna saat melakukan tes. Namun, saya tidak akan merekomendasikan melakukan ini dalam produksi. Jika Anda perlu, cache tambahan akan diinginkan untuk menghindari melakukan terlalu banyak refleksi.
Untuk pengujian Expresso, Anda dapat menggunakan ini dengan cukup baik:
onView(withDrawable(R.drawable.awesome_drawable))
.check(matches(isDisplayed()));
atau
onView(withId(R.id.view_id))
.check(matches(withDrawable(R.drawable.awesome_drawable)));
Sebelum Anda harus mendeklarasikan kelas helper ini:
public class CustomMatchers {
public static Matcher<View> withDrawable(@DrawableRes final int drawableId) {
return new DrawableViewMatcher(drawableId);
}
private static class DrawableViewMatcher extends TypeSafeMatcher<View> {
private final int expectedId;
private String resourceName;
private enum DrawableExtractionPolicy {
IMAGE_VIEW {
@Override
Drawable findDrawable(View view) {
return view instanceof ImageView ? ((ImageView) view).getDrawable() : null;
}
},
TEXT_VIEW_COMPOUND {
@Override
Drawable findDrawable(View view) {
return view instanceof TextView ? findFirstCompoundDrawable((TextView) view) : null;
}
},
BACKGROUND {
@Override
Drawable findDrawable(View view) {
return view.getBackground();
}
};
@Nullable
private static Drawable findFirstCompoundDrawable(TextView view) {
for (Drawable drawable : view.getCompoundDrawables()) {
if (drawable != null) {
return drawable;
}
}
return null;
}
abstract Drawable findDrawable(View view);
}
private DrawableViewMatcher(@DrawableRes int expectedId) {
this.expectedId = expectedId;
}
@Override
protected boolean matchesSafely(View view) {
resourceName = resources(view).getResourceName(expectedId);
return haveSameState(actualDrawable(view), expectedDrawable(view));
}
private boolean haveSameState(Drawable actual, Drawable expected) {
return actual != null && expected != null && areEqual(expected.getConstantState(), actual.getConstantState());
}
private Drawable actualDrawable(View view) {
for (DrawableExtractionPolicy policy : DrawableExtractionPolicy.values()) {
Drawable drawable = policy.findDrawable(view);
if (drawable != null) {
return drawable;
}
}
return null;
}
private boolean areEqual(Object first, Object second) {
return first == null ? second == null : first.equals(second);
}
private Drawable expectedDrawable(View view) {
return drawableFrom(view, expectedId);
}
private static Drawable drawableFrom(View view, @DrawableRes int drawableId) {
Context context = view.getContext();
try {
View dummyView = view.getClass().getConstructor(Context.class).newInstance(context);
dummyView.setBackgroundResource(drawableId);
return dummyView.getBackground();
} catch (Exception e) {
return ResourcesCompat.getDrawable(context.getResources(), drawableId, null);
}
}
@NonNull
private Resources resources(View view) {
return view.getContext().getResources();
}
@Override
public void describeTo(Description description) {
description.appendText("with drawable from resource id: ");
description.appendValue(expectedId);
if (resourceName != null) {
description.appendValueList("[", "", "]", resourceName);
}
}
}
}