Android UI/UX アンチパターン

参考資料

Android Developers

画面全般

スプラッシュ画面

必要のないスプラッシュ画面の表示はやめましょう。

なぜダメか

多くのアプリにおいて、スプラッシュ画面では何も処理していません。 このようなアプリでは、ユーザがアプリを起動してからスプラッシュ画面の表示が終わるまでの間、ユーザの時間を単純に浪費してしまいます。 わずかな時間かもしれませんが、Androidではアプリの起動/終了が頻繁に繰り返される場合があるため、デスクトップアプリと比べて表示される頻度が多くなることを意識する必要があります。

解決法

スプラッシュ画面を削除する。 余談ですが、iOSのLaunchImagesは体感上の起動時間を短縮するためにアプリ起動後の画面と似たものを使用することが推奨されています。 Androidアプリにおいて、体感上の起動時間を短縮するためにできることはスプラッシュ画面を削除することです。

iOS Human Interface Guidelines: Launch Images

例外

起動時に重い処理をする必要があるアプリ、特にゲームアプリなどはスプラッシュ画面を設けるべきです。 このようなアプリでは起動から実際に動作が可能になるまでタイムラグが生じるため、可能な限り迅速に何らかの画面を表示する必要があります。

Keeping Your App Responsive | Android Developers

時代遅れのUI

以下の要素はUIを時代遅れと感じさせます。

  • ナビゲーションバーのメニューボタン
  • Gingerbread時代のtheme/style
  • ActionBarを使用していない

なぜダメか

ナビゲーションバーのメニューボタンは古い端末におけるメニューボタンを模したもので、「Menu Button of Shame」とも呼ばれています。 Menu Button of Shameの機能は最近のUIではActionBarのMenuItem、またはオーバーフローメニューに集約されています。

Gingerbread時代のtheme/styleはアプリ全体を古臭くみせてしまいます。 アプリ全体がHoloベースになっていても一部部品が古いままだったりと見栄えが悪いので、細部にも気を配りましょう。

ActionBarを使用せず、TitleBarが表示されてしまうのもアプリを古臭く見せる要因です。 Support Libraryにv7-appcompatが追加されてから1年以上経過しており、ActionBarはすでに一般的な実装になっています。

解決法

  • targetSdkVersionを最新にする
  • ThemeをHoloやAppCompatにする
  • ActionBarを導入する

他のプラットフォームの模倣

以下のような実装は他プラットフォームの模倣です。

  • 下タブバー
  • リスト項目の右向き矢印
  • 非標準ナビゲーション(独自のActionBar、メニューボタン)

Pure Android | Android Developers

なぜダメか

他のプラットフォームを模倣したデザインはAndroidプラットフォームデザインの一貫性を損ないます。 iOSにおいて下タブバーが標準的なUIであるように、Androidでは画面上部のタブバーやNavigationDrawerが一般的です。

リスト項目の右向き矢印に関しては、Androidでは不要です。 直感的だと言われていますが、画面遷移アニメーションが矢印にあわせて実装されていることはほとんどありません。

非標準ナビゲーションはプラットフォームの一貫性を損なうだけでなく、多くの場合拡張性も犠牲になっています。

解決法

まず、Androidプラットフォームの標準的な実装を心がけるべきです。 下タブバーはActionBarのタブ機能を使うか、NavigationDrawerを利用してください。 FragmentTabHostやPagerTitleStripも利用できます。

リスト項目の右向き矢印は削除してください。

非標準ナビゲーションはActionBarに置き換えてください。 ActionBarはある程度カスタムUIを含めることができ、多機能で、拡張性に優れます。

縦向き固定・電話限定

アプリ画面の縦向き固定・スマートフォンのみ対応は賢明ではありません。

なぜダメか

縦向き固定のアプリは純粋に使い勝手が悪いです。 スマートフォンでしか使えないアプリはタブレットユーザに対する機会損失です。 Androidタブレットの普及率は高く、無視するには惜しいレベルです。

タブレットの国内出荷台数は713万台、iOSとAndroidのシェアは1%の僅差 - ケータイ Watch

解決法

Androidではさまざまな解像度に対応するための仕組みが用意されています。 dpで設計し、拡縮範囲、スクロール範囲を意識していれば横画面でも崩れないレイアウトを作るのは難しくありません。 タブレット対応は難しく感じますが、最初から完全なマルチペインが必須なわけでもありません。 表示崩れを防ぎ、タブレットでも十分に操作できるというレベルにするのは他画面対応できていれば思ったよりも簡単です。

Supporting Multiple Screens | Android Developers

Designing for Multiple Screens | Android Developers

ダイアログ

確認ダイアログの多用

あるアクションに対しそれを続行するかどうかを選択させるダイアログのことを確認ダイアログといいます。 確認ダイアログを多用しないでください。

なぜダメか

