Android
google
MaterialDesign
appcompat

[Android] Google I/Oアプリの実装方針を参考にして爆速開発したい

More than 3 years have passed since last update.

結構命名など様々な種類があり方針を決めるのは悩みますし時間がかかります。

考えることも重要ですが、標準的なものを真似しておくと素早く開発していくことができます。

出来れば既存のプロジェクトの方針をつまみ食いして開発したいです。

また、自分は毎回Google I/Oアプリはどうやっていたっけ?って確認しにいってしまっていて面倒でした。

なので、Google I/Oアプリがどんな感じでやっているのかをまとめてみました。


プロジェクト基本構成

minSdkVersion 14

targetSdkVersion 22

Android 4.0から対応で targetSdkは5.1と普通な感じですね

targetSdkが23未満なのでランタイムパーミッションは対応してないようです。


大きな方針


  • AppCompatActivityを継承したActivityを使う


    • AppCompatActivityを利用することで、android:theme属性を利用することができるようになったり、自動的にtint(色をつけること)に対応したAppCompat系のViewが利用できるようになります。



  • それぞれAndroid ManifestでAppCompat系のテーマを継承したテーマをつける


    • AppCompatActivityを利用するのでAppCompat系のテーマを付ける必要があります。これによりprimaryColorなどのテーマの色が全体的に使われるようになります。



  • デフォルトのActionBarは使わない


    • サポートライブラリのToolBarを代わりに利用します。(拡張性が高いためだと思われます。)



  • フラグメントは使う

基本的にデフォルトのAndroid Studioで作られるプロジェクトと一緒ですね。


パッケージのわけ方

機能ごとにパッケージを分けてその中にActivityやFragmentがごちゃってある感じです。

具体的にはsessionやwelcomeなどといったパッケージがたり、その中にActivityなどがある感じです。

開発するときは関連するクラスが近くにあるので、この分け方のほうが楽そう。

https://github.com/google/iosched/tree/master/android/src/main/java/com/google/samples/apps/iosched


レイアウトの方針


レイアウトのファイル名

参考

https://github.com/google/iosched/tree/master/android/src/main/res/layout

・アンダーバー区切り

activity_welcome.xmlなど

・アクティビティの場合はactivity_から始める

例: activity_welcome.xml ActivityはWelcomeActivityになっている。

・フラグメントの中のレイアウトはfragment_から始める

例: fragment_my_schedule FragmentはMyScheduleFragmentになっている。

ただ古いソースコードはsession_detail_act.xmlがSessionDetailActivityになっていたりするので、完全にルールを守りきれているわけではなさそう


レイアウトのid

アンダーバー区切りで記述する

結構自由につけている

レイアウトの中のコンテンツっぽいレイアウトにmain_contentってつけるのが多い気がする。


Ripple対応

基本的にはbackgroundなどに

?android:selectableItemBackgroundをつけるだけ

ForegroundLinearLayoutを使ってLinearLayoutにもRippleをつけている(リストのアイテムにつけているみたい)


explore_io_keynote_stream_item.xml

<com.google.samples.apps.iosched.ui.widget.ForegroundLinearLayout

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/explore_io_clickable_item"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:focusable="true"
android:onClick="sessionDetailItemClicked"
android:orientation="vertical"
android:foreground="?android:selectableItemBackground">
...


レイアウトのスペース

https://github.com/google/iosched/blob/master/android/src/main/res/values/dimens.xml

端末の横幅によってAlternative resourceの仕組みを使い変えたりしているようです。

よく使うスペース


values/dimens.xml

    <!-- standard metrics -->

<dimen name="spacing_normal">8dp</dimen>
<dimen name="spacing_micro">4dp</dimen>
<dimen name="padding_normal">16dp</dimen>


values-sw600dp/dimens.xml

    <!-- standard metrics -->

