Money Forward Developers Blog

株式会社マネーフォワード公式開発者向けブログです。技術や開発手法、イベント登壇などを発信します。サービスに関するご質問は、各サービス窓口までご連絡ください。

20230215130734

Android版「マネーフォワード」をマテリアルデザイン対応したときのあれこれ

エンジニアの黒田です。

この度Android版「マネーフォワード」アプリをマテリアルデザインにフルリニューアルしました。

そしてなんと!!! おかげさまでGooglePlayの「2014年ベストアプリ」に選出いただきました! GooglePlay 2014年ベストアプリ

アプリのDLはこちらから ic_launcher_android_512
家計簿マネーフォワード【投資・資産管理】

この記事ではマテリアルデザイン対応するにあたり、考えたことや実際に書いたコード、使ったツールなどあれこれと書いてみたいと思います。

マテリアルデザインとは

マテリアルデザインとはGoogleが発表した新たなデザインガイドラインです。 モバイルに限らず、AndroidWearやAndroidTVなど様々なデバイスでもユーザーに対して同じ体験を提供することを目的としています。

マテリアルデザインに関する細かい説明は割愛しますが、GoogleMapやGMailなどGoogleが提供している多くのAndroidアプリはこのマテリアルデザインガイドラインに則って設計されており、既に多くのAndroidユーザーが触れるものとなっています。

マネーフォワードでもデザインリニューアルを行うにあたり、Androidユーザーが慣れている(これからのスタンダードになる)UIであるマテリアルデザインを採用することにしました。 これにより普段の体験を損なわず、マネーフォワードが提供する価値そのものを感じてもらえることを期待しています。

マテリアルデザイン対応前/後

目的はマテリアルデザイン対応そのものではなく、既存のUIで起こっていた問題を解決することにあります。 マテリアルデザイン適用前にどういう問題があり、それをどう解決したかをまとめました。

視認性

シンプルで見やすく

chart

既存アプリはアイコンや文字がカラフルでわかりやすさという点では良かったと思います。 ただ、アイコンやテキストなど色を多用したことで全体的な統一感が損なわれていました。 また、情報が詰まった状態で「なにが重要な情報か」が解りづらいものとなっていました。

今回のデザインリニューアルでは全体的な統一感を持たせるとともに、キーとなるカラーを絞って、色に意味を持たせることで重要なものによりフォーカスできるようになりました。 全体的に冗長な表示項目を減らし、余白をうまくつかうことで情報を切り分け、認識しやすくなっています。 マテリアルデザインを採用してAndroidらしさを出しながらも、マネーフォワードらしさもしっかりと出せているのではないかと思います。

TOP画面をカード型に変更

home.png

TOPは自分の資産や収支の履歴などをダッシュボード的に表示しています。 既存アプリでは余白があまり無く、情報が詰まったように感じます。

今回はTOPの各項目の枠にカード型を採用しています。 カード型にすることで情報が分離されて、視認性が上がりました。 今後は表示項目を増やして、ユーザーが見たい情報を好きに配置できるような仕様も検討しています。 その際、カード型にすることで順番の入れ替えや表示のオンオフがわかりやすくなるというメリットもあります。

手入力の項目を見やすく

manual2.png

既存アプリでは手入力画面でテンキーをダイアログ表示していました。 これだと、ユーザーが金額を入力している間ダイアログに隠れてしまい、現在入力中のカテゴリや内容が見えないという問題がありました。

今回はデザインを一新して入力中も項目が確認できるようにし、入力中のカテゴリ毎に背景色を分けるとでカテゴリの設定ミスを防げるようになっています。

カテゴリ選択の視認性を上げる

manual3.png

いままではカテゴリ選択しても文字ばかりでわかりづらかったのでアイコンを付けてわかりやすくしました。 また全体的にこのアイコンを他の画面にも流用することで統一感と視認性があがっていると思います。

操作性

ナビゲーションドロワーは「ナビゲーション」

menu.png

