Codelab: Gradle ビルドシステムを使用して car-ui-lib コンポーネントを含む RRO を作成する

car-ui-lib ライブラリを使用して、自己完結型の車載インフォテインメント(IVI)システムを起動します。この Codelab では car-ui-lib とランタイム リソース オーバーレイ(RRO)を使用してライブラリのコンポーネントをカスタマイズする方法を紹介します。

学習内容

次のことを行う方法を学習します。

  • Android アプリに car-ui-lib コンポーネントを含める。
  • Gradle を使用して Android アプリと RRO を構築する。
  • car-ui-lib で RRO を使用する。

この Codelab では RRO の仕組みの詳細は説明しません。詳しくは、実行時にアプリのリソースの値を変更するランタイム リソース オーバーレイのトラブルシューティングをご覧ください。

準備

前提条件

開始前に以下の準備が整っていることを確認します。

  • コマンドラインを使用できるパソコン(Linux マシン、Linux 用 Windows サブシステムを使用できる Mac または Windows マシン)。

  • Android Studio

  • マシンに接続されている Android デバイスまたはエミュレータ。Android ソースのダウンロードAndroid のビルドをご覧ください。

  • RRO に関する基礎知識。

新しい Android アプリを作成する

所要時間: 15 分

このセクションでは、新しい Android Studio プロジェクトを作成します。

  1. Android Studio で EmptyActivity のアプリを作成します。

    Empty Activity を作成する
    図 1. Empty Activity を作成する
  2. アプリに CarUiCodelab と名前を付け、Java 言語を選択します。希望する場合は、ファイルの場所も選択できます。それ以外の設定はデフォルト値をそのまま使用します。

    アプリに名前を付ける
    図 2. アプリに名前を付ける
  3. activity_main.xml を次のコードブロックに置き換えます。

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/sample_text"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    このコードブロックは文字列 sample_text を表示します。これは定義されていません。

  4. strings.xml ファイルにリソース文字列 sample_text を追加して、「Hello World!」に設定します。このファイルを開くには、[app] > [src] > [main] > [res] > [values] > [strings.xml] を選択します。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiCodelab</string>
        <string name="sample_text">Hello World!</string>
    </resources>
    
  5. アプリをビルドするために、右上にある緑の [Play] ボタンをクリックします。クリックすると自動的に Gradle 経由で APK がエミュレータまたは Android デバイスにインストールされます。

    再生ボタン

エミュレータまたは Android デバイスで、新しいアプリが自動的に開きます。開かない場合は、アプリ ランチャーから CarUiCodelab アプリを開きます。この時点でこのアプリはインストールされています。次のような画面が開きます。

新しい CarUiCodelab アプリを開く
図 3. 新しい CarUiCodelab アプリを開く

car-ui-lib を Android アプリに追加する

所要時間: 15 分

car-ui-lib をアプリに追加します。

  1. car-ui-lib 依存関係をプロジェクトの build.gradle ファイルに追加するには、[app] > [build.gradle] を選択します。依存関係は次のように表示されます。

    dependencies {
        implementation 'com.android.car.ui:car-ui-lib:2.0.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.4.0'
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    }
    

Android アプリで car-ui-lib コンポーネントを使用する

これで car-ui-lib が追加されました。続いてアプリにツールバーを追加します。

  1. MainActivity.java ファイルで、onCreate メソッドを上書きします。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // Get the toolbar controller instance.
        ToolbarController toolbar = CarUi.getToolbar(this);
        // Set the title on toolbar.
        toolbar.setTitle(getTitle());
        // Set the logo to be shown with the title.
        toolbar.setLogo(R.mipmap.ic_launcher_round);
    }
    
  2. 必ず、ToolbarController をインポートしてください。

    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.toolbar.ToolbarController;
    
  3. Theme.CarUi.WithToolbar テーマを使用するには、[app] > [src] > [main] > [AndroidManifest.xml] を選択し、以下のように AndroidManifest.xml を更新します。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.example.caruicodelab">
    
        <application
            android:allowBackup="true"
            android:dataExtractionRules="@xml/data_extraction_rules"
            android:fullBackupContent="@xml/backup_rules"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/Theme.CarUi.WithToolbar"
            tools:targetApi="31">
            <activity
                android:name=".MainActivity"
                android:exported="true">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    
  4. アプリをビルドするには、先ほどと同様に緑の [Play] ボタンを押します。

    アプリをビルドする

RRO をアプリに追加する

所要時間: 30 分