<dimen name="spacing_normal">12dp</dimen>
<dimen name="spacing_micro">8dp</dimen>
<dimen name="padding_normal">24dp</dimen>

keyline


values/dimens.xml

    <dimen name="keyline_1">16dp</dimen>

<dimen name="keyline_1_minus_8dp">8dp</dimen>
<dimen name="keyline_2">72dp</dimen>
<dimen name="keyline_2_minus_16dp">56dp</dimen>
<dimen name="keyline_2_session_detail">@dimen/keyline_2</dimen>


values-sw600dp/dimens.xml

    <dimen name="keyline_1">24dp</dimen>

<dimen name="keyline_1_minus_8dp">16dp</dimen>
<dimen name="keyline_2">80dp</dimen>
<dimen name="keyline_2_minus_16dp">64dp</dimen>
<dimen name="keyline_2_session_detail">104dp</dimen>

keylineはよく分かっていないのですが、標準となる横からのマージンみたいです

http://wazanova.jp/items/1462


レイアウトでNavigationDrawerだけステータスバーに透過で表示する

https://github.com/google/iosched/blob/master/android/src/main/res/layout/explore_io_act.xml

レイアウトにandroid:fitsSystemWindows="true"をつけると、ステータスバー領域の部分の中に普通に表示されるので、ルートのレイアウトとNavigationDrawerにつけてあげて、透過の表示をしているようです。

image

http://www.google.com/design/spec/patterns/navigation-drawer.html より


explore_io_act.xml

<!-- ExploreIOActivity layout seen when users enter the app after the WelcomeActivity. -->

<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".explore.ExploreIOActivity"
android:fitsSystemWindows="true">

<!-- Main layout fitsystemWindowsをつけない -->
<com.google.samples.apps.iosched.ui.widget.DrawShadowFrameLayout />
<!--省略-->
<!-- fitsystemWindowsをつける -->
<com.google.samples.apps.iosched.ui.widget.ScrimInsetsScrollView
android:fitsSystemWindows="true"
app:appInsetForeground="#4000">
<!--省略-->
</com.google.samples.apps.iosched.ui.widget.ScrimInsetsScrollView>
</android.support.v4.widget.DrawerLayout>



テキスト


フォント

ButtonやTextViewに以下をつける。


layout.xml

            android:textStyle="@integer/font_textStyle_medium"

android:fontFamily="@string/font_fontFamily_medium"

Lollipop未満はsans-serifのboldで

以上であればsans-serif-mediumのnormalにするみたい

sans-serif-mediumが使えるのが21からだからかも?


values/fonts.xml

<resources>

<integer name="font_textStyle_medium">1</integer> <!-- bold -->
<string name="font_fontFamily_medium">sans-serif</string>
</resources>


values-v21/fonts.xml


<resources>
<integer name="font_textStyle_medium">0</integer> <!-- normal -->
<string name="font_fontFamily_medium" translatable="false">sans-serif-medium</string>
</resources>


テキストサイズ

text_size_mediumをよく使うみたい?

TextViewなどに

android:textSize="@dimen/app_text_size_medium"を指定する


dimens.xml

    <!-- App Primary Text Sizes -->

<dimen name="app_text_size_xsmall">11sp</dimen>
<dimen name="app_text_size_small">12sp</dimen>
<dimen name="app_text_size_medium">14sp</dimen>
<dimen name="app_text_size_large">18sp</dimen>
<dimen name="app_text_size_xlarge">20sp</dimen>
<dimen name="app_text_size_diff_large_small">6sp</dimen>


以下のように定義されています。

基本的にテーマで利用される色が定義されている感じです。

注目はtheme_accent_2があることでしょうか。どこかのマテリアルデザイン動画などでも2つあると言っていたと思います。

image

    <color name="theme_primary">@color/app_primary_accent</color>

<color name="theme_primary_dark">#00BCD4</color>
<color name="theme_primary_light">#7986cb</color>

