【Android】BitmapとCanvasでアイコン画像に通知件数バッジをつける【Kotlin】

アプリ内で使用するアイコン画像に、通知件数を表示するようなバッジを付与したいケースがあると思います。

こんな感じです。

上記はAndroidプロジェクトに初めから入っているドロイド君の画像を使用しています。

実際のアプリだと、ベルアイコンに通知件数を表示したり、タイムラインタブに未読件数を表示したりするケースで使用すると思います。

本記事では、任意のアイコン画像にバッジを付与するコードを実装します。

実装手順

大まかな流れは以下の通りです。

  1. アイコン画像のBitmapを取得する
  2. バッジを描画したBitmapを作成する
  3. アイコン画像BitmapとバッジBitmapを合成する

アイコン画像のBitmapを取得する

あらかじめ用意した画像リソースからBitmapオブジェクトを生成します。

val icon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)

バッジを描画したBitmapを作成する

Android では Canvas を使用することで、円などを描画することができます。

今回はバッジBitmapを作成する関数を作成しました。

fun createBadgeBitmap(badgeSize : Int, badgeStr : String) : Bitmap{
        // バッジ半径
        val badgeRadius = badgeSize/2.0f
        // バッジを描画するCanvasを作成
        val bitmap = Bitmap.createBitmap(badgeSize,badgeSize, Bitmap.Config.ARGB_8888)
        val canvas = Canvas(bitmap)
        // バッジの円を描画
        val circlePaint = Paint()
        circlePaint.isAntiAlias = true
        circlePaint.color = Color.RED
        canvas.drawCircle(badgeRadius, badgeRadius, badgeRadius, circlePaint)
        // バッジテキストを描画
        val textPaint = Paint()
        textPaint.isAntiAlias = true
        textPaint.color = Color.WHITE
        textPaint.textAlign = Paint.Align.CENTER
        textPaint.textSize = badgeRadius
        val xPos = canvas.width/2f
        val yPos = (canvas.height/2f)-((textPaint.descent()+textPaint.ascent()) / 2)
        canvas.drawText(badgeStr, xPos, yPos, textPaint)

        return bitmap
    }

まず、バッジの円を描画します。

次に、その円の中心に任意の文字列(badgeStr)を描画しています。

文字を円の中心に配置するのに苦労しました。。。

アイコン画像BitmapとバッジBitmapを合成する

アイコン画像とバッジを合成します。

以下がまとめのコードです。

// 1. で作成したアイコン画像のBitmap
val icon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher)
// バッジサイズの比率
val badgeAspect = 0.5f
// アイコンサイズを取得する
val width = icon.width
val height = icon.height
// バッジサイズ
val badgeSize = (width * badgeAspect).toInt()
val badgeRadius = badgeSize/2.0f
// キャンバスを作成してベースとなるアイコンを描画する
val newBitmap = Bitmap.createBitmap(width + badgeRadius.toInt(), height + badgeRadius.toInt(), Bitmap.Config.ARGB_8888)
val newCanvas = Canvas(newBitmap)
newCanvas.drawBitmap(icon,0f, badgeRadius,null)
// バッジを作成してアイコンに合成する
val badge = createBadgeBitmap(badgeSize,  "2")
newCanvas.drawBitmap(badge, width.toFloat() - badgeRadius, 0f, null)
// アイコン画像とバッジが合成されたBitmap
newBitmap

newBitmap のサイズには、アイコン画像のサイズにバッジの半径を加算したものを設定しています。

newCanvas.drawBitmap 部分で、アイコン画像とバッジを合成しています。
バッジの描画位置には、

  • x座標(left):アイコン画像のwidth - バッジの半径
  • y座標(top):Canvasの一番上

となるように指定しています。

最終的には、newBitmap にアイコン画像とバッジが合成されたBitmapが格納されています。