弊サイトは福岡を中心とした再開発や都市に関する情報を発信するサイトですが、
今回の記事は都市に関係のないプログラミングの記事となります。
画像を非同期で表示する
まずはURLから画像を取得してbitmapに直す方法です。
/**
* 非同期でbitmapに変換する
* @param url 画像URL
* @param callback 変換後のcallback
**/
fun getImageBitmap(url: String, maxWidth: Int = 2048, maxHeight: Int = 2048, callback: (Bitmap?, Int) -> Unit) {
backgroundExecutor.execute {
// 返却するbitmap
var bitmap: Bitmap? = null
// bitmapの縮小率 元サイズの半分なら2、4分の1なら4
var reductionRatio = 1
var connection: HttpURLConnection? = null
try {
val imageUrl = URL(url)
connection = imageUrl.openConnection() as HttpURLConnection
connection.connect()
if (connection.responseCode == HttpURLConnection.HTTP_OK) {
val inputStream: InputStream = BufferedInputStream(connection.inputStream)
// Bitmapのデコードオプションを作成
val options = BitmapFactory.Options()
// trueを設定して実際のビットマップのサイズを読み込まずに、メタデータのみを取得します
options.inJustDecodeBounds = true
BitmapFactory.decodeStream(inputStream, null, options)
inputStream.close()
// デコード時のサンプリングファクターを計算します
options.inSampleSize = calculateInSampleSize(options, maxWidth, maxHeight)
reductionRatio = options.inSampleSize
// サンプリングファクターを適用して本当のデコードを行います
options.inJustDecodeBounds = false
val inputStream2: InputStream = imageUrl.openStream()
bitmap = BitmapFactory.decodeStream(inputStream2, null, options)
inputStream2.close()
}
} catch (e: IOException) {
e.printStackTrace()
} finally {
connection?.disconnect()
mainHandler.post {
callback(bitmap, reductionRatio)
}
}
}
}
// サンプリングファクターを計算するユーティリティ関数
fun calculateInSampleSize(options: BitmapFactory.Options, maxWidth: Int, maxHeight: Int): Int {
val originalWidth = options.outWidth
val originalHeight = options.outHeight
var inSampleSize = 1
if (originalWidth > maxWidth || originalHeight > maxHeight) {
val halfWidth = originalWidth / 2
val halfHeight = originalHeight / 2
while ((halfWidth / inSampleSize) >= maxWidth && (halfHeight / inSampleSize) >= maxHeight) {
inSampleSize *= 2
}
}
return inSampleSize
}
このコードでは、URLから画像を取得してbitmapに変換して返しています。
callbackを設定し、URLから画像を取得しbitmapに変換されるのを待って、その後callbackの処理が走ります。
取得した画像のサイズが大きすぎるとbitmapを表示する際にメモリが足りずに落ちてしまうので、calculateInSampleSizeでサイズを取得して引数のmaxWidthとmaxHeightを超えていたら画像を縮小して返します。
RecyclerViewに画像取得処理を実装
RecyclerViewを実装するためのAdapterクラスで画像取得処理を実装します。
class SampleRecyclerViewAdapter (
private val title: String,
private val url: String
): RecyclerView.Adapter<SampleViewHolder>
...
override fun onBindViewHolder(holder : SampleViewHolder, position: Int) {
holder.title.text = title
// 画像が表示されるまで、グルグルのgifを表示させておく。(gifはお好みで)
Glide.with(activity)
.asGif()
.load(R.drawable.gif_progress_indicator)
.into(holder.image)
// ここでbitmap表示処理を行います。reductionRatioは元画像のピクセル数を参照したい時などに利用してください。
Functions().getImageBitmap(url) { bitmap, reductionRatio ->
holder.image.setImageBitmap(bitmap)
}
}
...
素早くスクロールすると画像の表示位置がズレる不具合を直す
上記の実装でRecyclerViewを表示させ、素早くスクロールすると本来表示されるはずの場所と違うViewで画像が表示されてしまうことがあります。
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
RecyclerViewAdapter内に上記の処理を追加すると、スクロールでズレる不具合が解消されます。
これらの実装でアイテムの一意のIDとビュータイプを識別できるようになり、ビューの再利用やアニメーション処理を調整するのに役立ちます。
コメント