Androidにおいて、確認ダイアログを利用すべきシチュエーションは2パターンのみです。

  1. ユーザがその処理を実行する前に結果を警告する必要がある場合(決済やリセットなど)
  2. ユーザが誤って実行した場合に不利益となり、かつ技術的に元に戻せない場合(データ削除など)

これ以外のシチュエーションにおいて、ユーザに確認ダイアログを表示する処理は非推奨となります。

Confirming & Acknowledging | Android Developers

解決法

Confirming & Acknowledgingの内容に従って実装する必要があります。 その上で誤操作が頻繁に起きるような場合、UI上の混乱が原因となっている場合が多いので、UIの見直しを検討してください。 ActionBarのメニューにする、アイコンをわかりやすいものに変更するなど確認ダイアログに頼らない方法があるかもしれません。 事前に防ぐだけではなく、アクション後に取り消せるような動線を用意するのも良いと思います。

読み込み中ダイアログの多用

読み込み中ダイアログ(プログレスダイアログ)とは、非同期処理の実行中に表示するダイアログを指します。

なぜダメか

読み込み中ダイアログは、多くの場合、キャンセルが無効(setCancelable(false))にされています。 この場合では非同期処理であってもユーザが画面を操作できないため、ユーザ体験を損ないます。 キャンセル可能なダイアログであっても頻繁に表示されたり、時間のかかる処理が誤タッチで中断してしまうと悪印象になる恐れがあります。 また、実装上の問題として読み込み中ダイアログをDialogFragmentで実装する場合に処理が複雑になりがちです。

解決法

SwipeRefreshLayoutなどを利用し、画面操作を必要以上に阻害しない形で読み込み中表示を行います。 SwipeRefreshLayout#setEnabled(false)で、スワイプ更新機能を無効にしてプログレス表示機能のみを利用することができます。 ListFragmentは標準でsetAdapterされるまで読み込み中表示を行う機能があるため、これを利用することも検討してください。

例外

仕様上、どうしてもキャンセルさせたくない処理というものも存在します。 IntentServiceを利用する方法もありますが、画面と密に連動している場合などやはり読み込み中ダイアログを利用したほうが良い場面もあります。 読み込み中ダイアログをすべて無くそうとするよりも、必要な箇所だけに限定できないか検討してください。

ダイアログの独自アクションボタン

Androidでは通常、ダイアログ下部に「OK」「キャンセル」といったアクションボタンを表示します。 これらとは別に、カスタムレイアウトを利用してダイアログ内に独自のアクションボタンを配置することがあります。

なぜダメか

Androidのダイアログアクションボタンの配置はOSのバージョンによって異なります。 古い端末では左側が肯定ボタンでしたが、ICSからは右側が肯定ボタンになっています。 独自にこれらのボタンを実装する場合、バージョンごとにレイアウトファイルを用意しない限り、OS標準から外れることになります。

解決法

標準のダイアログアクションボタンを使用します。 AlertDialog.Builder#setPositiveButtonなどを利用することで、OSバージョンによって適切な表示を行ってくれます。

例外

凝ったカスタムダイアログを表示したい場合など、OS標準のアクションボタンではダメな場合があります。 その場合でもなるべくシステム内の一貫したユーザ体験を損なわないように注意してください。

操作に関して

タッチ領域が小さすぎる

ボタンなど、タッチ可能なView要素の表示領域は一定以上必要です。

なぜダメか

うまくタッチできないView要素はユーザ体験を損ないます。 以前は縦横共に48dp以上必要となっていましたが、Material Designガイドラインでは一部違う例もあります。

Metrics and Grids | Android Developers

解決法

基本的には表示サイズを一定以上にする必要があります。 どうしても表示サイズを拡大できない場合、TouchDelegateを利用してタッチ領域を広げる方法を検討してください。

Managing Touch Events in a ViewGroup | Android Developers

タッチ時のフィードバックがない

タッチ可能なView要素では、タッチフィードバック(タッチした際のView要素の状態変化)の実装が必要です。

なぜダメか

タッチパネルではマウスと違い明確なタッチポイントがわかりません。 ユーザにどのView要素がタッチされたかを通知するために、タッチフィードバックの実装が必須となります。 特に画面遷移を行うような場合では間違えてタッチしても画面遷移が終わるまでユーザにわからないため、悪印象となります。

解決法

Viewの状態(states)によって見た目が変わるように設定します。

Touch Feedback | Android Developers

AndroidTVやChromebookなど、将来的にタッチパネルでない端末も増えることが予想されるので、pressedだけでなく、focusedも実装しましょう。 Material Designガイドラインではstatesによる描画の切り替えだけではなくアニメーションによるフィードバックも行うようになります。

Responsive Interaction - Animation - Google design guidelines

ジェスチャの間違った利用

Viewの長押しで独自のアクションを実行する、リストアイテムをスワイプして削除ボタンを表示させるというような動作は推奨されません。

なぜダメか

Androidにおいて、ジェスチャの種類とそれぞれのジェスチャがどのような動作をすべきかは決められています。

Gestures | Android Developers これらのジェスチャを異なる意味に用いるとユーザが混乱する場合があります。