リニューアルの初期デザインモックではナビゲーションドロワーに「同期」や「金融機関登録」などユーザーが操作する"アクション"を置いていました。 しかし、ナビゲーションドロワーはあくまで「ナビゲーション」であり「アクション」を置く場所では無いと考え、今のデザインに至っています。 実際にはナビゲーションドロワーにアクションがあると便利なケースがあるかもしれませんが、こういった細かいルールを守ることでユーザーの迷いを防げると考えています。

よく使うホームをカバー画像を用いることで押しやすい位置に下げる

ナビゲーションドロワーにカバー画像を置きました。 こちらもはじめのデザインモックでは存在しませんでしたが、実際に使ってみるとナビゲーションドロワーで一番良く使う「ホーム」ボタンが遠くなって押しづらいということがわかりました。 特に最近の端末では画面が大きいのでほとんどのユーザーが端末を持ち替えないとホームボタンに届きません。 そこでカバー画像(ユーザーステータス+グループ切替)を置いたところ、ナビゲーションドロワーの操作性、とくに「ホーム」へのアクセスが格段に容易になったかと思います。

フローティングアクションボタンで手入力や金融機関登録にアクセスしやすく

fab_all.png

よく使う「手入力」や「金融機関登録」はフローティングアクションボタンにしました。 今まではアクションバー(現:アップバー)に置いていたのですが、前述の通り端末画面サイズの拡大傾向に伴い、アクションバーのボタンが押しづらくなっていましたので、よりユーザーの手に近く押しやすいようフローティングアクションボタンとして配置しています。 またスクロールしたときには隠してあげることで情報を見やすく、邪魔しない作りになっています。

こうしたデザインや操作性におけるルールを定めたことで、他のAndroidアプリとの操作の差異がなくなり、よりOS標準に近い使用感を得ることができるようになったのではないでしょうか。

必要な対応

では実際にどのような対応が必要だったか。コードを交えながら簡単に紹介していきたいと思います。

ActionBarからToolbarへ

旧バージョンではAndroid2.3.3(API Level:10)以上が対象でしたのでActionBarActivityを使用していました。 リニューアル後も引き続き2.3.3をサポートするため、ActionBarActivityを使用しています。

新しいSupportLibraryではToolbarというものが用意され、より自由度の高い表現が可能となりました。

Toolbarと併せてNavigationDrawerにも手を入れています。 ActionBarActivityではDrawableLayoutとActionBarTabの相性が悪く、普通に実装してしまうとDrawerがタブの下から出てきてその後タブが消えるという状況になっていました。

Screenshot_2014-12-02-20-27-17.jpg

なのでドロワーをアップバーに被せるように表示したかったのですが、これを実現するためには色々と手を入れなければなりませんでした。 Toolbarに変えたおかげでこちらへの対応も非常に楽にできます。

ToolbarとDrawerLayoutのサンプルを載せておきます。

まず、Toolbarを利用する際はThemeでActionBarを無効化しておく必要があります。

<resources>
    <style name="AppTheme" parent="AppTheme.Base"/>

    <style name="AppTheme.Base" parent="Theme.AppCompat">
        <item name="android:windowNoTitle">true</item>
        <item name="windowActionBar">false</item>
    </style>
</resources>

全体をDrawerLayoutで囲み、必要な場所にandroid.support.v7.widget.Toolbarを置いてください

<android.support.v4.widget.DrawerLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Toolbar -->
        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimaryDark"/>

        <!-- Main Content -->
        <FrameLayout
            android:id="@+id/content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_below="@id/toolbar"/>

    </RelativeLayout>

    <!-- Navigation Drawer -->
    <ScrollView
        android:id="@+id/navdrawer"
        android:layout_width="240dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/background_light">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <Button
                android:id="@+id/btnHome"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Home" />

        </LinearLayout>
    </ScrollView>
</android.support.v4.widget.DrawerLayout>

あとはレイアウトファイルにあるToolbarを読み込み、SupportActionBarにSetしてあげるだけです。