すでに RRO について知っている場合は、次の権限コントローラをアプリに追加するセクションに進んでください。あまりなじみがない場合は、RRO の基本について、実行時にアプリのリソース値を変更するでご確認ください。

権限コントローラをアプリに追加する

RRO パッケージがオーバーレイするリソースをコントロールするために、overlayable.xml という名前のファイルをアプリの /res フォルダに追加します。このファイルはアプリ(ターゲット)と RRO パッケージ(オーバーレイ)の間の権限コントローラとして機能します。

  1. res/values/overlayable.xml をアプリに追加して、以下のコンテンツをファイルにコピーします。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <overlayable name="CarUiCodelab">
            <policy type="public">
                <item type="string" name="sample_text"/>
            </policy>
        </overlayable>
    </resources>
    

    文字列 sample_text は RRO でオーバーレイ可能とする必要があるため、アプリの overlayable.xml にリソース名を含めます。

    overlayable.xml ファイルは res/values/ に存在する必要があります。存在しない場合、OverlayManagerService がそれを見つけられません。

    オーバーレイ可能なリソースとその設定方法について詳細は、オーバーレイ可能なリソースの制限をご覧ください。

RRO パッケージを作成する

このセクションでは、RRO パッケージを作成して、上記で表示された文字列「Hello World!」を「Hello World RRO」に変更します。

  1. 新しいプロジェクトを作成するには、[File] > [New] > [New Project] を選択します。RRO パッケージにはリソースのみが含まれるため、必ず Empty Activity ではなく、No Activity を選択してください。

    設定は以下に示すものと同様になります。保存される場所は異なる場合があります。

  2. 新しい CarUiRRO プロジェクトを作成後に、AndroidManifest.xml を修正してプロジェクトを RRO として宣言します。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.caruirro">
    
        <application android:hasCode="false" />
    
        <uses-sdk
            android:minSdkVersion="29"
            android:targetSdkVersion="29"/>
    
        <overlay
            android:targetPackage="com.example.caruicodelab"
            android:targetName="CarUiCodelab"
            android:isStatic="false"
            android:resourcesMap="@xml/sample_overlay"
            />
    </manifest>
    

    そうすることにより @xml/sample_overlay でエラーが作成されます。resourcesMap ファイルはリソース名をターゲット パッケージから RRO パッケージにマッピングします。

  3. 以下のコードのブロックを …/res/xml/sample_overlay.xml にコピーします。

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
    </overlay>
    
  4. …/res/values/strings.xmlsample_text を追加します。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <string name="app_name">CarUiRRO</string>
        <string name="sample_text">Hello World RRO</string>
    </resources>
    
    作成された RRO の Gradle ビルド
  5. RRO ターゲットをビルドするには、緑の [Play] ボタンを押して、エミュレータまたは Android デバイスで RRO の Gradle ビルドを作成します。

  6. RRO が適切にインストールされていることを確認するために、以下を実行します。

    shell:~$ adb shell cmd overlay list --user current | grep -i com.example
    com.example.caruicodelab
    [ ] com.example.caruirro
    

    このコマンドはシステム上の RRO パッケージの状態に関する有益な情報を表示します。

    • [ ] は RRO がインストールされ、有効化の準備が完了していることを示します。
    • --- は RRO がインストールされているものの、エラーが発生していることを示します。
    • [X] は RRO がインストールされ、有効化されていることを示します。

    RRO にエラーが含まれる場合は、先に進む前にランタイム リソース オーバーレイのトラブルシューティングをご確認ください。

  7. RRO を有効にして、有効になっていることを確認します。

    shell:~$ adb shell cmd overlay enable --user current com.example.caruirro
    shell:~$ adb shell cmd overlay list --user current | grep -i com.example
    com.example.caruicodelab
    [x] com.example.caruirro
    

アプリに文字列「Hello World RRO」が表示されます。

Hello World RRO!
図 4: Hello World RRO!

お疲れさまでした。これで最初の RRO が作成されました。

RRO を使用する際には、リンク オプションに記載の Android Asset Packaging Tool(AAPT2)フラグの --no-resource-deduping--no-resource-removal を使用することもできます。この Codelab でフラグを追加することは必須ではありませんが、ご自身の RRO ではそれを使用して、リソースの削除(およびデバッグの手間)が発生しないようにします。次のように RRO の build.gradle ファイルに追加できます。

android {
    …
    aaptOptions {
        additionalParameters "--no-resource-deduping", "--no-resource-removal"
    }
}

これらのフラグの詳細については、パッケージのビルドAAPT2 をご覧ください。