解決法

たとえばメニューアイテムの削除であれば、削除(Long Press)によってContextual Actionを呼び出し、複数選択して削除できるような実装にするのが自然です。 単独でアクションさせる場合はfloating context menuによって操作を選ばせる方法もありますが、ジェスチャページではContextual Menuのために長押し操作を利用するなと書いてあるため、なるべくContextual Actionによる実装を検討してください。

Menus | Android Developers

例外

GmailのSwipe-to-Dismiss(スワイプすると削除)のように、標準ジェスチャと異なる実装であっても問題ない場合もあります。 その場合はViewPagerなど既存のジェスチャと競合しないこと、他のアンチパターンに抵触しないことに気をつけてください。 例えばSwipe-to-Dismissの場合、削除時に確認ダイアログを出すとテンポが悪くなります。

共有機能の独自実装

SNSごとに共有ボタンを置くような実装は推奨されません。

なぜダメか

これらは多くの場合、仕様を決める際の考慮漏れです。 本当に必要なのはTwitter,Facebookなど特定のSNSへの投稿ではなく、Twitter/Facebookを含むSNSなどへの拡散ではないでしょうか。 そうであった場合、MixiやG+など他のSNSへ拡散される機会を損失しています。 このような仕様は避けるべきです。

Pure Android | Android Developers

解決法

単純に他アプリへ共有したい場合、暗黙的Intentを発行すれば良くなります。 ユーザの利便性を考慮し、ShareActionProviderを利用することもできます。 どうしても明示的にTwitter/Facebookに投稿させたい場合、アプリがSDKを使用して直接投稿すべきです。

Adding an Easy Share Action | Android Developers

何らかの理由で特定アプリのみ挙動を変えたい場合、Intent.createChooserで対応できる場合があります。

Y.A.M の 雑記帳: Facebookとそれ以外でACTION_SENDで渡すテキストを変える

例外

最終的には連携先のアプリによってしまうので、どうしても解決できない場合もあります。

Androidソースコードレビューで指摘する事が多い項目まとめ

業務でソースコードレビューを行う機会が増えたので、複数回指摘した項目や気になった実装などをまとめてみました。 こういう観点をできる人と共有できるといいなあ…。

2014/09/29 23:00 一部修正しました。

業務上ソースコードレビューの名目で仕様・デザインまで見ることになっていたためこれらを先頭に書いていましたが、わかりづらかったため最後にまとめました。

Fragment関連

FragmentとActivityの密結合

Fragmentが特定のActivityから呼ばれることを想定して書かれている場合、そのFragmentActivityは密結合である場合が多いです。 具体的には、以下の様な実装です。

  • ActivityのViewを参照する
  • Activityメソッドを直接呼び出す

なぜダメか

Fragmentの利点のひとつは優れた再利用性にあります。 Fragmentが特定のActivityに依存することで、Fragmentの再利用を妨げます。 現時点でアプリ内で再利用しないという場合でも、将来においてメンテナンスが困難になる可能性があります。

解決法

ActivityのViewを参照している場合、そのViewをFragmentに配置できないか検討してください。 ActionBarのタイトルやMenuItemFragmentでも操作できます。 Fragment内の特定のタイミングでActivityのViewを操作したい場合、後述するコールバックでの処理を検討してください。 どうしてもActivityと密結合になってしまう場合、FragmentではなくCustomViewとして実装したほうが良い場合もあります。

Activityメソッドを直接読んでいる場合、コールバックインターフェイスによる実装を検討してください。

if(getActivity() instanceof MyFragmentCallback){
    ((MyFragmentCallback)getActivity()).callback();
}

Fragmentからの不自然な画面遷移

Fragmentから新しいActivityへ遷移する際、getActivity().startActivity()するような実装は危険です。 特に、getActivity().startActivityForResult()を使用するのは避けてください。

なぜダメか

上記のような実装を行うとActivityからFragmentonActivityResult()を伝達する必要があり、密結合となる場合が多くなります。 Fragmentの再利用性が下がるだけでなく、Activity#onActivityResult()のメンテナンスも困難になります。

解決法

Fragment#startActivity()Fragment#startActivityForResult()Fragment#onActivityResult()を利用してください。 これらが利用できない(Activityを経由する必要がある)場合、そもそも密結合である可能性高いので、コールバックインターフェイスなどを利用して疎結合にできないか検討してください。

DB関連

Columnsの定義方法が間違っている

ColumnsクラスはBaseColumnsを継承し、BaseColumns._idを主キーフィールドとして使用します。 この値を文字列として再定義しないでください。

なぜダメか

Androidの一部機能(CursorAdapterなど)は主キーフィールド名がBaseColumns._idと同一であることを期待しています。 おそらく将来にわたって変わることはありませんが、わざわざアプリ内で再定義して管理する必要もありません。

解決法

ColumnsクラスはBaseColumnsを継承し、BaseColumns._idを主キーフィールドとして使用します。 http://developer.android.com/training/basics/data-storage/databases.html

