どっこと備忘録群

アウトプットしないとインプットできない私が Androidアプリ開発をメインとした備忘録を載せています。

Viewを扱う

Viewを表示する

レイアウトファイルに要素を追加する。

ActivityFragmentの画面表示処理でレイアウトを指定するが、そのレイアウトに表示したいViewを追加する。

例えばAndroid Studioでシンプルなプロジェクトを新規作成すると、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:id="@+id/main"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".MainActivity">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Hello World!"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ConstraintLayout は複数のViewを配置するときに使う ViewGroup 派生のクラスのひとつ(説明は割愛)。 その中にTextViewというテキストを表示するためのViewが配置されている。 新規作成したプロジェクトでは、↑のレイアウトをActivity.setContentView(resourceId)により表示している。

resourceId(リソースID)はリソースファイルを作成するとAndroid Studioが自動生成してくれる。 レイアウトファイルの場合はR.layout.filename が自動生成されるので、これを指定する。

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ...
    // Activityで表示する画面として↑のレイアウトファイルを指定
    setContentView(R.layout.activity_main)
    ...
  }

Viewの大きさを設定する

レイアウトファイルにViewを配置する際、必須項目としてandroid:layout_widthandroid:layout_heightがある。 この設定項目でViewの大きさを指定することができる。

Viewの幅を設定する

android:layout_widthで設定する。以下のいずれかを指定する。

  • wrap_content。OSが必要な最小幅を計算して自動で大きさを決めてくれる。
  • match_parent。領域いっぱいに表示する。
  • 数値。dpという単位を使って指定する。例えば100を指定する場合はandroid:layout_width="100dp"となる。

Viewの高さを設定する

android:layout_heightで設定する。以下のいずれかを指定する。

  • wrap_content。OSが必要な最小の高さを計算して自動で大きさを決めてくれる。
  • match_parent。領域いっぱいに表示する。
  • 数値。dpという単位を使って指定する。例えば100を指定する場合はandroid:layout_height="100dp"となる。

背景を設定する

android:backgroundで設定する。以下の設定方法がある。

1. リソース指定

res/colors.xmlに設定した色や、drawableリソースを指定する。

  • 色の場合は @colors/resouce_nameで指定する。
  • drawableリソースの場合は @drawable/resource_nameで指定する。

例えばres/colors.xml に以下を定義してある場合、

<color name="background_color">#FF0000</color>

以下のように指定する。

<View
  ...
  android:background="@color/background_color" />

2. color指定

#RGB の形式で指定する。例えば赤色(FF0000)を指定する場合は以下。

<View
  ...
  android:background="#FF0000" />

不透明度(alpha)も合わせて指定可能。その場合は#ARGBで指定する。

背景をプログラム上で設定する

View.setBackgroundResource(resourceId)View.setBackgroundColor(color)を使う。

setBackgroundResource(resourceId) ではリソースIDを指定する。 drawableリソース(R.drawable.resource_name)、colorリソース(R.color.resource_name)どちらでも利用可能。

setBackgroundColor(color)ではRGB形式で色を指定する。 16進数であることを明示するためには0xRGB の形で設定する。

例えば赤色(FF0000)を指定する場合は以下。

val view = findViewById(R.id.view)
view.setBackgroundColor(0xFF0000)

また、不透明度(alpha)も合わせて指定可能。その場合は#ARGBで指定する。 android.graphics.Colorクラスによくある色指定の定義が実装されているのでこちらも参考にできる。

Viewの表示位置を設定する

android:layout_gravityで設定する。 親のViewGroupの領域に対してViewをどこに寄せて表示するかを設定でき、以下が設定できる。

  • center
  • center_vertical
  • center_horizontal
  • start(=left)
  • top
  • end(=right)
  • bottom

クリックリスナーを設定する

コード上でクリックリスナーを設定する

クリックリスナーを設定するViewにIDを付与し、コードで参照&クリックリスナー付与を実装する。

<TextView
   android:id="@+id/text_view" 
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="テスト"
   android:onClick="testClicked" />
val textView = findViewById(R.id.text_view)
textView.setOnClickListener {
  Log.d("TAG", "TextView テスト is clicked")
}

XML上でクリックリスナーを設定する

dataBindingを使わない通常のケースでも似たような実装ができる。 XMLでクリックリスナーを設定するには、任意のViewに対して android:onClickを設定する。 たとえば、TextViewに設定するときは以下。

<TextView
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:text="テスト"
   android:onClick="testClicked" />

このXMLを読み込むクラスに、testClicked(View v)関数を実装する。

override fun testClicked(v: View) {
   Log.d("TAG", "TextView テスト is clicked")
}

参考

親ビューからはみ出して子ビューを表示する

「表示する領域のViewGroup」に以下を設定する。

android:clipChildren="false"

はみ出す子ビューの親ではなくはみ出した子ビューを表示する**領域のViewGroup**に設定すること。 つまり以下のように記述する。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_width="match_parent"
   android:clipChildren="false"            // 押し込まれる領域にclipChildren:falseを設定
   android:layout_height="match_parent">

   <FrameLayout
     android:layout_width="250dp"
     android:layout_height="250dp"
     android:layout_gravity="center">
   
     <View
       android:layout_width="150dp"
       android:layout_height="50dp"
       android:layout_marginTop="-25dp" // 親のFrameLayoutの領域外
       android:background="@android:color/black"
       android:gravity="center" />
  </FrameLayout>
</FrameLayout>

参考

タップした時のViewにリップルエフェクトを適用する

リップルエフェクトは、ボタンやリスト項目などをタップした際に、水の波紋のように色が広がるアニメーション効果のこと。

