136
133

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-12-29

結構命名など様々な種類があり方針を決めるのは悩みますし時間がかかります。
考えることも重要ですが、標準的なものを真似しておくと素早く開発していくことができます。
出来れば既存のプロジェクトの方針をつまみ食いして開発したいです。
また、自分は毎回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だけステータスバーに透過で表示する

レイアウトに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>

また独自にstyleの要素を定義していて、それを使っているようです。
例えばactionBarInsetStartはToolbarのコンテンツの横からの位置を設定しています。
これは画面によってこの部分を変えたいということがあり、画面によってその部分をテーマで変更して利用しているようです。(スタイルでもオーバーライドっていうのかな?)

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

タスク一覧(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

まとめ

全て合わせる必要はないですが、何か方針や書き方などで悩んだ時は見てみると参考にあると思います。
個人的に気になった部分をまとめてみましたが、他にも実装していく中で気になるところがあったら追記していく予定です。
他にも大量に参考になる部分などあると思いますので、ここは読んだほうが良い、ここが間違っているなどございましたらコメントでよろしくお願いします。

ライセンス

136
133
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
136
133

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?