WebView関連

WebViewのライフサイクルを考慮していない

WebViewの一部機能はActivityFragmentのライフサイクルと同期させる必要があります。

なぜダメか

ライフサイクルメソッドの呼び出しを怠るとFlashHtml5コンテンツが休止されないため、音が鳴り止まない、バッテリーを浪費するなどの問題があります。

解決法

WebViewのライフサイクルメソッドを必要に応じて呼び出してください。 API level 11以降であればWebViewFragmentが利用できます。

    @Override
    public void onPause(){
        super.onPause();
        webview.onPause();
    }
    
    @Override
    public void onResume(){
        super.onResume();
        webview.onResume();
    }

    @Override
    public void onDestroy(){
        super.onDestroy();
        webview.destroy();
    }

http://developer.android.com/reference/android/webkit/WebView.html

その他

ソースコードによるレイアウトの共通化

あまり多くありませんが、<include />を使わず、BaseActivity内でヘッダやフッタの追加を行っている場合があります。

なぜダメか

この処理の問題はメンテナンスコストが上がることです。 あとからヘッダやフッタを編集する際、本当にBaseActivityだけ編集すれば良いのかどうか判断できません。 ヘッダやフッタが必要でない画面や特殊なヘッダを用いる画面では特殊な処理をオーバーライドしている可能性があるためです。 Androidにはレイアウト(の一部)を流用するための仕組みがあるので、ソースコードで共通化の処理を行う必要はありません。

解決法

レイアウトxml内で<include />を使用してください。 あるいは、Activity同士の継承ではなくFragmentによる画面の一部遷移という形式にできる可能性もあります。

引数における型指定

メソッド引数の型指定をより良くできる場合があります。 ArrayList<?>で指定している部分をList<?>にしたり、StringCharSequenceにできないか検討して下さい。

なぜダメか

別にそのままでもダメではないのですが、必要最小限の実装を持つスーパークラスインターフェイスを指定することで将来の拡張に耐えられる場合があります。 特に文字列を引数で受け取り、その引数に対して編集などを行わない場合はCharSequenceにしておくことでTextView#getText()などを直接扱えるようになります。

解決法

より上位のクラスやインターフェイスで対応できないか検討して下さい。 自作のクラスを引数とする場合、インターフェイス化できないか検討してください。 (冗長になってしまう場合もあるので、Utilsクラスのみを対象とする、という形でも良いと思います)

仕様面

厳密にはソースコードレビューとは異なりますが、気にかけておくべき事柄。

画面構成がiOS

指摘してもどうしようもない場合が多いですが、一応…。

なぜダメか

AndroidにはAndroidに適したUIがあるので、なるべく沿うようにしましょう。 http://developer.android.com/design/patterns/pure-android.html

解決法

代替案を提案してみる他ないです。

  • 下タブ
    • NavigationDrawer
    • ActionBarタブ
  • 独自ヘッダー
    • ActionBar
  • 画面遷移要素の「>」マーク
    • 削除

特定のアプリへの依存

TwitterFacebookなどのSNSアプリに対してパッケージを指定した明示的なインテントを発行したり、インストールされていない場合にPlayストアに遷移するような構成は好ましくありません。

なぜダメか

これらは多くの場合、仕様を決める際の考慮漏れです。 本当に必要なのはTwitter,Facebookへの投稿ではなく、Twitter/Facebookを含むSNSなどへの拡散ではないでしょうか。 そうであった場合、MixiやG+など他のSNSへ拡散される機会を損失しています。 また、明示的にTwitterFacebookに投稿する場合であっても、公式アプリ以外のクライアントアプリの存在を考慮する必要があります。 特にTwitterは非公式のクライアントが多く、そのユーザも少なくありません。 公式アプリの有無で挙動を変えることは、それらのアプリユーザから見れば不自然な挙動となります。 さらに、非公式アプリを無視できるとしても、公式アプリが挙動を変更した場合に正しく動作しなくなる可能性があります。 このような仕様は避けるべきです。 http://developer.android.com/design/patterns/pure-android.html

解決法

単純にSNSへ拡散したい場合、暗黙的Intentを発行すれば良くなります。 ユーザの利便性を考慮し、ShareActionProviderを利用することもできます。 http://developer.android.com/training/sharing/shareaction.html どうしても明示的にTwitter/Facebookに投稿させたい場合、アプリがSDKを使用して直接投稿すべきです。

ListViewのpadding設定

ListViewにpaddingを設定する

ListViewで子要素の配置を調整する場合、通常はandroid:paddingで描画領域を、android:dividerHeightで子要素同士の間隔を設定する。 この時、単純にandroid:paddingだけ指定すると以下のようにリストの上下端で子要素が途切れてしまう。

pict-Screenshot_2014-08-19-11-28-01.png

