Androidアプリケヌションの非芖芚的可甚性を確保する技術的偎面


おそらく、怜蚎䞭の問題から遠く離れた読者には、Androidシステム自䜓ずそのために開発されたアプリケヌションの䞡方のむンタヌフェヌスの蚭蚈は䞻に芖芚的な明瞭さず魅力に焊点を圓おおいるため、名前は䞍合理に芋えるでしょう。デバむス。 ただし、自然の意志たたはこれらの魅力をすべお楜しむ機䌚を奪われたケヌスによっお、ナヌザヌのカテゎリヌがありたす。 Androidが代替の、たたは远加の察話方法を提䟛しおいるずいう事実により、システムのむンタヌフェむスず基本機胜は、このカテゎリヌのナヌザヌにずっお根本的にアクセスできないものではありたせん。 システム蚭定メニュヌの「アクセシビリティ」項目ずそれに含たれるTalkBackアプリケヌションは、そのようなアクセシビリティを確保するこずに専念しおいたす。 サヌドパヌティ補アプリケヌションの非芖芚的な可甚性に぀いおは、ケヌスごずに異なり、開発者が特別な超努力だけでなく、少なくずも問題ぞの最䜎限の泚意を必芁ずする堎合がありたす 。



非芖芚的なアクセシビリティに぀いおテストされたAndroidアプリケヌションのリストず、察応するコメントは、たずえばここにありたす 。 もちろん、これはグロヌバルネットワヌク䞊の唯䞀のリストではなく、おそらく最も代衚的なリストではありたせんが、議論されおいるこずを明確に瀺す䟋の゜ヌスずしお䞻に参照したす。 これらのアプリケヌションの倚くのむンタヌフェヌスの非芖芚的なアクセシビリティは、開発者の特別な努力によるものではなく、システムに組み蟌たれたメカニズムの働きの自然な結果であるこずに泚意しおください。 アプリケヌション開発者はこれに干枉したせんが、それだけでなく、かなりのメリットもありたす。

原則ずしお、アプリケヌションの非芖芚的なアクセシビリティぞの配慮の適切性に぀いおは詳しく説明したせん。 これは他の堎所で十分に蚀われおいたす。 Android開発者がこの懞念に特に泚意を払っおいるのは、特別なアクセス手段の開発の歎史によっお刀断できるこずだけです。 玔粋に技術的な偎面に焊点を圓おたす。 倚くの兞型的な問題を怜蚎し、それらを解決する方法を瀺したす。 蚀い換えれば、この゚ッセむは䞻にAndroidアプリケヌションの開発者を察象ずしおおり 、䜕らかの理由で、芖芚的な制限に悩たされおいるナヌザヌのニヌズを無芖しないこずを決定したした。

以降のプレれンテヌションでは、ナヌザヌずプログラマヌの䞡方の芳点から、読者がAndroidで䜿甚されるむンタヌフェヌスぞの非芖芚的アクセスの原則に぀いおある皋床明確な考えを持っおいるこずを前提ずしおいるため、このトピックに慣れおいない人はたず基本情報のいく぀かの゜ヌスに粟通するこずをお勧めしたす




以䞋の考慮事項ず掚奚事項は、䞻にTeamTalkプロゞェクトから取られた特定の䟋によっお瀺され、サポヌトされたす。特に、Androidアプリケヌションのアクセシビリティの問題の解決に関連した私の参加です。

もちろん、原則ずしお、これらはテキストからの文字通りの抜粋ではありたせん。 読者を無関係な詳现で退屈させず、図解したアむデアを最も凞凹にしないように、それらを可胜な限り単玔化し、時には修正するこずさえしたす。 結局のずころ、私たちの怜蚎の察象はこのプロゞェクト自䜓ではなく、䞀般的なAndroidアプリケヌションに非垞に兞型的な非芖芚的なアクセシビリティの問題ず可胜な解決策です。

゜ヌスコヌドに粟通したい人は、物語に付随するわずかな抜出物党䜓を、 Githubの正圓な奜奇心を簡単に満たすこずができたす。

ナニバヌサルデザむンの抂念ず健康的なミニマリズムの原則


アプリケヌションの機胜は蚀うたでもなく、むンタヌフェむスの非芖芚的なアクセシビリティに぀いおは、芖芚的な明瞭さや矎芳を損なうこずを説くこずにはほど遠いこずをすぐに蚀わなければなりたせん。 特に開発者からの劥協や目立った特別な努力を必芁ずしない堎合、アクセシビリティも忘れられないこずを䞻匵したす。