<color name="theme_accent_1">#00b0ff</color>
<color name="theme_accent_1_light">#8ad4fa</color>

<color name="theme_accent_2">#e91e63</color>

これらの色はテーマから参照しており、基本的にレイアウトからは以下のように参照してテーマの色を使います。

        android:background="?colorPrimary"

他にも他の場所で必要になった色なども定義されています。

https://github.com/google/iosched/blob/master/android/src/main/res/values/colors.xml


画像

ファイル名は_区切り

drawableフォルダを利用する

アプリアイコンのみmipmapディレクトリを使う


アイコン

ic_から始める

xxhdpiのpng画像とvectorリソースを用意していたりしていなかったり、、

https://github.com/google/iosched/blob/master/android/src/main/res/drawable-v21/ic_submit_feedback.xml#L1

https://github.com/google/iosched/blob/master/android/src/main/res/drawable-xxhdpi/ic_submit_feedback.png


Theme

テーマは継承できるので、以下の様な構造を作っています。

一番親としてTheme.AppCompat.Light.NoationBarを継承しています。これは白色をベースとしたアクションバーを表示しないAppCompatのテーマです。IOアプリは画面ごとにテーマを作っていますが、共通部分は同じテーマを継承することで共通化を図っています。

iosched.png

おそらく、一番重要なのはどんな場合も使われるTheme.IOSched.Baseでしょう。

どの要素がどこに反映されるかを理解しつつ反映していく必要がありますが、ベースとなるcolorPrimaryやcolorPrimaryDark、colorAccentは設定しておきましょう。


values/styles.xml

    <style name="Theme.IOSched.Base" parent="Theme">

<item name="actionBarIconColor">#fff</item>
<item name="actionBarInsetStart">@dimen/keyline_2</item>
<item name="homeAsUpIndicator">@drawable/ic_up</item>
<item name="spinnerBarInsetStart">@dimen/keyline_2_minus_16dp</item>
<item name="popupItemBackground">?android:selectableItemBackground</item>
<item name="photoItemForeground">?android:selectableItemBackground</item>
<item name="photoItemForegroundBorderless">?android:selectableItemBackground</item>

<item name="colorPrimary">@color/theme_primary</item>
<item name="colorPrimaryDark">@color/theme_primary_dark</item>
<item name="colorAccent">@color/theme_primary</item>

<item name="android:textColorLink">@color/flat_button_text</item>

<item name="windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>

<item name="android:windowContentOverlay">@null</item>
<item name="android:windowBackground">@android:color/white</item>
<item name="android:homeAsUpIndicator">@drawable/ic_up</item>

<item name="android:popupMenuStyle">@style/Widget.IOSched.PopupMenu</item>
<item name="android:listPopupWindowStyle">@style/Widget.IOSched.PopupMenu</item>
<item name="android:dropDownListViewStyle">@style/Widget.IOSched.ListView.DropDown</item>
<item name="android:textAppearanceLargePopupMenu">@style/TextAppearance.LargePopupMenu</item>

<item name="imageItemBackground">?android:selectableItemBackground</item>
<item name="android:borderlessButtonStyle">@style/Widget.AppCompat.Button.Borderless</item>
</style>


https://github.com/google/iosched/blob/master/android/src/main/res/values/styles.xml

また独自にstyleの要素を定義していて、それを使っているようです。

例えばactionBarInsetStartはToolbarのコンテンツの横からの位置を設定しています。

これは画面によってこの部分を変えたいということがあり、画面によってその部分をテーマで変更して利用しているようです。(スタイルでもオーバーライドっていうのかな?)

https://github.com/google/iosched/blob/master/android/src/main/res/values/attrs.xml


attrs.xml

    <declare-styleable name="BaseTheme">