これを防ぐためには、android:clipToPadding属性でpadding領域であっても子要素を描画するように設定する。

    <ListView
        android:id="@android:id/list"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/darker_gray"
        android:clipToPadding="false"
        android:divider="@null"
        android:dividerHeight="16dp"
        android:padding="16dp" />

pict-Screenshot_2014-08-19-11-27-37.png

styleで定義する

いちいちListViewに全部の設定を書くのは面倒なので、上記設定をstyleでやろうとするとこうなる。

    <style name="ListView">
        <item name="android:padding">16dp</item>
        <item name="android:clipToPadding">false</item>
        <item name="android:divider">@null</item>
        <item name="android:dividerHeight">16dp</item>
        <item name="android:background">@android:color/darker_gray</item>
    </style>
    <ListView
        android:id="@android:id/list"
        style="@style/ListView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

themeで定義する

いちいちListViewにstyleを書くのは面倒なので、上記設定をthemeでやろうとするとこうなる。

    <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="android:listViewStyle">@style/ListView</item>
    </style>
    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        ...

…のだが、なぜかうまくいかない。

pict-Screenshot_2014-08-19-11-28-01.png

どうやらthemeではandroid:clipToPaddingの設定が反映されないらしい。 仕方がないのでソースコード側でListView#setClipToPadding(false)を呼び出すと動く。 ListFragmentで標準レイアウトを使う場合、themeで基本レイアウトを指定した後android:clipToPaddingだけソースコードで上書きするしかなさそうだ。

getListView().setClipToPadding(false);

これでうまく表示できた

pict-Screenshot_2014-08-19-11-27-37.png

3行未満のAndroid Tips集

記事にするまでもない3行未満で書けるレベルのTipsとリンク集。 常識レベルのものも含む。

URLのパスの結合にはUri.withAppendedPathを使う

スラッシュの重複を省いてくれて便利。 http://developer.android.com/reference/android/net/Uri.html

DBのColumnsはandroid.provider.BaseColumnsを継承する

中に_IDってフィールドがあるからそれ使う。 http://developer.android.com/reference/android/provider/BaseColumns.html

StringBufferとStringBuilder

StringBuilderのほうが圧倒的に軽い。 でもスレッドセーフじゃないから複数のスレッドからは呼べない。

Integer.toHexString

数値を16進文字列にする場合、Formatterより速い。 0xf以下の値を渡すと1桁しか出力しないので注意。

Deep Linkの提供

App-indexing以外にもGoogle+とGoogleAdsから提供できる http://developer.android.com/distribute/engage/deep-linking.html

Playストアへのリンクの貼り方

details?id=以外もある。 http://developer.android.com/distribute/tools/promote/linking.html

Playストアへのリンクボタン生成ページ

ランディングページとかに貼るやつ。 http://developer.android.com/distribute/tools/promote/badges.html

スクリーンショットに端末画像の枠つける

Nexus端末じゃないとダメ。 http://developer.android.com/distribute/tools/promote/device-art.html

便利な新機能の探し方

Android Developersを「Compat」で検索する→左側のメニューで「Reference」を選択する。 元のクラスとの差分が含まれたXXXCompat系がいっぱい表示されるので片っ端から見る。 Compatに含まれるくらいなんだから便利な新機能にきまっている。 http://developer.android.com/reference/android/support/v13/app/FragmentCompat.html#q=Compat

よく使うシステムブロードキャストの一覧

AndroidDevelopersのIntent#Standard Broadcast Actionsに書いてある http://developer.android.com/reference/android/content/Intent.html

はてなブログはじめた

Markdownに慣れきってしまうとBloggerで日記を書くのが苦痛になったのではてなブログを始めた。 とりあえずQiitaに書いた記事も転記してみたが、体裁が一部おかしいかもしれない。 Qiitaに投げるまでもない小ネタとかはこっちにまとめていきたい。

コンゴトモヨロシク。

買ってよかったAndroid関連電子書籍まとめ

Androidの技術書、どうせ買うなら電子書籍がいいよね!と電子書籍をあちこちで買ったり買い直したりしてるので知見を共有します。

どこで買うべきか

電子書籍で技術書を購入する際、なるべく制限の少ないものを探すことが重要です。 何も考えずに買ってしまうと、以下の様な制限に引っかかってしまいます。

  • PC/Macで閲覧できない
  • 全文検索ができない
  • 文字列コピーができない
  • 印刷ができない

技術書に限っては閲覧制限のかかっていないPDFを入手できることが最善だと思います。

達人出版会

閲覧制限のないPDFを入手することができます。 ここで購入できる書籍はなるべくここで購入しています。 領収書が発行されないので、必要な人は注意してください。 http://tatsu-zine.com/

技術評論社

閲覧制限のないPDFを入手することができます。 Android本もそこそこあって良いです。 AdobeReaderでコピーすると文字化けするPDFがあるので注意。 https://gihyo.jp/dp

O'Reilly Japan Ebook Store

閲覧制限のないPDFを入手することができます。 ただし、Android本があまりありません…。 1年以内に発売されたAndroid本が「実践 Android Developer Tools」しかない…。 http://www.oreilly.co.jp/ebook/