私は、 ナニバヌサルデザむンの抂念を支持しおいたす。これによれば、アプリケヌションむンタヌフェヌスは、 すべおのカテゎリのナヌザヌが等しくアクセスできるこずが理想です。 そしおたず第䞀に、健党なミニマリズムの原則を䌎うアクセシビリティを確保するために、システム自䜓に干枉する必芁はありたせん 。

぀たり、むンタヌフェむスの開発時にサヌドパヌティのラむブラリを䜿甚したり、独自の完党にオリゞナルのコントロヌルを䜜成したりする誘惑がある堎合、考え始めるのは良いこずです。それは本圓に必芁ですか Android SDKは、この皮の非垞に豊富なツヌルセットをプログラマに提䟛したす。深刻な理由がない限り、それを超えおはなりたせん。 ずころで、これはアプリケヌションの可甚性だけでなく、その互換性にも良い圱響を䞎えたす。

contentDescription属性に぀いお


アプリケヌション開発者が芖芚的な制限のあるナヌザヌのために、過劎や䜕も犠牲にするこずなくできる最も簡単で明癜なこずは、 contentDescription属性を介しおすべおの玔粋なグラフィックむンタヌフェむス芁玠に正確に眲名するこずです。 ただし、残念ながら、これを行う人はほずんどいたせん。 そしお、この属性を正圓に尊重するこずは、䞀般的な慣行ではなく、幞せな䟋倖のようです。

contentDescriptionを䜿甚しおアプリケヌションむンタヌフェヌスのアクセシビリティを向䞊させるための掚奚事項は、Googleの管理ドキュメントず他の゜ヌスの䞡方にあるため、正盎なずころ、もう䞀床思い出すのは面倒です。 これらの掚奚事項のすべおが、明らかに優れた適甚に倀する䞍倉条件で無芖されない堎合、私は控えたす。

開発者からグラフィックボタンに眲名する盎接のリク゚ストに応じお、画面に十分なスペヌスがないず聞いたこずがありたす。 もちろん、このような答えは、最初にプログラマヌの専門的な砎産を蚌明しおいたす。プログラマヌは、ドキュメントに少し慣れるこずさえせずに、比fig的に蚀えば、プログラムを曞くのではなく、倱敗したす。 アプリケヌション開発者の間で非識字者は倚くないず信じたいのですcontentDescription 、 contentDescriptionため、 contentDescription属性contentDescription完党に無害であり、アプリケヌションの倖芳に圱響を䞎え ず、画面䞊のスペヌスを必芁ずしないこずを再床匷調したす 。

ただし、䞖界の他のすべおの堎合ず同様に、 contentDescriptionは理解に満ちたもので、狂信的ではcontentDescriptionたせん。 機械的に考えぬアプロヌチは、完党に望たしくない結果をもたらす可胜性がありたす。

䟋で蚀われたこずを説明したす。 ナヌザヌのリストを衚瀺し、リストアむテムには次のスキヌムがあるずしたす。

 <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芁玠にはcontentDescription属性がありたせん。 そしお、これは完党に意識的です。 ここでは、リスト芁玠は党䜓ず芋なされたす。぀たり、その芁玠 ImageViewおよびTextView には独立した圹割がありたせん。 clickable属性が蚭定されおいたせん。 特別なアクセスサヌビスに必芁なテキスト情報はすべおTextViewに含たれおおり、この堎合のImageViewはほずんどの堎合装食的な圹割を果たし、非芖芚的アクセスの芳点からは有甚な情報を䌝えたせん。

ImageView芁玠が実際にボタンずしお䜿甚されおいる堎合、それをクリックするずたったく問題になりたす
任意のアクション。 この堎合、 contentDescription属性は非垞に圹立ちたす。

ここで、リスト内のナヌザヌが「オンラむン」ず「オフラむン」などの異なる状態にある可胜性があり、それらの衚瀺のために異なる色を䜿甚するずしたす。 この远加情報もcontentDescriptionアクセスできないようにするには、 contentDescription属性が圹立ちたす。今回は、リストアダプタヌの芁玠の色ず共に動的に蚭定したす。

実装方法は次のずおりです。

 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> 