<attr name="actionBarIconColor" format="color" />
<attr name="actionBarInsetStart" format="dimension" />
<attr name="spinnerBarInsetStart" format="dimension" />
<attr name="popupItemBackground" format="reference" />
<attr name="photoItemForeground" format="reference" />
<attr name="photoItemForegroundBorderless" format="reference" />
<attr name="imageItemBackground" format="reference"/>
</declare-styleable>

セッション画面のTheme


values/styles.xml

    <style name="Theme.IOSched.Sessions" parent="Theme.IOSched.WithNavDrawer">

<item name="actionBarInsetStart">@dimen/keyline_2_minus_16dp</item>
<item name="spinnerBarInsetStart">@dimen/keyline_2_minus_16dp</item>
<item name="android:windowBackground">@color/grey_background</item>
</style>


Preference管理

何も考えていない方式みたい

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/settings/SettingsUtils.java


アニメーション

5系未満が対応していないアニメーションは5系以上だけアニメーションを行ったりが多いです。

ここは検索のところを開いた時に円で広がるアニメーションのところだと思います。

参考になりそう

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/SearchActivity.java#L203


NavigationDrawer関連の処理をBaseActivityにして継承させる

人によって是非が分かれそうですが、、BaseActivityに書いて共通化させてるみたいです。

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/ui/BaseActivity.java


Fragment生成時にはFrament#newInstanceメソッドを用意してそれを使う


WiFiUtils.java

        public static WiFiDialog newInstance(boolean wiFiEnabled) {

WiFiDialog wiFiDialogFragment = new WiFiDialog();

Bundle args = new Bundle();
args.putBoolean(ARG_WIFI_ENABLED, wiFiEnabled);
wiFiDialogFragment.setArguments(args);

return wiFiDialogFragment;
}


これですね

ActivityのExtraやFragmentのArgumentを設定する責務は、呼び出される側に持たせたほうがいいんじゃねーのという提案

http://qiita.com/Nkzn/items/f6c4582a92862b3b6f45

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/util/WiFiUtils.java#L183


タスク一覧(Overview Screen)で表示するアイコンを変更する

Google I/Oアプリはランチャーアイコンとタスク一覧で表示するアイコンが異なります。

ランチャーアイコン
タスク一覧でのアイコン

Screenshot_20151230-000844.png
Screenshot_20151230-000831.png

これは以下の様なコードで実現しているようです。

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/util/RecentTasksStyler.java


RecentTasksStyler.java

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)

public static void styleRecentTasksEntry(Activity activity) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
return;
}

Resources resources = activity.getResources();
String label = resources.getString(activity.getApplicationInfo().labelRes);
int colorPrimary = resources.getColor(R.color.theme_primary);

if (sIcon == null) {
// Cache to avoid decoding the same bitmap on every Activity change
sIcon = BitmapFactory.decodeResource(resources, R.drawable.ic_stat_notification);
}

activity.setTaskDescription(new ActivityManager.TaskDescription(label, sIcon, colorPrimary));
}
}


こちらの記事が詳しく解決してくれています。

http://qiita.com/pside/items/e28dfdd1f42f7a2cb9f2


通信して画像読み込み

Glideを使う

読み込み終わった時にフェードインアニメーションさせる処理などが書かれていて参考になりそう。

https://github.com/google/iosched/blob/master/android/src/main/java/com/google/samples/apps/iosched/util/ImageLoader.java#L106


Gradle関連

「有名なAndroid オープンソースアプリ2つのbuild.gradleを読む」の中で紹介しています。

http://qiita.com/takahirom/items/9919697580fa3919df88


まとめ

全て合わせる必要はないですが、何か方針や書き方などで悩んだ時は見てみると参考にあると思います。

個人的に気になった部分をまとめてみましたが、他にも実装していく中で気になるところがあったら追記していく予定です。

他にも大量に参考になる部分などあると思いますので、ここは読んだほうが良い、ここが間違っているなどございましたらコメントでよろしくお願いします。


ライセンス

https://github.com/google/iosched/blob/master/LICENSE.txt