mToolbar = (Toolbar) findViewById(R.id.toolbar);
if (mToolbar != null) {
    setSupportActionBar(mToolbar);
    mToolbar.setNavigationIcon(R.drawable.abc_ic_ab_back_mtrl_am_alpha);
}
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer);
mToolbar.setNavigationIcon(getNavigationIcon());

CardView

カードビューの導入は非常に簡単です。 まずはgradleのDependenciesにCardviewのサポートライブラリを追加します。

build.gradle

dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile 'com.android.support:appcompat-v7:21.0.+'
    compile 'com.android.support:support-v4:21.0.0'
+   compile 'com.android.support:cardview-v7:21.0.+'
}

Layoutでは下記のようにCardViewタグで囲んであげるとそれっぽくなります。

<android.support.v7.widget.CardView
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    card_view:cardBackgroundColor="@color/white"
    card_view:cardCornerRadius="2dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/selectableItemBackground"
        android:orientation="vertical">
    
    ・・・

    </LinearLayout>
</android.support.v7.widget.CardView>

リップル効果

マテリアルデザインのわかりやすい特徴といえばリップル効果です。

Screenshot_2014-12-02-22-56-12.png

タッチした箇所から波紋のように広がるリップル効果は触っていて楽しくなります。 残念ながらLolipopより前のOSではこの効果を表現することができませんが、せめてLolipop端末ではリップル効果を出すように対応しました。

以前のバージョンではタッチした場合に背景色を変えるのにSelectorを用いていました。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- pressed -->
    <item android:drawable="@color/primary"
        android:state_pressed="true"/>
    <!-- released -->
    <item android:drawable="@android:color/transparent"
        android:state_pressed="false"/>
</selector>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" 
    android:background="@drawable/default_selector">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />
</LinearLayout>

しかしこれではリップル効果はでませんので下記のように修正する必要があります。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" 
-   android:background="@drawable/default_selector">
+   android:background="?attr/selectableItemBackground">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="" />
</LinearLayout>

注意したこと、困ったこと

途中でSupportLibraryが出て手戻り

開発を初めたころはSupportLibraryがLolipopに対応しておらず、カードビューやDrowerNavigationの対応を自前で実装しようと試みていました。 10月22日にLolipop対応SupportLibraryがリリースされ、このまま自前実装でいくか、既存コードを捨ててSupportLibraryを使うか悩みましたが、今後の保守やOSのバージョンアップを考慮してSupportLibraryでの対応に切り替えました。

今後新たなAPIがリリースされた場合は、SupportLibraryのリリースを待ちつつ極力標準APIを使用した実装を心がけたほうがよいのではないかと思います。

画像を極力使わない

Android5.0からSVGが使えるようになったようですが、残念ながら今回はそこまでチャレンジできませんでした。 とはいえデータ容量の削減・メモリ節約のために、できるだけ画像ファイルを使わずにShapeなどを用いて描画するよう心がけました。 とくにマテリアルデザインではレイヤーの概念が重要になり、奥行きを表現するたにシャドーを多用します。 これもすべてXMLで表記しましたのでサンプルを載せておきます。

単純なシャドー

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid
        android:height="1dp"
        android:color="#222222" />
    <gradient
        android:angle="270"
        android:endColor="#00000000"
        android:startColor="#33222222" />
</shape>
<View
    android:layout_width="match_parent"
    android:layout_height="3dp"
    android:background="@drawable/simple_shadow"

円形

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="oval">
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#10888888" />
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#20888888" />
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#30888888" />
        </shape>
    </item>
    <item>
        <shape android:shape="oval">
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#40888888" />
        </shape>
    </item>
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#FFFFFF" />
    <stroke android:width="2dp" android:color="#FE9626" />
    <size
        android:width="40dp"
        android:height="40dp" />
</shape>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:drawable="@drawable/circle_shadow"
        android:top="1dp"
        android:left="1dp"
        android:right="1dp"
        android:bottom="1dp" />
    <item android:drawable="@drawable/circle_button" />