Impress Japan

あまり使ったことがないですが、書籍によって提供されるフォーマットがまちまちです。 PDFもコピペ制限があったりなかったりで、大抵の場合買うまでわかりません。 http://www.impressjapan.jp/

Google Play ブックス

PCでもブラウザで読めますが、文字列コピーと印刷ができません。 ソースコードを検索した際の精度もいまいち良くない気がします。 https://play.google.com/store/books

Amazon Kindle

Kindle(jp)はPCで読めないという致命的な欠点があるので、技術書の購入には向きません。 http://www.amazon.co.jp/

買ってよかった電子書籍

Smashing Android UI レスポンシブUIとデザインパターン

Smashing Android UI Androidのデザインについて深く考察した上で書かれた良著。 特に「第21章 UIデザインのアンチパターン」は「なぜ」Androidではそのデザインがだめなのか?ということを解決策付きで解説しており、Androidデザインの基本を理解するのに最適です。 サンプルアプリの一部挙動が若干分かり難いのが難点。 達人出版会で閲覧制限のないPDFが買えます。 http://tatsu-zine.com/books/smashing-android-ui

良いAndroidアプリを作る139の鉄則

タイトルの通り、良いAndroidアプリを作るためのノウハウがまとめられた本。 内容もデザインからテスト、リリースと多岐にわたっているので自分が苦手な部分への取っ掛かりをつかむには最適です。 また、コラムの内容も充実していて非常に参考になりました。 コンテンツ数が多い文、個々の内容が若干物足りない感じもしますが、そこは自分で調べればいいかな…。 技術評論社のPDFはAdobeReaderでコピーすると文字化けしますが、Chromeでコピーするとなぜか文字化けしません。 技術評論社で閲覧制限のないPDFが買えます。 https://gihyo.jp/dp/ebook/2014/978-4-7741-6607-0

Effective Android

Effective Android デザインからNDKまで幅広く取り扱っているので、知識の幅を広げるのに非常に良い本。 Androidマスターしたわ!と思ったらこの本でボッコボコにされるといいと思います。 あまり細かい解説がなかったりするので、初級者向けではないです。 達人出版会で閲覧制限のないPDFが買えます。 http://tatsu-zine.com/books/effective-android

Master of Fragment (Android Professional Developerシリーズ)

Master of Fragment Fragmentなんとなく使えるけどこれでいいんだっけ…くらいのときに読むと良い本。 よくある疑問点がまとめられていて非常に助かりました。 達人出版会で閲覧制限のないPDFが買えます。 http://tatsu-zine.com/books/master-of-fragments

Android Pattern Cookbook マーケットで埋もれないための差別化戦略

Android Pattern Cookbook Kiatkat準拠のデザイン本。 解説が細かく、サンプルソースも読みやすいです。 Impress Japan版はコピペ制限がかかっていたので、達人出版会で買い直しました。 達人出版会で閲覧制限のないPDFが買えます。 http://tatsu-zine.com/books/android-pattern-cookbook

Android Security 安全なアプリケーションを作成するために

Android Security Androidアプリのセキュリティについて書かれた稀有な本。 2年以上前の本なので、後述の「Android アプリのセキュア設計・セキュアコーディングガイド」と読み合わせると良い感じ。 達人出版会で閲覧制限のないPDFが買えます。 http://tatsu-zine.com/books/androidsec

Androidアプリのセキュア設計・セキュアコーディングガイド

Androidアプリのセキュリティについて書かれた資料。しかも無料。 AccountManagerとSyncAdapterを学習する上で滅茶苦茶お世話なりました。しかも無料。 ソースコード内にわかりやすくポイントが書いてあり、読みやすいです。しかも無料。 無料でPDFが公開されています http://www.jssec.org/report/securecoding.html

まとめ

達人出版会の宣伝みたいになっていますが、汎用性を考えるとPDF最強になってしまうので仕方ないですね。 技術評論社もかなり充実してきたので今後が楽しみです! O'Reillyはもっと電子書籍化がんばってほしい…。

設定画面のインテントアクションまとめ

はじめに

Androidでは、アプリが必要な権限を持っていれば、アプリから端末の設定(一部)を変更することができます。 この仕組はアプリが必要とする機能を確実に使用するためには有用ですが、インストール時に設定変更権限が必要な旨がダイアログ表示されてしまい、ユーザから敬遠される恐れもあります。 また、端末設定の変更ロジックに問題があった場合、影響範囲がアプリだけに収まらなくなります。

Androidでは上記のようなアプリから直接設定を変更する方法の他に、設定アプリの各画面へ簡単にアクセスする方法も提供されています。 android.provider.SettingsクラスにはAndroidの各機能設定画面を呼び出すIntentActionが定義されており、遷移先の設定画面でユーザ自身に必要な設定を行わせることができます。 この場合には自アプリには権限が必要ないため、インストール時の敬遠リスクは下がり、設定変更ロジックも書かなくて良いため実装コストは軽くなります。

