レイアウト階層を最適化する

Compose をお試しください
Jetpack Compose は、Android で推奨される UI ツールキットです。Compose でレイアウトを扱う方法について説明します。

基本的なレイアウト構造を使用するとレイアウトが最も効率的になるというのは、よくある誤解です。ただし、アプリに追加する各ウィジェットとレイアウトには初期化、レイアウト、描画が必要です。たとえば、ネストされた LinearLayout インスタンスを使用する場合、ビュー階層が深くなりすぎることがあります。さらに、layout_weight パラメータを使用する LinearLayout の複数のインスタンスをネストすると、子ごとに 2 回測定する必要があるため、特にコストがかかる可能性があります。 これは、RecyclerView で使用される場合など、レイアウトを繰り返しインフレートさせる場合に特に重要です。

このドキュメントでは、 Layout Inspectorlint を使用してレイアウトを検証、最適化する方法について説明します。

レイアウトの検査

Android SDK Tools には、アプリの実行中にレイアウトを分析できる Layout Inspector ツールが含まれています。このツールを使用すると、レイアウトのパフォーマンスにおける非効率性を見つけることができます。

Layout Inspector では、接続されたデバイスまたはエミュレータで実行中のプロセスを選択し、レイアウト ツリーを表示できます。各ブロックの信号は、測定、レイアウト、描画のパフォーマンスを表し、潜在的な問題を特定するのに役立ちます。

たとえば図 1 は、RecyclerView 内のアイテムとして使用されるレイアウトを示しています。このレイアウトの左側には小さなビットマップ画像が、右側には 2 つの縦に並べられたテキスト アイテムが表示されます。パフォーマンスを向上させるために、複数回インフレートさせるレイアウト(この例のようなレイアウト)を最適化することが特に重要です。

リスト内の 1 つのアイテムを示す画像: 1 つの画像と 2 つの縦に並べられたテキスト
図 1.`RecyclerView` 内のアイテムの概念的なレイアウト RecyclerView.

Layout Inspector には、使用可能なデバイスと、そのデバイスで実行中のコンポーネントのリストが表示されます。[Windows] タブからコンポーネントを選択し、[Layout Inspector] をクリックして、選択したコンポーネントのレイアウト階層を表示します。 たとえば、図 2 は図 1 のリストアイテムのレイアウトを示しています。

Layout Inspector と LinearLayout のコンポジションを示す画像
図 2.図 1 のレイアウトのレイアウト階層(ネストされた LinearLayout インスタンスを使用)

レイアウトの見直し

前述のレイアウトのパフォーマンスは、LinearLayout のネスト化によって低下するため、レイアウトを平坦化する(レイアウトを狭く深くするのではなく、浅く広くする)ことによってパフォーマンスが向上する可能性があります。ConstraintLayout をルートノードとして使用すると、このようなレイアウトが可能になります。このデザインを ConstraintLayout を使用するように変換すると、レイアウト階層が 2 階層になります。

    <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:id="@+id/root"
      android:layout_width="match_parent"
      android:layout_height="52dp"
      android:background="#e4e6e4"
      android:padding="4dp">

      <ImageView
          android:id="@+id/image"
          android:layout_width="48dp"
          android:layout_height="48dp"
          android:background="#5c5c74"
          android:contentDescription="An example box"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintStart_toStartOf="parent"
          app:layout_constraintTop_toTopOf="parent" />

      <TextView
          android:id="@+id/title"
          android:layout_width="0dp"
          android:layout_height="0dp"
          android:layout_marginStart="4dp"
          android:background="#745c74"
          app:layout_constraintBottom_toTopOf="@+id/subtitle"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toEndOf="@id/image"
          app:layout_constraintTop_toTopOf="parent" />

      <TextView
          android:id="@+id/subtitle"
          android:layout_width="0dp"
          android:layout_height="0dp"
          android:background="#7e8d6e"
          app:layout_constraintBottom_toBottomOf="parent"
          app:layout_constraintEnd_toEndOf="parent"
          app:layout_constraintStart_toStartOf="@id/title"
          app:layout_constraintTop_toBottomOf="@+id/title" />
  </androidx.constraintlayout.widget.ConstraintLayout>
    

新しいレイアウトの検査は次のようになります。

3D Layout Inspector を示す画像
図 3.Layout Inspector の 3D モード

このレイアウトはリスト内のすべてのアイテムに使用されるため、わずかに見えてもメリットは数倍になります。

違いのほとんどは、LinearLayout デザインでの layout_weight の使用によるもので、測定速度が遅くなる可能性があります。これは、各レイアウトを適切に使用する方法の一例にすぎず、 レイアウトのウェイトを使用する必要があるかどうかを慎重に検討する必要があります。

一部の複雑なレイアウトでは、同じ UI 要素を複数回測定する無駄が生じる場合があります。この現象を二重課税といいます。 二重課税とその回避方法の詳細については、 パフォーマンス とビューの階層をご覧ください

lint の使用

レイアウト ファイルで lint ツール を実行して、ビュー階層の最適化の可能性を探ることを常におすすめします。lint は layoutopt に代わるツールであり、はるかに優れた機能を備えています。lint ルールの 例を次に示します

  • 複合ドローアブルを使用します。LinearLayout を含む ImageViewTextView は、複合ドローアブルとしてより効率的に処理できます。
  • ルートフレームを結合します。レイアウトのルートが、背景やパディングを指定しない FrameLayout の場合、 差し込みタグで置き換えることができます。効率性の向上はわずかです。
  • 不要なリーフを削除します。子や背景のないレイアウトは、たいていは削除可能です(表示されないため)。これにより、より平坦で効率的なレイアウト階層を実現できます。
  • 不要な親を削除します。子はあるが兄弟はなく、 ScrollViewやルート レイアウトでもなく、背景もないレイアウトを削除できます。子ビューを親に直接移動して、より平坦で効率的なレイアウト階層を実現することもできます。
  • 深いレイアウトを避けます。ネスト数が多すぎるレイアウトはパフォーマンスに悪影響を及ぼします。パフォーマンスを向上させるには、 ConstraintLayout, などの平坦なレイアウトの使用を検討します。lint チェックのデフォルトの最大深度は 10 です。

lint ツールのもう一つのメリットは、Android Studio に統合されていることです。Lint は、プログラムをコンパイルするたびに自動的に実行されます。Android Studio では、lint 検査を特定のビルド バリアントのみを対象に実行することも、すべてのビルド バリアントを対象に実行することも可能です。

[File > Settings > Project Settings] オプションを使用して、Android Studio 内で検査プロファイルを管理し、検査を設定することもできます。検査設定ページに、サポートされている検査が表示されます。

Android Studio の [Inspections] メニューを示す画像
図 4.検査設定ページ

Lint では、問題の自動的な修正、他者への提案、レビューを目的とした問題のコードへの直接ジャンプが行えます。

詳しくは、 レイアウトレイアウト リソースをご覧ください。