I have been working on stripping out a library that deals with adding Accessibility with Talkback that I have created in an existing app. Originally my custom views were all ViewGroups
, so I got everything working amazingly with ViewGroups
(focusable navigation with D-pad, initial view focus, and content descriptions)
When I was moving this to a standalone library, I noticed that it didn't work with View
. I thought ViewGroup
was the superclass, but it turns out that View
is the superclass. So I have been trying to find some workarounds to fix my issue. I started to do the following, and have a question based on this approach...
public class Accessibility {
public static ViewGroupAccessibility with(ViewGroup viewGroup) {
return new ViewGroupAccessibility(viewGroup);
}
public static ViewAccessibility with(View view){
return new ViewAccessibility(view);
}
}
I have fully implemented ViewGroupAccessibility
and I intend to fully implement ViewAccessibility
as it is a stub right now. So far the below code works well with TalkBack, I can do ViewGroup
related stuff with ViewGroups
, and it appears that I can do View
related stuff with Views
; however, I am wondering if this is even needed
Accessibility.with(new RelativeLayout(...)) // Returns ViewGroupAccessibility as RelativeLayout is a ViewGroup
//
...will return a ViewGroupAccessibility
that can handle ViewGroup
related stuff that can contain many different View
and ViewGroup
. (See code at the bottom of this post for real usage, and what what methods are available for ViewGroupAccessibility
)
Accessibility.with(new Button(...)) // Returns ViewAccessibility as Button is a View
//
...will return a ViewAccessibility
that can handle single View
only related stuff (that is my assumption). Think only a Button
.
// Hypothetical Usage
Accessibility
.with(new ClassThatExtendsView_WithMultipleComponentsThatCanHaveAccessibilitySetOnEachComponentIndividually(...));
// Custom View that extends View
public class ClassThatExtendsView_WithMultipleComponentsThatCanHaveAccessibilitySetOnEachComponentIndividually extends View {
...
}
ViewAccessibility
that can handle single View
only, but then that would be the wrong thing to return.Another way of asking the question is am I guaranteed that if a user calls Accessibility.with(View)
that the given view will ALWAYS be a single view only? Like Just a single Button. Or can the View be made of more than one component
You can check out the code at https://codereview.stackexchange.com/questions/134289/easily-add-accessibility-to-your-app-as-an-afterthought-yes-as-an-afterthought (there is also a GitHub link to the original code). I go in incredible detail into how the project was started, my design decisions, and my future goals all to help guide the code review process.
However, here is a snippet of a usage I have for ViewGroup
public class ContributionView extends RelativeLayout implements Mappable<Resume.Contribution> {
// Called from Constructors
private void init(AttributeSet attrs, int defStyle) {
root = (ViewGroup) LayoutInflater.from(getContext()).inflate(
R.layout.internal_contribution_view, this, true);
...
// Declare Navigation Accessibility
Accessibility.with(root)
// Disable certain views in the ViewGroup from ever gaining focus
.disableFocusableNavigationOn(
R.id.contribution_textview_creator,
R.id.contribution_textview_year)
// For all focusable views in the ViewGroup, set the D-pad Navigation
.setFocusableNavigationOn(txtProjectName)
.down(R.id.contribution_textview_description).complete()
.setFocusableNavigationOn(txtContributionDescription)
.up(R.id.contribution_textview_name)
.down(R.id.contribution_textview_link).complete()
.setFocusableNavigationOn(txtProjectLink)
.up(R.id.project_textview_description).complete()
// Set which view in the ViewGroup will have be first to be focused
.requestFocusOn(R.id.contribution_textview_name);
invalidateView();
}
private void invalidateView() {
...
// Declare Content Description Accessibility
Accessibility.with(root)
// Set the content descriptions for each focusable view in the ViewGroup
// Set the content description for the Contribution Name
.setAccessibilityTextOn(txtProjectName)
.setModifiableContentDescription(getProjectName())
.prepend("Contribution occurred on the Project called ")
.append(String.format(" by %s in %s",
getProjectCreator(),
getContributionYear())).complete()
// Set the content description for the Contribution Description
.setAccessibilityTextOn(txtContributionDescription)
.setModifiableContentDescription(getContributionDescription())
.prepend("Description: ").complete()
// Set the content description for the Contribution URL
.setAccessibilityTextOn(txtProjectLink)
.setModifiableContentDescription(getProjectLink())
.prepend("URL is ").complete();
}
...
}
Yes, there is a way to move accessibility amongst the various areas/components of a View. It requires a little work, though.
Start here: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeProvider.html