RecyclerView:画像ファイルをグリッド表示

投稿日:  更新日:

RecyclerViewで「画像ファイルをグリッド表示」する方法を、まとめます。

スマートフォンのアプリを作っていると、何度も遭遇するテクニックです。

※環境:Android Studio Ladybug | 2024.2.1 Patch 1
    Kotlin 2.0.0
    Compose Compilerプラグイン 2.0.0
    androidx.recyclerview:recyclerview:1.1.0

スポンサーリンク

画像ファイルをグリッド表示

RecyclerView+GridLayoutManagerを用います。

※RecyclerViewの詳細は「RecyclerViewの実装」を参照

サンプルの仕様

サンプルは以下の仕様で作成しています。開発するアプリに従い、置き換えてください。

  • assetsフォルダに保存されたJpegファイルが対象
  • 画像サイズは4000×3000
  • グリッド表示の積み上げ方向は縦
  • グリッド表示の列数は4

データの準備と入力

通常、画像ファイルの保存先は、アプリが管理するフォルダになります。ここでは安易にassetsフォルダを用いています。

Assetsフォルダーと画像ファイル

activity_photo_list.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=".PhotoListActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rcyPhotoList"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_photo_list)
        ...

        val _folder = "4000x3000"
        val _row = 4

        val _photos = assets.list(_folder)
        val _items = List(100) { PhotoData(it, "${_folder}/${_photos!![it]}") }

        val _adapter = PhotoAdapter(_items)
        val _manager = GridLayoutManager(this, _row)

        val _photoList = findViewById<RecyclerView>(R.id.rcyPhotoList)
        _photoList.adapter = _adapter
        _photoList.layoutManager = _manager
    }
data class PhotoData(
    val id: Int = 0,
    val filename: String = "default"
)

グリッド表示のポイント

GridLayoutManagerがアイテムを格子状に並べてくれます。

ただし、画像を隙間なく敷き詰めるためには、レイアウト(photo.item.xml)のトップにConstraintLayoutを用いる事と、ハイライトで示したポイントが必要です。

ImageViewの横縦アスペクト比を「4:3(長方形)」にしています。正方形にしたければ「1:1」に変えてください。。

<?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:padding="1dp">

    <ImageView
        android:id="@+id/imgPhoto"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:scaleType="centerCrop"
        app:layout_constraintDimensionRatio="4:3"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:srcCompat="@android:drawable/ic_menu_gallery" />

</androidx.constraintlayout.widget.ConstraintLayout>

画像ファイルの扱い

表示の対象はJpegファイルです。

JPEGは不可逆の画像圧縮データなので、JPEG(データ)⇒Bitmap(画像)へ変換するデコード処理が必要になります。

この処理をonBindViewHolder内で行っています。

class PhotoAdapter(val items: List<PhotoData>)
    : RecyclerView.Adapter<PhotoAdapter0.PhotoViewHolder>() {

    // ----- ビューホルダー ------------------------------------------
    class PhotoViewHolder(val view: View) 
        : RecyclerView.ViewHolder(view) {
            val photo = view.findViewById<ImageView>(R.id.imgPhoto)
        }

    // ----- アダプター本体 ------------------------------------------
    override fun getItemCount(): Int { return items.size }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int)
            : PhotoViewHolder {
        val _view = LayoutInflater.from(parent.context)
            .inflate(R.layout.photo_item, parent, false)
        return PhotoViewHolder(_view)
    }

    override fun onBindViewHolder(holder: PhotoViewHolder, position: Int) {
        val _filename = items[position].filename
        val _iStream = holder.view.context.assets.open(_filename)
        val _bitmap = BitmapFactory.decodeStream(_iStream)
        holder.photo.setImageBitmap(_bitmap)
    }
}

サンプルの結果

サンプルを実行した結果、グリッド表示は以下のようになります。

【4:3】

タイル表示(アスペクト比=4:3の画像)
【1:1】

タイル表示(アスペクト比=1:1の画像)
スポンサーリンク

スクロールに問題⇒出来ない・遅い

グリッド表示はできましたが、このサンプルは問題があります。スムーズなスクロールが出来ません。

スクロールを試みると、膨大なフレームのスキップを引き起こし、最後にANRとなってアプリが落ちます。

