contentDescription
attributecontentDescription
attribute. However, unfortunately, very few people do it. And due respect for this attribute seems to be a happy exception, rather than common practice.contentDescription
to increase the accessibility of the application interface are found in Google’s governing documents and other sources, so, frankly, it’s even embarrassing to remind again. I would abstain if all these recommendations were not ignored with consistency worthy of a clearly better application.contentDescription
attribute contentDescription
completely harmless, it absolutely does not affect the appearance of the application and does not require screen space .contentDescription
should be approached with understanding and without fanaticism. A mechanically thoughtless approach is likely to lead to completely undesirable results. <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true"> <ImageView android:id="@+id/usericon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/user" /> <TextView android:id="@+id/nickname" android:textSize="16sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" /> </LinearLayout>
ImageView
in this scheme does not have the contentDescription
attribute. And it is completely conscious. The list element is considered here as a whole, that is, its parts ( ImageView
and TextView
) do not have an independent role: they do not have the clickable
attribute set. The text information required by the special access service is entirely contained in the TextView
, and the ImageView
in this case plays for the most part a decorative role and does not carry any useful information in terms of non-visual access.ImageView
element was actually used as a button, pressing which would causecontentDescription
attribute would be extremely useful.contentDescription
attribute will again help us to make this additional information, which this time we will dynamically set along with the element color in the list adapter. class UserListAdapter extends ArrayAdapter<User> { public UserListAdapter(Context context, int resource) { super(context, resource); } @Override public View getView(int position, View convertView, ViewGroup parent) { Context context = getContext(); LayoutInflater inflater = LayoutInflater.from(context); if (convertView == null) convertView = inflater.inflate(R.layout.item_user, null); User user = getItem(position); TextView nickname = (TextView) convertView.findViewById(R.id.nickname); nickname.setText(user.nickname); if (user.stateOnline) { convertView.setBackgroundColor(Color.rgb(133, 229, 141)); // , // contentDescription, // , // text. nickname.setContentDescription(context.getString(R.string.user_state_online, user.nickname)); } else { convertView.setBackgroundColor(Color.rgb(0, 0, 0)); // contentDescription, // text. nickname.setContentDescription(null); } return convertView; } }
<string name="user_state_online">%1$s online</string>
contentDescription
, we place the user's name before the designation of his state, because, for reasons of perceptual efficiency, the most requested information should be located at the beginning of the voice message . public class MainActivity extends Activity { private UserListAdapter userListAdapter; private CountDownTimer listUpdateTimer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); userListAdapter = new UserListAdapter(this, R.layout.item_user); listUpdateTimer = new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { userListAdapter.notifyDataSetChanged(); } @Override public void onFinish() { start(); } }; listUpdateTimer.start(); } }
public class AccessibilityAssistant extends AccessibilityDelegate { private final Activity hostActivity; private volatile boolean eventsLocked; public AccessibilityAssistant(Activity activity) { hostActivity = activity; eventsLocked = false; } // . public void lockEvents() { eventsLocked = true; } // , // , // . public void unlockEvents() { if (!hostActivity.getWindow().getDecorView().post(new Runnable() { @Override public void run() { eventsLocked = false; } })) eventsLocked = false; } @Override public void sendAccessibilityEvent(View host, int eventType) { if (!eventsLocked) super.sendAccessibilityEvent(host, eventType); } @Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { if (!eventsLocked) super.sendAccessibilityEventUnchecked(host, event); } }
public class MainActivity extends Activity { private AccessibilityAssistant accessibilityAssistant; private ArrayAdapter userListAdapter; private CountDownTimer listUpdateTimer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); accessibilityAssistant = new AccessibilityAssistant(this); userListAdapter = new ArrayAdapter(this, R.layout.item_user) { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, null); User user = getItem(position); TextView nickname = (TextView) convertView.findViewById(R.id.nickname); nickname.setText(user.nickname); if (user.stateOnline) { convertView.setBackgroundColor(Color.rgb(133, 229, 141)); nickname.setContentDescription(getString(R.string.user_state_online, user.nickname)); } else { convertView.setBackgroundColor(Color.rgb(0, 0, 0)); nickname.setContentDescription(null); } // , // . convertView.setAccessibilityDelegate(accessibilityAssistant); return convertView; } }; listUpdateTimer = new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { // accessibilityAssistant.lockEvents(); // userListAdapter.notifyDataSetChanged(); // // accessibilityAssistant.unlockEvents(); } @Override public void onFinish() { start(); } }; listUpdateTimer.start(); } }
public void notifyDataSetChanged() { accessibilityAssistant.lockEvents(); super.notifyDataSetChanged(); accessibilityAssistant.unlockEvents(); }
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true"> <ImageView android:id="@+id/usericon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/user" /> <TextView android:id="@+id/nickname" android:textSize="16sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:singleLine="true" /> </LinearLayout> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true"> <Button android:id="@+id/msg_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/button_msg" android:focusable="false" android:clickable="true" /> </LinearLayout> </RelativeLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:id="@+id/user_list" android:layout_width="match_parent" android:layout_height="wrap_content" /> <TextView android:id="@+id/count_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:singleLine="true" /> </LinearLayout>
public class MainActivity extends Activity { private int counter; private CountDownTimer updateTimer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); counter = 0; updateTimer = new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { ((TextView)findViewById(R.id.count_state)).setText(String.valueOf(++counter)); } @Override public void onFinish() { start(); } }; updateTimer.start(); } }
public class AccessibilityAssistant extends AccessibilityDelegate { private final Activity hostActivity; private volatile boolean eventsLocked; private final AccessibilityManager accessibilityService; // , // . private volatile boolean discourageUiUpdates; public AccessibilityAssistant(Activity activity) { hostActivity = activity; accessibilityService = (AccessibilityManager) activity.getSystemService(Context.ACCESSIBILITY_SERVICE); discourageUiUpdates = false; eventsLocked = false; } // , , // , . public boolean isUiUpdateDiscouraged() { return discourageUiUpdates && accessibilityService.isEnabled(); } public void lockEvents() { eventsLocked = true; } public void unlockEvents() { if (!hostActivity.getWindow().getDecorView().post(new Runnable() { @Override public void run() { eventsLocked = false; } })) eventsLocked = false; } @Override public void sendAccessibilityEvent(View host, int eventType) { // , . if (host instanceof Button) checkEvent(eventType); if (!eventsLocked) super.sendAccessibilityEvent(host, eventType); } @Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { // , . if (host instanceof Button) checkEvent(event.getEventType()); if (!eventsLocked) super.sendAccessibilityEventUnchecked(host, event); } // , private void checkEvent(int eventType) { switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: discourageUiUpdates = true; break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: discourageUiUpdates = false; break; default: break; } } }
public class MainActivity extends Activity { private AccessibilityAssistant accessibilityAssistant; private ArrayAdapter userListAdapter; private CountDownTimer updateTimer; private int counter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); accessibilityAssistant = new AccessibilityAssistant(this); counter = 0; userListAdapter = new ArrayAdapter(this, R.layout.item_user) { @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_user, null); User user = getItem(position); TextView nickname = (TextView) convertView.findViewById(R.id.nickname); Button button = (Button) convertView.findViewById(R.id.msg_btn); nickname.setText(user.nickname); if (user.stateOnline) { convertView.setBackgroundColor(Color.rgb(133, 229, 141)); nickname.setContentDescription(getString(R.string.user_state_online, user.nickname)); } else { convertView.setBackgroundColor(Color.rgb(0, 0, 0)); nickname.setContentDescription(null); } // , // . button.setAccessibilityDelegate(accessibilityAssistant); convertView.setAccessibilityDelegate(accessibilityAssistant); return convertView; } }; updateTimer = new CountDownTimer(10000, 1000) { @Override public void onTick(long millisUntilFinished) { // , // // . if (!accessibilityAssistant.isUiUpdateDiscouraged()) ((TextView)findViewById(R.id.count_state)).setText(String.valueOf(++counter)); } @Override public void onFinish() { start(); } }; updateTimer.start(); } }
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <android.support.v4.view.ViewPager android:id="@+id/pager" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1.0" > <android.support.v4.view.PagerTitleStrip android:id="@+id/pager_title_strip" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="top" android:background="#33b5e5" android:paddingBottom="4dp" android:paddingTop="4dp" android:textColor="#fff" /> </android.support.v4.view.ViewPager> </LinearLayout>
public class AccessibilityAssistant extends AccessibilityDelegate { private final Activity hostActivity; private final AccessibilityManager accessibilityService; // private SparseArray<View> monitoredPages; // , private View visiblePage; // private int visiblePageId; private volatile boolean discourageUiUpdates; private volatile boolean eventsLocked; public AccessibilityAssistant(Activity activity) { hostActivity = activity; accessibilityService = (AccessibilityManager) activity.getSystemService(Context.ACCESSIBILITY_SERVICE); monitoredPages = new SparseArray<View>(); visiblePage = null; visiblePageId = 0; discourageUiUpdates = false; eventsLocked = false; } public boolean isUiUpdateDiscouraged() { return discourageUiUpdates && accessibilityService.isEnabled(); } public void lockEvents() { eventsLocked = true; } public void unlockEvents() { if (!hostActivity.getWindow().getDecorView().post(new Runnable() { @Override public void run() { eventsLocked = false; } })) eventsLocked = false; } // // . , , // onCreateView() onViewCreated() . public void registerPage(View page, int id) { monitoredPages.put(id, page); if (id == visiblePageId) visiblePage = page; page.setAccessibilityDelegate(this); } public void setVisiblePage(int id) { visiblePageId = id; visiblePage = monitoredPages.get(id); } @Override public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child, AccessibilityEvent event) { return ((monitoredPages.indexOfValue(host) < 0) || (host == visiblePage)) && super.onRequestSendAccessibilityEvent(host, child, event); } @Override public void sendAccessibilityEvent(View host, int eventType) { if (host instanceof Button) checkEvent(eventType); if (!eventsLocked) super.sendAccessibilityEvent(host, eventType); } @Override public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) { if (host instanceof Button) checkEvent(event.getEventType()); if (!eventsLocked) super.sendAccessibilityEventUnchecked(host, event); } private void checkEvent(int eventType) { switch (eventType) { case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: discourageUiUpdates = true; break; case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: discourageUiUpdates = false; break; default: break; } } }
public class MainActivity extends Activity implements ViewPager.OnPageChangeListener { private AccessibilityAssistant accessibilityAssistant; private ViewPager viewPager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); accessibilityAssistant = new AccessibilityAssistant(this); viewPager = (ViewPager) findViewById(R.id.pager); viewPager.setOnPageChangeListener(this); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { } @Override public void onPageSelected(int position) { accessibilityAssistant.setVisiblePage(position); } @Override public void onPageScrollStateChanged(int state) { } }
public class PageFragment extends Fragment { @Override public void onViewCreated(View view, Bundle savedInstanceState) { ((MainActivity)getActivity()).accessibilityAssistant.registerPage(view, PAGE_NUMBER); super.onViewCreated(view, savedInstanceState); } }
PAGE_NUMBER
parameter here actually means the positional page number. The same as the parameter of the FragmentPagerAdapter.getItem () method.Source: https://habr.com/ru/post/245351/
All Articles