実装方法

XMLレイアウトファイルのView属性に指定するだけ。

1. 背景として設定する場合

通常の背景として設定する場合は、android:backgroundに属性を指定する。

android:background="?attr/selectableItemBackground"

2. 既存の背景色を残したい場合

すでにandroid:backgroundで色を設定している場合は、android:foregroundに指定することで背景色の上にリップル効果を重ねることができる。

android:foreground="?attr/selectableItemBackground"

Viewのコンストラクタを理解する

Viewクラスには、主に3つのコンストラクタがある。

  • constructor(context: Context): コードからビューを生成する際に使用される。XML属性やスタイルは考慮されないため、必要に応じて手動で設定する必要がある。
  • constructor(context: Context, attrs: AttributeSet?): XMLレイアウトからビューがインフレートされる際に呼び出される。 styleが明示的に指定されていない場合のみ。
  • constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): これもXMLからインフレートされる際に呼び出されるが、 styleが明示的に指定されている場合はこちらが呼び出される。

既存コンポーネントを拡張する場合のstyle適用

既存のコンポーネントを拡張する場合、上記3つのコンストラクタと合わせてデフォルトのスタイルを継承させ、カスタムViewに適用させる。 例えば、EditTextを継承したHomeEditTextクラスを実装してみる。

1. カスタム属性の追加

attrs.xmlファイルで、カスタムビューのデフォルトスタイルを参照するためのカスタム属性を定義する。

<resources>
    <attr name="hogeEditTextStyle" format="reference" />
</resources>

2. カスタムスタイルの作成

styles.xmlでカスタムスタイルを定義する。 このとき定義するスタイルは親コンポーネントのデフォルトスタイルを継承する。 EditTextの場合はandroid:Widget.EditTextが該当。

<resources>
    <style name="HogeEditTextStyle" parent="android:Widget.EditText">
        </style>
</resources>

3. テーマへのリンク

アプリのテーマが定義されているthemes.xmlに、先ほど作成したカスタム属性とスタイルをリンクさせる。

<style name="Theme.AppTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <item name="hogeEditTextStyle">@style/HogeEditTextStyle</item>
</style>

4. コンストラクタの実装

カスタムビューのクラスでコンストラクタを正しく実装する。

それぞれのコンストラクタで、defStyleAttrを正しく設定しスーパークラスを呼び出すように実装することで、 XMLからでもコードからでも、カスタムビューが期待通りの外観となる。

import androidx.appcompat.widget.AppCompatEditText

class HogeEditText(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
    AppCompatEditText(context, attrs, defStyleAttr) {

    // ①のコンストラクタ(コードからの生成)
    constructor(context: Context) : this(context, null, R.attr.hogeEditTextStyle)

    // ②のコンストラクタ(XMLからのインフレート)
    constructor(context: Context, attrs: AttributeSet?) : this(
        context,
        attrs,
        R.attr.hogeEditTextStyle
    )
}

ダークモードを実装する

ダークモード実装の3ステップ

必要最小限でダークモードに対応するための手順は以下の3ステップ。

  1. ダークモード表示の確認:現状のデザインを確認する。
  2. 色の修正values-night を使って色を最適化する。
  3. 切り替え動線の用意:設定画面などでユーザーが切り替えられるようにする。

1. ダークモード表示の確認

まず、ダークモードにした際にUIがどのように見えるか確認する。 Application#onCreate() 内で以下のコードを呼び出すと、強制的にダークモードが適用される。

AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)

この状態でアプリを実行し、テキストが背景と同化して読めなくなっていないか、アイコンが見えにくくなっていないかなどを確認する。

2. 色指定を修正する

Androidでは、valuesフォルダとvalues-nightフォルダを使い分けることで、テーマごとの色指定を管理できる。

  1. res/values/colors.xml にライトテーマの色を定義する。
  2. res/values-night/colors.xml を作成し、同じ name でダークテーマ用の色を定義する。

例:

<color name="background_color">#FFFFFF</color>

<color name="background_color">#121212</color>

システムはライトモードの時は values を、ダークモードの時は values-night を自動的に参照してくれるため、 ソースコードを変更することなくテーマを切り替えられます。

3. ライト/ダークモードの切り替え動線を用意

ユーザーが自由にテーマを選択できるように、設定画面などにスイッチを用意する。

切り替え時の処理

スイッチが押された際にテーマを更新し、その設定を SharedPreferences 等に保存する。

val value = AppCompatDelegate.MODE_NIGHT_YES // ユーザー選択値
AppCompatDelegate.setDefaultNightMode(value)

// 設定を保存
getSharedPreferences("sample", MODE_PRIVATE)
  .edit().putInt("theme_setting", value).apply()

アプリ起動時の処理

アプリ起動時に保存済みの設定を読み込み、適用するように Application#onCreate() を更新する。

val value = getSharedPreferences("sample", MODE_PRIVATE)
            .getInt("theme_setting", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)

AppCompatDelegate.setDefaultNightMode(value)

参考

カスタムViewにダークモードを適用する

values-nightフォルダを用意してcolors.xmlを追加&参照させることは簡単だが、 カスタムViewのレイアウトでは、merge タグにViewを配置することが多いがmergeタグにはbackgroundの設定は聞かない。 TypedValue を使うことで、?android:attr/colorBackground をコード上で設定することができる。

typedValue = TypedValue()
context.theme.resolveAttribute(android.R.attr.colorBackground, typedValue, true)
setBackgroundColor(typedValue.data)

参考

最終更新: 2026.6.11