この記事では、android.provider.Settingsクラスのアクション一覧を簡単な解説つきで列挙します。 キャプチャはAPI level 19未満で表示できるものはGalaxy Nexus@Android 4.3、API level 19以降が必要なものについてはNexus5@Android L Developer Previewを使用しています。

参考URL: http://developer.android.com/reference/android/provider/Settings.html

注意点

生成したIntentで呼び出される画面は端末の種類(メーカー)、OSのバージョンなどによって変わります。 同じ画面構成であっても見た目が同じとは限らないので、ヘルプなどを書く際には注意が必要です。

また、基本的にActionを指定した暗黙的インテントになるため、環境によっては複数Activity<intent-filter>に引っかかる場合があります。 逆に、<uses-feature>の設定などによっては対象Activityが見つからないことも考えられます。 どの設定画面への遷移を利用する場合でも、Intentの対象となるアクティビティが存在するかどうかを確認後、遷移するようにしてください。

ACTION_ACCESSIBILITY_SETTINGS

API level 5から追加。 ユーザ補助設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_ACCESSIBILITY_SETTINGS);
    startActivity(intent);

ACTION_ADD_ACCOUNT

API level 8から追加。 アカウントの追加画面(アカウントの種別選択画面)のアクション。 Settings.EXTRA_ACCOUNT_TYPESaccount_typeを指定(String[]形式)することで追加対象のアカウントをフィルタリング可能。 Settings.EXTRA_AUTHORITIESsyncable="true"ContentProviderauthoritiesを指定(String[]形式)することで追加対象のアカウントをフィルタリング可能。 フィルタリングの結果、追加対象のアカウントが特定された場合は選択画面を経由せず、直接対象アカウントの追加画面を表示する。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_ADD_ACCOUNT);
    // 対象をGoogleアカウントに限定
    intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES,new String[]{"com.google"});
    startActivity(intent);

ACTION_AIRPLANE_MODE_SETTINGS

API level 3から追加。 機内モードの設定が可能な画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_AIRPLANE_MODE_SETTINGS);
    startActivity(intent);

ACTION_APN_SETTINGS

API level 1から追加。 APNの設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_APN_SETTINGS);
    startActivity(intent);

ACTION_APPLICATION_DETAILS_SETTINGS

API level9から追加。 指定したアプリケーションの詳細設定画面のアクション。 アプリケーションの指定はパッケージURI形式(package:com.my.app)でsetData()する。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
    intent.setData(Uri.parse("package:com.android.settings"));
    startActivity(intent);

ACTION_APPLICATION_DEVELOPMENT_SETTINGS

API level 3から追加。 開発者向けオプション画面のアクション。 開発者向けオプションを有効にしていない場合にどうなるかは不明。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
    startActivity(intent);

ACTION_APPLICATION_SETTINGS

API level 1から追加。 アプリケーションに関する設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_APPLICATION_SETTINGS);
    startActivity(intent);

ACTION_BLUETOOTH_SETTINGS

API level 1から追加。 Bluetooth設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
    startActivity(intent);

ACTION_CAPTIONING_SETTINGS

API level 19から追加。 Kitkatから追加されたユーザ補助設定のうち、ビデオキャプション設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_CAPTIONING_SETTINGS);
    startActivity(intent);

ACTION_DATA_ROAMING_SETTINGS

API level 3から追加。 データローミング設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_DATA_ROAMING_SETTINGS);
    startActivity(intent);

ACTION_DATE_SETTINGS

API level 1から追加。 日付と時刻設定画面のアクション

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_DATE_SETTINGS);
    startActivity(intent);

ACTION_DEVICE_INFO_SETTINGS

API level 8から追加。 端末の状態画面のアクション。 Android L Developer Previewではなぜかクラッシュする。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_DEVICE_INFO_SETTINGS);
    startActivity(intent);

ACTION_DISPLAY_SETTINGS

API level 1から追加。 ディスプレイ設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_DISPLAY_SETTINGS);
    startActivity(intent);

ACTION_DREAM_SETTINGS

API level 18から追加。 JELLY_BEAN_MR1で追加されたDaydream(スクリーンセーバー)設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_DREAM_SETTINGS);
    startActivity(intent);

ACTION_INPUT_METHOD_SETTINGS

API level 3から追加。 言語と入力設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_INPUT_METHOD_SETTINGS);
    startActivity(intent);

ACTION_INPUT_METHOD_SUBTYPE_SETTINGS

API level 11から追加。 インプットメソッド(文字入力アプリ)のサブタイプ設定画面のアクション。 Settings.EXTRA_INPUT_METHOD_IDInputMethodInfo#getId()で取得したidを指定することでサブタイプを指定できるらしいが未検証。 Nexus5では、Settings.EXTRA_INPUT_METHOD_IDに何も設定しないとgoogleキーボードの入力言語設定が表示される。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_INPUT_METHOD_SUBTYPE_SETTINGS);
    startActivity(intent);