Android アプリで RRO を使用して car-ui-lib コンポーネントを変更する

このページではランタイム リソース オーバーレイ(RRO)を使って、Android アプリの car-ui-lib ライブラリからのコンポーネントを変更する方法について説明します。

ツールバーの背景色を設定する

所要時間: 15 分

ツールバーの背景色を変更する方法は次のとおりです。

  1. 以下の値を RRO アプリに追加して、リソースを明るい緑色(#0F0)に設定します。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <drawable name="car_ui_toolbar_background">#0F0</drawable>
    </resources>
    

    car-ui-lib ライブラリには car_ui_toolbar_background という名前のリソースが含まれます。このリソースが RRO の設定に含まれる場合、誤った値がターゲットとなるため、ツールバーは変更されません。

  2. RRO の AndroidManifest.xml で、targetNamecar-ui-lib を指すように更新します。

    …
    android:targetName="car-ui-lib"
    …
    

    RRO の対象とする各ターゲット パッケージに対して新しい RRO パッケージを作成する必要があります。たとえば、2 つの異なるターゲットに対してオーバーレイを作成する場合、2 つのオーバーレイ APK を作成する必要があります。

  3. 先ほどと同じ方法で、RRO のビルド、検証、インストール、有効化を行います。

アプリは次のように表示されます。

ツールバーの新しい背景色
図 5: ツールバーの新しい背景色

RRO レイアウトとスタイル

所要時間: 15 分

この演習では、先ほどビルドしたアプリと同様の新しいアプリをビルドします。このアプリではレイアウトのオーバーレイが可能です。先ほどと同じ手順に沿って行うか、既存のアプリを修正します。

  1. 必ず次の行を overlayable.xml に追加してください。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <overlayable name="CarUiCodelab">
        <policy type="public">
          <item type="string" name="sample_text"/>
          <item type="layout" name="activity_main"/>
          <item type="id" name="textView"/>
        </policy>
      </overlayable>
    </resources>
    
  2. activity_main.xml は次のようになっていることを確認します。

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:id="@+id/textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/sample_text"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  3. RRO アプリで res/layout/activity_main.xml を作成し、以下を追加します。

    <?xml version="1.0" encoding="utf-8"?>
    <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
      <TextView
          android:id="@+id/textView"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@string/sample_text"
          android:textAppearance="@style/TextAppearance.CarUi"
          android:layout_gravity="center_vertical|center_horizontal"/>
    </FrameLayout>
    
  4. res/values/styles.xml を更新してスタイルを RRO に追加します。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <style name="TextAppearance.CarUi" parent="android:TextAppearance.DeviceDefault">
            <item name="android:textColor">#0f0</item>
            <item name="android:textSize">100sp</item>
        </style>
    </resources>
    
  5. 新しいアプリを指すように、AndroidManifest.xmltargetName を変更します。

    …
    android:targetName="CarUiCodelab"
    …
    
  6. RRO の sample_overlay.xml ファイルにリソースを追加します。

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="string/sample_text" value="@string/sample_text"/>
        <item target="id/textView" value="@id/textView"/>
        <item target="layout/activity_main" value="@layout/activity_main"/>
    </overlay>
    
  7. 先ほどと同じ方法でアプリと RRO をビルドしてインストールします(緑の [Play] ボタン)。忘れずに RRO を有効にしてください。

アプリと RRO は次のようにレンダリングされます。レイアウト RRO で指定されているとおり、「Hello World RRO」のテキストは緑色で中心に配置されています。

Hello World RRO
図 6: Hello World RRO

CarUiRecyclerView をアプリに追加する

所要時間: 15 分

CarUiRecyclerView インターフェースは car-ui-lib リソース経由でカスタマイズされる RecyclerView にアクセスできる API を提供します。たとえば、CarUiRecyclerView は実行時にフラグを確認して、スクロールバーを有効にすべきかどうかを判断し、対応するレイアウトを選択します。

CarUiRecyclerViewContainer
図 7. CarUiRecyclerViewContainer
  1. CarUiRecyclerView を追加するには、activity_main.xmlMainActivity.java ファイルに追加します。新しいアプリをゼロから作成することも、既存のアプリを修正することもできます。既存のアプリを修正する場合は、overlayable.xml からの宣言されていないリソースを必ず削除してください。

    activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <com.android.car.ui.recyclerview.CarUiRecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    

    以下のエラーが表示される可能性がありますが、無視してください。

    Cannot resolve class com.android.car.ui.recyclerview.CarUiRecyclerView

    クラスが正しく入力されており、car-ui-lib を依存関係として追加している限り、APK のビルドとコンパイルはできます。エラーを削除するには、[File] > [Invalidate Caches] を選択して、[Invalidate and Restart.] をクリックします。

    MainActivity.java に以下を追加します。

    package com.example.caruicodelab;
    
    import android.app.Activity;
    import android.os.Bundle;
    import com.android.car.ui.core.CarUi;
    import com.android.car.ui.recyclerview.CarUiContentListItem;
    import com.android.car.ui.recyclerview.CarUiListItem;
    import com.android.car.ui.recyclerview.CarUiListItemAdapter;
    import com.android.car.ui.recyclerview.CarUiRecyclerView;
    import com.android.car.ui.toolbar.ToolbarController;
    import java.util.ArrayList;
    
    /** Activity with a simple car-ui layout. */
    public class MainActivity extends Activity {
    
        private final ArrayList<CarUiListItem> mData = new ArrayList<>();
        private CarUiListItemAdapter mAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ToolbarController toolbar = CarUi.getToolbar(this);
            toolbar.setTitle(getTitle());
            toolbar.setLogo(R.mipmap.ic_launcher_round);
    
            CarUiRecyclerView recyclerView = findViewById(R.id.list);
            mAdapter = new CarUiListItemAdapter(generateSampleData());
            recyclerView.setAdapter(mAdapter);
        }
    
        private ArrayList<CarUiListItem> generateSampleData() {
            for (int i = 0; i < 20; i++) {
                CarUiContentListItem item = new CarUiContentListItem(CarUiContentListItem.Action.ICON);
                item.setTitle("Title " + i);
                item.setPrimaryIconType(CarUiContentListItem.IconType.CONTENT);
                item.setIcon(getDrawable(R.drawable.ic_launcher_foreground));
                item.setBody("body " + i);
                mData.add(item);
            }
            return mData;
    }
    
  2. アプリをこれまでと同じようにビルドしてインストールします。

これで CarUiRecyclerView が表示されるようになります。

CarUiRecyclerView
図 7. CarUiRecyclerView

RRO を使用してスクロールバーを削除する

所要時間: 10 分

この演習では RRO を使用して CarUiRecyclerView からスクロールバーを削除する方法を示します。

  1. RRO に以下のファイルを追加して修正します。

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.caruirro">
    
        <application android:hasCode="false" />
    
        <uses-sdk
            android:minSdkVersion="29"
            android:targetSdkVersion="29"/>
    
        <overlay
            android:targetPackage="com.example.caruicodelab"
            android:targetName="car-ui-lib"
            android:isStatic="false"
            android:resourcesMap="@xml/sample_overlay"
            />
    </manifest>
    

    res/values/bools.xml

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <bool name="car_ui_scrollbar_enable">false</bool>
    </resources>
    

    リソース car_ui_scrollbar_enablecar-ui-lib ブール値リソースで、自動車向けに最適された、上下ボタンが付いたスクロールバーを CarUiRecyclerView に表示するかどうかをコントロールします。false に設定すると、CarUiRecyclerView は AndroidX RecyclerView のように動作します。

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
        <item target="bool/car_ui_scrollbar_enable" value="@bool/car_ui_scrollbar_enable"/>
    </overlay>
    

アプリをこれまでと同じようにビルドしてインストールします。スクロールバーは CarUiRecyclerView から削除されました。

スクロールバーのない CarUiRecyclerView
図 8. スクロールバーのない CarUiRecyclerView

レイアウトを使用して CarUiRecyclerView スクロールバーをオーバーレイする

所要時間: 15 分

この演習では、CarUiRecyclerView のスクロールバー レイアウトを修正します。

  1. 以下のファイルを RRO アプリに追加して修正します。

    res/layout/car_ui_recycler_view_scrollbar.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="112dp"
        android:layout_height="match_parent"
        android:id="@+id/car_ui_scroll_bar">
        <!-- View height is dynamically calculated during layout. -->
        <View
            android:id="@+id/car_ui_scrollbar_thumb"
            android:layout_width="6dp"
            android:layout_height="20dp"
            android:layout_alignParentTop="true"
            android:layout_centerHorizontal="true"
            android:background="@drawable/car_ui_recyclerview_scrollbar_thumb"/>
        <View
            android:id="@+id/car_ui_scrollbar_track"
            android:layout_width="10dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:layout_centerHorizontal="true"
            android:layout_above="@+id/car_ui_scrollbar_page_up"/>
        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:background="#323232"
            android:layout_toLeftOf="@+id/car_ui_scrollbar_thumb"
            android:layout_above="@+id/car_ui_scrollbar_page_up"
            android:layout_marginRight="5dp"/>
        <View
            android:layout_width="2dp"
            android:layout_height="match_parent"
            android:layout_marginTop="10dp"
            android:background="#323232"
            android:layout_toRightOf="@+id/car_ui_scrollbar_thumb"
            android:layout_above="@+id/car_ui_scrollbar_page_up"
            android:layout_marginLeft="5dp"/>
        <ImageView
            android:id="@+id/car_ui_scrollbar_page_up"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:focusable="false"
            android:hapticFeedbackEnabled="false"
            android:src="@drawable/car_ui_recyclerview_ic_up"
            android:scaleType="centerInside"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:layout_centerHorizontal="true"
            android:layout_above="@+id/car_ui_scrollbar_page_down"/>
        <ImageView
            android:id="@+id/car_ui_scrollbar_page_down"
            android:layout_width="75dp"
            android:layout_height="75dp"
            android:focusable="false"
            android:hapticFeedbackEnabled="false"
            android:src="@drawable/car_ui_recyclerview_ic_down"
            android:scaleType="centerInside"
            android:background="?android:attr/selectableItemBackgroundBorderless"
            android:layout_centerHorizontal="true"
            android:layout_alignParentBottom="true"/>
    </RelativeLayout>
    

    レイアウト ファイルをオーバーレイするには、すべての ID と名前空間の属性を RRO の overlay.xml に追加する必要があります。以下のファイルをご覧ください。

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
       <item target="drawable/car_ui_recyclerview_ic_down" value="@drawable/car_ui_recyclerview_ic_down"/>
       <item target="drawable/car_ui_recyclerview_ic_up" value="@drawable/car_ui_recyclerview_ic_up"/>
       <item target="drawable/car_ui_recyclerview_scrollbar_thumb" value="@drawable/car_ui_recyclerview_scrollbar_thumb"/>
       <item target="id/car_ui_scroll_bar" value="@id/car_ui_scroll_bar"/>
       <item target="id/car_ui_scrollbar_thumb" value="@id/car_ui_scrollbar_thumb"/>
       <item target="id/car_ui_scrollbar_track" value="@id/car_ui_scrollbar_track"/>
       <item target="id/car_ui_scrollbar_page_up" value="@id/car_ui_scrollbar_page_up"/>
       <item target="id/car_ui_scrollbar_page_down" value="@id/car_ui_scrollbar_page_down"/>
       <item target="layout/car_ui_recyclerview_scrollbar" value="@layout/car_ui_recyclerview_scrollbar"/>
    </overlay>
    

    res/drawable/car_ui_recyclerview_ic_up.xml

    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="48.0"
        android:viewportHeight="48.0">
        <path
            android:pathData="M14.83,30.83L24,21.66l9.17,9.17L36,28 24,16 12,28z"
            android:fillColor="#0000FF"/>
    </vector>
    

    res/drawable/car_ui_recyclerview_ic_down.xml

    <?xml version="1.0" encoding="utf-8"?>
    <vector xmlns:android="http://schemas.android.com/apk/res/android"
        android:width="48dp"
        android:height="48dp"
        android:viewportWidth="48.0"
        android:viewportHeight="48.0">
        <path
            android:pathData="M14.83,16.42L24,25.59l9.17,-9.17L36,19.25l-12,12 -12,-12z"
            android:fillColor="#0000FF"/>
    </vector>
    

    res/drawable/car_ui_recyclerview_scrollbar_thumb.xml

    <?xml version="1.0" encoding="utf-8"?>
    <shape
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:shape="rectangle">
        <solid android:color="#0000FF" />
        <corners android:radius="100dp"/>
    </shape>
    

    これらのファイルの仕組みを確認しておくことをおすすめします。

    簡単に説明すると、寸法と色はハードコードされています。ただし、これらの値を dimens.xml および colors.xml で宣言するか、res/color/ フォルダで色ファイルとして指定することをおすすめします。詳しくは、コントリビューター向け AOSP Java コードスタイルをご覧ください。

  2. アプリをこれまでと同じようにビルドしてインストールします。青のスクロールバーとグレーのレールの CarUiRecyclerView がビルドされます。

お疲れさまでした。スクロールバーの下に上下の矢印が表示されます。Android Studio から Gradle ビルドシステムを使用して、正常に RRO を car-ui-lib レイアウト リソース ファイルに適用できました。

青のスクロールバーとグレーのレールの CarUiRecyclerView
図 9. 青のスクロールバーとグレーのレールの CarUiRecyclerView

RRO リストアイテム

所要時間: 15 分

この時点で、AndroidX ではなくフレームワーク コンポーネントを使用して、RRO を car-ui-lib コンポーネントに適用しました。RRO で AndroidX コンポーネントを使用するには、コンポーネントの依存関係をアプリと RRO build.gradle. の両方に追加する必要があります。また、コンポーネントの attrs をアプリの overlayable.xml と RRO sample_overlay.xml に追加する必要があります。

ライブラリ(car-ui-lib)では ConstraintLayout に加え、他の AndroidX コンポーネントを使用するため、overlayable.xml は次のようになります。

<?xml version='1.0' encoding='UTF-8'?>
<resources>
    <overlayable name="car-ui-lib">
        …
        <item type="attr" name="layout_constraintBottom_toBottomOf"/>
        <item type="attr" name="layout_constraintBottom_toTopOf"/>
        <item type="attr" name="layout_constraintCircle"/>
        <item type="attr" name="layout_constraintCircleAngle"/>
        <item type="attr" name="layout_constraintCircleRadius"/>
        <item type="attr" name="layout_constraintDimensionRatio"/>
        <item type="attr" name="layout_constraintEnd_toEndOf"/>
        <item type="attr" name="layout_constraintEnd_toStartOf"/>
        <item type="attr" name="layout_constraintGuide_begin"/>
        <item type="attr" name="layout_constraintGuide_end"/>
        <item type="attr" name="layout_constraintGuide_percent"/>
        <item type="attr" name="layout_constraintHeight_default"/>
        <item type="attr" name="layout_constraintHeight_max"/>
        <item type="attr" name="layout_constraintHeight_min"/>
        <item type="attr" name="layout_constraintHeight_percent"/>
        <item type="attr" name="layout_constraintHorizontal_bias"/>
        <item type="attr" name="layout_constraintHorizontal_chainStyle"/>
        <item type="attr" name="layout_constraintHorizontal_weight"/>
        <item type="attr" name="layout_constraintLeft_creator"/>
        <item type="attr" name="layout_constraintLeft_toLeftOf"/>
        <item type="attr" name="layout_constraintLeft_toRightOf"/>
        <item type="attr" name="layout_constraintRight_creator"/>
        <item type="attr" name="layout_constraintRight_toLeftOf"/>
        <item type="attr" name="layout_constraintRight_toRightOf"/>
        <item type="attr" name="layout_constraintStart_toEndOf"/>
        <item type="attr" name="layout_constraintStart_toStartOf"/>
        <item type="attr" name="layout_constraintTag"/>
        <item type="attr" name="layout_constraintTop_creator"/>
        <item type="attr" name="layout_constraintTop_toBottomOf"/>
        <item type="attr" name="layout_constraintTop_toTopOf"/>
        <item type="attr" name="layout_constraintVertical_bias"/>
        <item type="attr" name="layout_constraintVertical_chainStyle"/>
        …
    </overlayable>
</resources>
  1. ConstraintLayout を使用して CarUiRecyclerView のリストアイテムのレイアウトを変更します以下のファイルを RRO に追加して修正します。

    res/xml/sample_overlay.xml

    <?xml version="1.0" encoding="utf-8"?>
    <overlay>
       <item target="id/car_ui_list_item_touch_interceptor" value="@id/car_ui_list_item_touch_interceptor"/>
       <item target="id/car_ui_list_item_reduced_touch_interceptor" value="@id/car_ui_list_item_reduced_touch_interceptor"/>
       <item target="id/car_ui_list_item_start_guideline" value="@id/car_ui_list_item_start_guideline"/>
       <item target="id/car_ui_list_item_icon_container" value="@id/car_ui_list_item_icon_container"/>
       <item target="id/car_ui_list_item_icon" value="@id/car_ui_list_item_icon"/>
       <item target="id/car_ui_list_item_content_icon" value="@id/car_ui_list_item_content_icon"/>
       <item target="id/car_ui_list_item_avatar_icon" value="@id/car_ui_list_item_avatar_icon"/>
       <item target="id/car_ui_list_item_title" value="@id/car_ui_list_item_title"/>
       <item target="id/car_ui_list_item_body" value="@id/car_ui_list_item_body"/>
       <item target="id/car_ui_list_item_action_container_touch_interceptor" value="@id/car_ui_list_item_action_container_touch_interceptor"/>
       <item target="id/car_ui_list_item_action_container" value="@id/car_ui_list_item_action_container"/>
       <item target="id/car_ui_list_item_action_divider" value="@id/car_ui_list_item_action_divider"/>
       <item target="id/car_ui_list_item_switch_widget" value="@id/car_ui_list_item_switch_widget"/>
       <item target="id/car_ui_list_item_checkbox_widget" value="@id/car_ui_list_item_checkbox_widget"/>
       <item target="id/car_ui_list_item_radio_button_widget" value="@id/car_ui_list_item_radio_button_widget"/>
       <item target="id/car_ui_list_item_supplemental_icon" value="@id/car_ui_list_item_supplemental_icon"/>
       <item target="id/car_ui_list_item_end_guideline" value="@id/car_ui_list_item_end_guideline"/>
       <item target="attr/layout_constraintBottom_toBottomOf" value="@attr/layout_constraintBottom_toBottomOf"/>
       <item target="attr/layout_constraintBottom_toTopOf" value="@attr/layout_constraintBottom_toTopOf"/>
       <item target="attr/layout_constraintEnd_toEndOf" value="@attr/layout_constraintEnd_toEndOf"/>
       <item target="attr/layout_constraintEnd_toStartOf" value="@attr/layout_constraintEnd_toStartOf"/>
       <item target="attr/layout_constraintGuide_begin" value="@attr/layout_constraintGuide_begin"/>
       <item target="attr/layout_constraintGuide_end" value="@attr/layout_constraintGuide_end"/>
       <item target="attr/layout_constraintHorizontal_bias" value="@attr/layout_constraintHorizontal_bias"/>
       <item target="attr/layout_constraintLeft_toLeftOf" value="@attr/layout_constraintLeft_toLeftOf"/>
       <item target="attr/layout_constraintLeft_toRightOf" value="@attr/layout_constraintLeft_toRightOf"/>
       <item target="attr/layout_constraintRight_toLeftOf" value="@attr/layout_constraintRight_toLeftOf"/>
       <item target="attr/layout_constraintRight_toRightOf" value="@attr/layout_constraintRight_toRightOf"/>
       <item target="attr/layout_constraintStart_toEndOf" value="@attr/layout_constraintStart_toEndOf"/>
       <item target="attr/layout_constraintStart_toStartOf" value="@attr/layout_constraintStart_toStartOf"/>
       <item target="attr/layout_constraintTop_toBottomOf" value="@attr/layout_constraintTop_toBottomOf"/>
       <item target="attr/layout_constraintTop_toTopOf" value="@attr/layout_constraintTop_toTopOf"/>
       <item target="attr/layout_goneMarginBottom" value="@attr/layout_goneMarginBottom"/>
       <item target="attr/layout_goneMarginEnd" value="@attr/layout_goneMarginEnd"/>
       <item target="attr/layout_goneMarginLeft" value="@attr/layout_goneMarginLeft"/>
       <item target="attr/layout_goneMarginRight" value="@attr/layout_goneMarginRight"/>
       <item target="attr/layout_goneMarginStart" value="@attr/layout_goneMarginStart"/>
       <item target="attr/layout_goneMarginTop" value="@attr/layout_goneMarginTop"/>
       <item target="attr/layout_constraintVertical_chainStyle" value="@attr/layout_constraintVertical_chainStyle"/>
       <item target="layout/car_ui_list_item" value="@layout/car_ui_list_item"/>
    </overlay>
    

    res/layout/car_ui_list_item.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:tag="carUiListItem"
        android:minHeight="@dimen/car_ui_list_item_height">
    
        <!-- The following touch interceptor views are sized to encompass the specific sub-sections of
        the list item view to easily control the bounds of a background ripple effects. -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <!-- This touch interceptor does not include the action container -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_reduced_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_start_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_begin="@dimen/car_ui_list_item_start_inset" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_icon_container"
            android:layout_width="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="@+id/car_ui_list_item_start_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <ImageView
                android:id="@+id/car_ui_list_item_icon"
                android:layout_width="@dimen/car_ui_list_item_icon_size"
                android:layout_height="@dimen/car_ui_list_item_icon_size"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_content_icon"
                android:layout_width="@dimen/car_ui_list_item_content_icon_width"
                android:layout_height="@dimen/car_ui_list_item_content_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_avatar_icon"
                android:background="@drawable/car_ui_list_item_avatar_icon_outline"
                android:layout_width="@dimen/car_ui_list_item_avatar_icon_width"
                android:layout_height="@dimen/car_ui_list_item_avatar_icon_height"
                android:layout_gravity="center"
                android:visibility="gone"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <CarUiTextView
            android:id="@+id/car_ui_list_item_title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:singleLine="@bool/car_ui_list_item_single_line_title"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toTopOf="@+id/car_ui_list_item_body"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintVertical_chainStyle="packed"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
        <CarUiTextView
            android:id="@+id/car_ui_list_item_body"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/car_ui_list_item_text_start_margin"
            android:textAppearance="@style/TextAppearance.CarUi.ListItem.Body"
            android:layout_gravity="right"
            android:gravity="right"
            android:textAlignment="viewEnd"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/car_ui_list_item_action_container"
            app:layout_constraintStart_toEndOf="@+id/car_ui_list_item_icon_container"
            app:layout_constraintTop_toBottomOf="@+id/car_ui_list_item_title"
            app:layout_goneMarginStart="@dimen/car_ui_list_item_text_no_icon_start_margin" />
    
        <!-- This touch interceptor is sized and positioned to encompass the action container   -->
        <com.android.car.ui.SecureView
            android:id="@+id/car_ui_list_item_action_container_touch_interceptor"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:background="@drawable/car_ui_list_item_background"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="@id/car_ui_list_item_action_container"
            app:layout_constraintEnd_toEndOf="@id/car_ui_list_item_action_container"
            app:layout_constraintStart_toStartOf="@id/car_ui_list_item_action_container"
            app:layout_constraintTop_toTopOf="@id/car_ui_list_item_action_container" />
    
        <FrameLayout
            android:id="@+id/car_ui_list_item_action_container"
            android:layout_width="wrap_content"
            android:minWidth="@dimen/car_ui_list_item_icon_container_width"
            android:layout_height="0dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@+id/car_ui_list_item_end_guideline"
            app:layout_constraintTop_toTopOf="parent">
    
            <View
                android:id="@+id/car_ui_list_item_action_divider"
                android:layout_width="@dimen/car_ui_list_item_action_divider_width"
                android:layout_height="@dimen/car_ui_list_item_action_divider_height"
                android:layout_gravity="start|center_vertical"
                android:background="@drawable/car_ui_list_item_divider" />
    
            <Switch
                android:id="@+id/car_ui_list_item_switch_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <CheckBox
                android:id="@+id/car_ui_list_item_checkbox_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <RadioButton
                android:id="@+id/car_ui_list_item_radio_button_widget"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:clickable="false"
                android:focusable="false" />
    
            <ImageView
                android:id="@+id/car_ui_list_item_supplemental_icon"
                android:layout_width="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_height="@dimen/car_ui_list_item_supplemental_icon_size"
                android:layout_gravity="center"
                android:scaleType="fitCenter" />
        </FrameLayout>
    
        <androidx.constraintlayout.widget.Guideline
            android:id="@+id/car_ui_list_item_end_guideline"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            app:layout_constraintGuide_end="@dimen/car_ui_list_item_end_inset" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    
  2. car_ui_list_item.xml はアプリの依存関係に含まれていないいくつかのコンポーネントまたはリソースを参照します。これらは car-ui-lib リソースです。これを修正するには、car-ui-lib を依存関係として app/build.gradle の RRO アプリに追加します。

    dependencies {
        implementation 'com.android.car.ui:car-ui-lib:2.0.0'
        implementation 'androidx.appcompat:appcompat:1.4.1'
        implementation 'com.google.android.material:material:1.4.0'
    }
    

タイトルと本文が左揃えではなく右揃えになりました。

右揃えのタイトルと本文
図 10. 右揃えのタイトルと本文

属性が overlayable.xml という名前の car-ui-lib ファイルと RRO sample_overlay.xml に存在している場合、AndroidX コンポーネント(ConstraintLayout)を使用して、RRO を car-ui-lib にのみ適用しました。自分のアプリで同様の対応をすることは可能です。car-ui-lib と同様に、すべての該当する attrs をアプリの overlayable.xml に追加します。

ただし、アプリの build.gradlecar-ui-lib が依存関係として存在する場合(アプリが car-ui-lib コンポーネントを使用する場合)、AndroidX コンポーネントを使用して、アプリを RRO することはできません。属性マッピングは car-ui-lib ライブラリの overlayable.xml ですでに定義されているため、それをアプリの overlayable.xmlcar-ui-lib を依存関係として追加すると、次のような mergeDebugResources エラーが発生する場合があります。これらの属性が複数の overlayable.xml ファイルに存在するためです。

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:mergeDebugResources'