I'm trying to implement a custom lint rule that checks xml files and reports an issue if some UI elements' width or height is less than a certain DP amount. It seems to work just fine if you perform ./gradlew lintDebug
, but I can't see any results and/or suggestions on UI in Android Studio.
I am following these materials https://proandroiddev.com/implementing-your-first-android-lint-rule-6e572383b292, https://www.youtube.com/watch?v=jCmJWOkjbM0 as well as browsing the built-in lint rules sources.
@SuppressWarnings("UnstableApiUsage")
public class ElementSizeScanner extends ResourceXmlDetector implements XmlScanner {
private static final Implementation IMPLEMENTATION = new Implementation(ElementSizeScanner.class, Scope.RESOURCE_FILE_SCOPE);
public static final Issue TOO_SMALL_AREA_ISSUE = Issue.create(
"TooSmallArea",
"The dimension must be at least 44dp",
"Using clickable elements which dimensions are smaller than 44dp may cause accessibility issues",
Category.A11Y,
7,
Severity.ERROR,
IMPLEMENTATION
);
private static final int MINIMAL_SIZE = 44;
private static final List<String> LAYOUT_ELEMENTS = Arrays.asList(SdkConstants.TEXT_VIEW,
SdkConstants.IMAGE_VIEW, SdkConstants.BUTTON, SdkConstants.FRAME_LAYOUT);
private final Map<String, String> dimens = new HashMap<>();
@Nullable
@Override
public Collection<String> getApplicableElements() {
List<String> applicableElements = new ArrayList<>(LAYOUT_ELEMENTS);
applicableElements.add(SdkConstants.TAG_DIMEN);
return applicableElements;
}
@Override
public boolean appliesTo(@NotNull ResourceFolderType folderType) {
return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.VALUES;
}
@Override
public void visitElement(@NotNull XmlContext context, @NotNull Element element) {
super.visitElement(context, element);
if (context.getPhase() == 1) {
if (element.getTagName().equals(SdkConstants.TAG_DIMEN)) {
String name = element.getAttribute(SdkConstants.ATTR_NAME);
String value = element.getFirstChild().getNodeValue();
dimens.put(name, value);
}
} else if (!dimens.isEmpty()) {
if (LAYOUT_ELEMENTS.contains(element.getTagName())) {
DimensionChecker dimensionChecker = new DimensionChecker(MINIMAL_SIZE, context, dimens);
String width = element.getAttribute(SdkConstants.ANDROID_NS_NAME_PREFIX + SdkConstants.ATTR_LAYOUT_WIDTH);
String height = element.getAttribute(SdkConstants.ANDROID_NS_NAME_PREFIX + SdkConstants.ATTR_LAYOUT_HEIGHT);
dimensionChecker.checkDimension(element, SdkConstants.ATTR_LAYOUT_WIDTH, width);
dimensionChecker.checkDimension(element, SdkConstants.ATTR_LAYOUT_HEIGHT, height);
}
}
}
@Override
public void afterCheckRootProject(@NotNull Context context) {
super.afterCheckRootProject(context);
// Phase 1 - collect dimen values
// Phase 2 - check dimen values
if (context.getPhase() == 1) {
context.requestRepeat(this, Scope.RESOURCE_FILE_SCOPE);
}
}
}
@SuppressWarnings("UnstableApiUsage")
public class DimensionChecker {
private final int minSize;
private final XmlContext context;
private final Map<String, String> dimens;
public DimensionChecker(int minSize,
XmlContext context,
Map<String, String> dimens) {
this.minSize = minSize;
this.context = context;
this.dimens = dimens;
}
public void checkDimension(Element element, String dimensionName, String dimension) {
// some checks here
double doubleValue;
try {
doubleValue = parseDimenValue(dimenValue);
} catch (NumberFormatException e) {
return;
}
if (doubleValue < minSize) {
LintFix lintFix = LintFix.create()
.set(ANDROID_URI, dimensionName, minSize + SdkConstants.UNIT_DP)
.build();
context.report(
ElementSizeScanner.TOO_SMALL_AREA_ISSUE,
element,
context.getValueLocation(element.getAttributeNodeNS(ANDROID_URI, dimensionName)),
"The area is too small to click",
lintFix
);
}
}
private double parseDimenValue(String value) throws NumberFormatException {
// ...
}
}
The rule is applied to the project with lintChecks project(path: ':rules')
What might I be missing here?
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…