ACTION_INTERNAL_STORAGE_SETTINGS

API level 3から追加。 内部ストレージ設定画面のアクション。 このアクションは端末ごとの差異が激しいかもしれない。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_INTERNAL_STORAGE_SETTINGS);
    startActivity(intent);

ACTION_LOCALE_SETTINGS

API level 1から追加。 端末の言語設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_LOCALE_SETTINGS);
    startActivity(intent);

ACTION_LOCATION_SOURCE_SETTINGS

API level 1から追加。 位置情報アクセス設定画面のアクション。 位置情報の取得モードを切り替えさせるのに使うっぽい。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
    startActivity(intent);

ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS

API level 9から追加。 すべてのアプリケーション情報画面のアクション。 アプリ設定画面の「すべて」タブのこと。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS);
    startActivity(intent);

ACTION_MANAGE_APPLICATIONS_SETTINGS

API level 3から追加。 アプリ管理画面のアクション。 ACTION_APPLICATION_SETTINGSと同じ画面が表示された。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
    startActivity(intent);

ACTION_MEMORY_CARD_SETTINGS

API level 3から追加。 外部ストレージ設定画面のアクション。 このアクションは端末ごとの差異が激しいかもしれない。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_MEMORY_CARD_SETTINGS);
    startActivity(intent);

ACTION_NETWORK_OPERATOR_SETTINGS

API level 3から追加。 利用可能なネットワーク設定画面のアクション。 表示と同時に利用可能なネットワークの検索が始まる。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_NETWORK_OPERATOR_SETTINGS);
    startActivity(intent);

ACTION_NFCSHARING_SETTINGS

API level 14から追加。 ICE_CREAM_SANDWICHから追加されたAndroidビーム設定画面のアクション。 Androidビームの有効/無効はNfcAdapter#isNdefPushEnabled()で確認できる。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_NFCSHARING_SETTINGS);
    startActivity(intent);

ACTION_NFC_PAYMENT_SETTINGS

API level 19から追加。 KITKATから追加されたタップ&ペイ設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_NFC_PAYMENT_SETTINGS);
    startActivity(intent);

ACTION_NFC_SETTINGS

API level 16から追加。 NFC設定画面のアクション。 Nexusなどでは無線とネットワーク画面内のチェックボックスNFCが追加されたのはAPI level 14からだが、この定数はAPI level 16からなので注意。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_NFC_SETTINGS);
    startActivity(intent);

ACTION_PRINT_SETTINGS

API level 19から追加。 KITKATで追加された印刷サービス設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_PRINT_SETTINGS);
    startActivity(intent);

ACTION_PRIVACY_SETTINGS

API level 5から追加。 プライバシー設定画面のアクション。 Nexusではバックアップとリセット画面が相当。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_PRIVACY_SETTINGS);
    startActivity(intent);

ACTION_QUICK_LAUNCH_SETTINGS

API level 3から追加。 Searchキーと他キーの同時押しで起動するアプリ設定画面のアクション。 この機能自体を知らなかった。ハードウェアキーボードが存在する端末向きだろうか? Android L Developer Previewではクラッシュする。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_QUICK_LAUNCH_SETTINGS);
    startActivity(intent);

ACTION_SEARCH_SETTINGS

API level 8から追加。 検索設定画面のアクション。 NexusではGoogle検索アプリの設定画面が表示される。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_SEARCH_SETTINGS);
    startActivity(intent);

ACTION_SECURITY_SETTINGS

API level 1から追加。 端末のセキュリティ設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_SECURITY_SETTINGS);
    startActivity(intent);

ACTION_SETTINGS

API level 1から追加。 設定アプリのアクション(設定アプリの起動)。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_SETTINGS);
    startActivity(intent);

ACTION_SOUND_SETTINGS

API level 1から追加。 端末の音関連設定のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_SOUND_SETTINGS);
    startActivity(intent);

ACTION_SYNC_SETTINGS

API level 3から追加。 アカウントの同期設定のアクション。 Settings.EXTRA_AUTHORITIESSyncAdapterauthoritiesを指定することで直接その同期設定が開けるようだが未検証。 Settings.EXTRA_AUTHORITIES未設定の場合は設定済み同期一覧が表示される。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_SYNC_SETTINGS);
    startActivity(intent);

ACTION_USER_DICTIONARY_SETTINGS

API level 3から追加。 ユーザー辞書設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_USER_DICTIONARY_SETTINGS);
    startActivity(intent);

ACTION_WIFI_IP_SETTINGS

API level 3から追加。 Wi-Fi接続時のIP設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_WIFI_IP_SETTINGS);
    startActivity(intent);

ACTION_WIFI_SETTINGS

API level 1から追加。 Wi-Fi設定画面のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_WIFI_SETTINGS);
    startActivity(intent);

ACTION_WIRELESS_SETTING

API level 1から追加。 無線とネットワーク設定のアクション。

    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_WIRELESS_SETTING);
    startActivity(intent);