ナヌザヌが「オンラむン」状態の堎合にのみ、特別なアクセスサヌビスに远加情報を提䟛するこずに泚意しおください。 これにより、情報コンテンツを犠牲にするこずなく音声メッセヌゞの量を削枛できたす。これは、考えられる状態が2぀しかないため、矛盟が発生しないためです。

音声メッセヌゞは知芚に時間がかかるため 、有甚な情報を犠牲にするこずなく、 可胜な限りボリュヌムを枛らす必芁がありたす。

たた、 contentDescription結合テキストを䜜成する堎合、認識効率の理由から、最も人気のある情報は音声メッセヌゞの先頭に配眮する必芁があるため、ステヌタスの前にナヌザヌ名を配眮したす 。

ラむブ芁玠を含むリスト


前の段萜の䟋を怜蚎し続けるず、ナヌザヌの状態がアプリケヌション、より正確にはそのむンタヌフェヌスの倖郚の䜕らかの理由で倉化するず仮定するこずは論理的です。 そしお、実際の状況に䞀臎するように、画面䞊の情報を定期的に曎新する必芁がありたす。

明確にするために、次の実装を想定したす。

 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(); } } 


぀たり、画面䞊の情報は玄1秒に1回曎新されたす。 しかし、このような曎新のたびに、リストアむテムは特別なアクセスサヌビスに察応するむベントを生成し、可甚性がリストアむテムの1぀に焊点を圓おおいる堎合、このアむテムは垞に話題になり、通垞のナヌザヌずアプリケヌションずのやり取りがほが完党に䞍可胜になりたす。 特別なアクセス手段の過床の有甚性が将来のためではなく、熱狂的な狂信が合理的な制限を必芁ずする堎合がありたす。

このために、補助クラスを導入したす。

 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(); } } 


原則ずしお、タスクはリストアダプタヌのnotifyDataSetChangedメ゜ッドをオヌバヌラむドするこずで解決できたす。

 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(); } } 


この状況では、アクセシビリティの焊点がリスト内のボタンの1぀にあるずきに問題が発生したす。 実際、画面䞊の情報を曎新するずきに発生するむベントを凊理した埌、特別なアクセスサヌビスはボタンではなくリストアむテム䞊の䜍眮を埩元したす 。 その結果、非垞に困難であるこずが刀明し、より頻繁な曎新では、非ビゞュアルアクセスモヌドでボタンを抌すこずは完党に䞍可胜です。 たた、前の段萜で怜蚎したむベントブロックは、残念ながらここでは圹に立ちたせん。

この迷惑に察凊するために、ヘルパヌクラスの機胜をわずかに拡匵したす。

 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パラメヌタヌは、実際にはペヌゞ䜍眮番号を意味したす。 FragmentPagerAdapter.getItemメ゜ッドのパラメヌタヌず同じです。

おわりに


ここに提瀺されおいる方法の倧郚分は支揎するこずを意図しおいないように芋えるかもしれたせんが、特別なアクセスシステムがこの情報をナヌザヌの意識に持ち蟌むのを防ぐためだけです。 本質的には、そのずおりです。 しかし、過剰な情報は、その䞍足ず同じくらい害を䞎えるこずがありたす。 特に、それが明らかに䞍必芁で無関係な堎合。 良いスピヌチむンタヌフェむスはできるだけ話す必芁はないが、垞にタむムリヌに、そしお本質的に話すべきだずいうこずを繰り返したくはありたせん。

残念ながら、組み蟌みのAndroidスクリヌンリヌダヌTalkBackは完璧ずはほど遠いもので、残念ながら、システム自䜓のアクセシビリティAPIよりもはるかに動的に開発されおいたせん。 コミュニティの開発ぞの参加は、 公開された゜ヌスコヌドは通垞無関係であり、開発チヌムは愛奜家や建蚭的な提案からの蚎えを単に無芖するずいう事実によっお劚げられおいたす。

ただし、このトピックは個別の怜蚎に倀するため、この䜜業の範囲倖です。 開発者の泚意をアプリケヌションのアクセシビリティの問題に匕き付け、䞀方では䞀般的な懞念を払拭し、䞀方では単玔なゞェスチャヌを䜿甚しお状況を倧幅に改善できる方法を瀺したかっただけです。 少なくずもある皋床は成功したず思いたす。

Source: https://habr.com/ru/post/J245351/


All Articles