</layer-list>

AndroidStudioでのマージ作業

マテリアルデザイン対応を進めると同時に、旧デザイン版でも日々改修やグロースハックのための施策など数多くのコミットが行われていました。 それらの施策と別ブランチで進めていたマテリアルデザイン対応をマージさせようとしましたが、これがとても大変でした。 あまりに変更が多すぎて弊社のGitlabが考えるのを放棄してしまったほどです。

実は、AndroidStudioにはマージのための強力なツールが用意されています。

コンフリクトしているファイルで右クリック Git -> Resolve conflict

Files_Merged_with_Conflicts copy.png

このようにコンフリクトしているファイル一覧が出てくるので作業するファイルを選んでMergeを押します

Merge_Revisions_for__Users_kuroda02_Dev_android_mf_android_MfAndroid_build_gradle copy.png

コンフリクトしている箇所を実際に見ながらポチポチマージさせていくだけで済みますので非常に楽でした。

社内でのテスト

社内テストではDeployGateを利用しました。

早い段階で社内に見せることで、バグだけではなくフィードバックを得ることができ、よりよいアプリへと改善することができました。 大きな改修には時間と手間が掛かります。これらのコストをかけて作り上げたものでも、開発者の思い込みにより全く価値のないものだった、ということは起こり得ます。

開発途中で公開してしまうと 「バグだ!(いやそれは元からの仕様で・・・)」 「デザインあたってない!(そこまだデザインできてないんです・・・)」 というやりとりが往々にしてありますが、そこをぐっと我慢して公開することがよりよいプロダクトを生む近道だと実感しました。

社内でのデザイナーとのやりとり

弊社ではデザイナーとのやりとりにSketchというデザインツールを使用しています。 Sketchは非常に便利なツールで、完成されたデザインデータから特定の画像を任意のサイズで簡単に出力できます。 使ったことが無い方はイメージつきづらいかもしれませんが、使っていない場合とでは明確にフローの差がでます。

よくあるエンジニアとデザイナーとのやりとりに、 「この部分の切り出した画像どこでしたっけ?」 「この画像ファイルのxxxhdpiも貰っていいですか?」 「色々アップデートされましたけど完成イメージもう一回もらっていいですか?」 などがありますが、Sketchではこれらが必要ありません。

完成したSketchファイルからエンジニアがhdpi(x1.5)やxxhdpi(x3)など任意のサイズで簡単に出力することができますし、完成イメージから直接パーツを指定して取り出すので、切り出した画像がどの部分の画像なのかをデザイナーに確認する必要がなくなります。

Sketchがどういうものかをもっと知りたい方はマネーフォワードのエンジニアブログ『Sketch Appを使おう(導入編)』をご覧ください。

エンジニアとデザイナーが協業するなかで一番のボトルネックになりやすいのはコミュニケーションです。コンセプト決めや、UI/UXを決める際のコミュニケーションは必要不可欠ですが、デザインデータのやりとりで何度も確認が発生するのは明らかなコストとなります。

マネーフォワードではエンジニアとデザイナーの意識を合わせるため、最近ではFramer.jsなどを使って動きも含めた完成イメージを共有したりしています。

終わりに

今回はモバイル版でマテリアルデザイン対応しましたが、今後はタブレットにも力を入れていく予定です。

冒頭で触れましたが、マテリアルデザインは全デバイスで同じ体験を提供するためのUIフレームワークです。 タブレット対応では実装方法やレイアウトなど考えないといけないことは多いですが、デザインの基本概念は共通化することができます。 この記事をみてマテリアルデザイン対応のきっかけになってくれれば幸いです。

マネーフォワードでは、一緒にタブレット対応してくれるAndroidエンジニアを募集しています! みなさまのご応募お待ちしております!

マネーフォワード採用サイト https://recruit.moneyforward.com/

Wantedly https://www.wantedly.com/companies/moneyforward