Skipped 129 frames!  The application may be doing too much work on its main thread.
Skipped 139 frames!  The application may be doing too much work on its main thread.
ANR in com.example.res.recyclerview (com.example.res.recyclerview/.PhotoListActivity)

この原因は次に上げる通りです。

  • (1)画像サイズが大きく、ファイルアクセスに時間がかかる
  • (2)画像サイズが大きく、JPEG⇒Bitmap変換に時間がかかる
  • (3)上記により、Mainスレッドが重くなり、フレーム処理が間に合わない

サンプルの実行に用いたエミュレータ―は、メモリーを増強(RAM:1024→4096MB)しています。それでも、市販のデバイスに比べて非力なために、問題の発生が顕著です。

非力なデバイス,、RAM拡張

また、これよりも非力にすると、グリッド表示の直後にANRが発生します。

ANR(Application Not Responding)
アプリが応答しなくなったことを通知しています。

ANRが発生すると、Androidは「待機するか・アプリを終了するか」を選択するダイアログを開き、ユーザーに指示を求めます。

場合によっては、ダイアログを開く前に、アプリが落ちてしまう場合もあります。

※スムーズなスクロールを実現する方法は「RecyclerView:グリッド表示をスムーズにスクロール」を参照。

スポンサーリンク

関連記事:

