Ok, tôi nghĩ rằng tôi đã tìm ra giải pháp cuối cùng cho việc này. Vì AppCompat và những người bạn, phần có thể vẽ được cung cấp đôi khi bị thổi phồng ở các dạng khác nhau nên không đủ để làm getResources().getBitmap(R.drawable.my_awesome_drawable).
Vì vậy, để có được một thể hiện có thể vẽ cùng kiểu và biểu mẫu như được cung cấp bởi dạng xem, người ta có thể làm điều này:
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);
}
}
Điều này rất hữu ích khi làm các bài kiểm tra. Tuy nhiên, tôi không khuyên bạn nên làm điều này trong sản xuất. Nếu bạn cần, bạn nên sử dụng thêm bộ nhớ đệm để tránh phản ánh quá nhiều.
Đối với các bài kiểm tra Expresso, bạn có thể sử dụng điều này khá độc đáo:
onView(withDrawable(R.drawable.awesome_drawable))
.check(matches(isDisplayed()));
hoặc là
onView(withId(R.id.view_id))
.check(matches(withDrawable(R.drawable.awesome_drawable)));
Trước khi bạn phải khai báo lớp trợ giúp này:
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);
}
}
}
}