RecyclerViewは子Viewを並べて表示するコンテナタイプ(ConstraintLayoutと同じ)のViewです。 複数のデータをスクリーン上に一覧表示したい時、例えば電話帳のような「氏名+住所+電話番号」の一覧を表示する場合などに最適です。 アプリを開発していると一覧表示したいデータが多いことに気付きます。 なのでRecyclerViewはとても重要で重宝するViewです。 しかし、思い通りの表示を行わせるためのテクニックが多すぎて、使いこなしが難しいです。 今まで調べたテクニックを忘れないように、整理して書き残そうと思います。 今回は基本の「RecyclerViewの実装」です。 ...
RecyclerViewでアイテムのクリックイベントを取得し、処理を実行する方法を紹介します。 ...
RecyclerViewはアイテムのレイアウトをアイテム毎に変更できます。その時に使う値がViewTypeです。 ViewTypeでアイテムのレイアウトを変更する方法を紹介します。 ...
RecyclerViewは表示が変更される(アイテムの更新、スクロール)時、アイテムのViewをリサイクル(再生利用)します。 これにより余分なViewの作成が行われなくなり、メモリーの節約とパフォーマンスの向上が望めます。 リサイクルはCachedViewsとRecyclerPoolという2つのキャッシュで行われます。 このキャッシュを使ったリサイクルの動作を調べたので紹介します。 ...
RecyclerViewのリサイクル動作で使われるキャッシュは、サイズを大きくすれば多くのViewHolderが保持できます。その分、多くのメモリを消費します。 ViewHolderを多く保持できたとしても、サイクル動作で効率よく使われなければ、メモリの浪費です。 キャッシュのサイズはRecyclerViewの使われ方よって適切なサイズがあります。 そのため、RecyclerViewはキャッシュのサイズを変更できるようになっています。 ...
RecyclerViewはアイテムを一覧表示してくれます。 ただ一覧表示するだけではなく、「追加・削除・移動・切り替え」といったアイテムの表示を効率よく変更する仕組み持っています。 今回はこの仕組みを使ったアイテムの変更方法を紹介します。 ...
RecyclerViewのリサイクル動作で使われるキャッシュは、サイズを大きくすれば多くのViewHolderが保持できます。その分、多くのメモリを消費します。 ViewHolderを多く保持できたとしても、サイクル動作で効率よく使われなければ、メモリの浪費です。 キャッシュのサイズはRecyclerViewの使われ方よって適切なサイズがあります。 そのため、RecyclerViewはキャッシュのサイズを変更できるようになっています。 ...
RecyclerViewが空(アイテムが無い)の時、EmptyViewを表示する実装を行ったので紹介します。 ...
RecyclerView上のアイテムを選択する方法を紹介します。 外部ライブラリー(AndroidX)で提供されるrecyclerview-selection APIを用いた方法です。 ...
RecyclerViewはListView(RecyclerViewの前身)の時に存在していたChoiceModeがありません。 同様な機能が欲しければプログラマ側で実装しなければなりません。 RecyclerView.AdapterをカスタマイズしてChoiceModeを実装してみたので紹介します。 ...
RecyclerViewはアイテムへ装飾を付けることが出来るようになっています。 装飾とは、例えばアイテムの区切り線などです。 今回はアイテムへ装飾を付ける方法を紹介します。 ...
RecyclerViewに表示しきれなかったアイテムはスクロールを行うことで表示されるようになっています。 スクロールは「外部入力(指でスクリーン上をタッチしてスライド)によるスクロール」の他に、「プログラムによるスクロール」をすることも出来ます。 今回はこのアイテムのスクロールについてまとめてみました。 ...
RecyclerViewはアイテムをスクロールさせて隠れたアイテムを表示できます。 スクロールを止める位置は任意です。 任意であるがゆえに、止めた位置によってはアイテムの一部が欠けてしまうこともあります。 携帯端末の画面は狭いので、効率よくコンテンツの表示を行いたいとアプリ開発者は考えます。 アイテムの一部が欠けてしまうことは、効率が良いとは言えません。 このような問題を解決するために、アイテムのスナップをRecyclerViewへ追加できます。 アイテムのスナップを追加する方法を紹介します。 ...
RecyclerViewでアイテムの変更(Change/Insert/Move/Remove)を行うと、変更される様子がアニメーション化されています。 これはデフォルトでアイテムの変更アニメーションが組み込まれているためです。 デフォルトは単純なアニメーションですが、「ある」と「ない」の違いは歴然で、アニメーションのある方が高価なアプリケーションに見えます。 GUI(Graphical User Interface)が主体の携帯端末にとって、利用者に対するアプリの見せ方は重要です。高価に見えた方が使ってもらえる可能性が高くなります。 上記のことから、アプリの機能に関係なくても、ちょっとした動きをアニメーション化するメリットがあります。 2回にわたりアイテムの変更アニメーションについてまとめてみました。 アイテムの変更アニメーション(DefaultItemAnimator、デフォルト) アイテムの変更アニメーション(SimpleItemAnimatorの継承、カスタム) 今回は第1回目「ItemAnimator、デフォルト」編です。デフォルト変更アニメーションの動作について説明します。 ...
RecyclerViewでアイテムの変更(Change/Insert/Move/Remove)を行うと、変更される様子がアニメーション化されています。 これはデフォルトでアイテムの変更アニメーションが組み込まれているためです。 デフォルトは単純なアニメーションですが、「ある」と「ない」の違いは歴然で、アニメーションのある方が高価なアプリケーションに見えます。 GUI(Graphical User Interface)が主体の携帯端末にとって、利用者に対するアプリの見せ方は重要です。高価に見えた方が使ってもらえる可能性が高くなります。 上記のことから、アプリの機能に関係なくても、ちょっとした動きをアニメーション化するメリットがあります。 2回にわたりアイテムの変更アニメーションについてまとめてみました。 アイテムの変更アニメーション(DefaultItemAnimator、デフォルト) アイテムの変更アニメーション(SimpleItemAnimatorの継承、カスタム) 今回は第2回目「SimpleItemAnimatorの継承、カスタム」編です。カスタム変更アニメーションの作り方を説明 ...
RecyclerViewへアイテムが表示されるとき、アニメーションはありません。。一瞬で表示されて終わりです。 「RecyclerViewへアイテムが表示される」ことを、ここでは「アイテムの出現」と言い表すことにします。 このアイテムの出現にアニメーションを付ける方法を紹介します。 アイテムの出現をアニメーションで演出することで、RecyclerViewに表示したい内容が際立つと思います。 ...
RecyclerViewで「グリッド表示をスムーズにスクロール」する方法について、まとめます。 画像ファイルをグリッド表示する場合は、スムーズなスクロールを行うための工夫が必要です。 その工夫について紹介します。 ※環境:Android Studio Ladybug | 2024.2.1 Patch 1     Kotlin 2.0.0     Compose Compilerプラグイン 2.0.0     androidx.recyclerview:recyclerview:1.1.0 ...
スポンサーリンク